Completed improved device guest sharing.

Ylian Saint-Hilaire committed Apr 9, 2021 at 13:27 UTC 0ad256610c7d04974e24d8ad48f5518be87ee5d1
6 files changed +118 -38
agents/meshcore.js
+5 -1
@@ -1608,6 +1608,10 @@ function onTunnelData(data) {
1608 if ((data.length > 3) && (data[0] == '{')) { onTunnelControlData(data, this); return; }
1609 this.httprequest.protocol = parseInt(data);
1610 if (typeof this.httprequest.protocol != 'number') { this.httprequest.protocol = 0; }
1611 +
1612 + // See if this protocol request is allowed.
1613 + if ((this.httprequest.soptions != null) && (this.httprequest.soptions.usages != null) && (this.httprequest.soptions.usages.indexOf(this.httprequest.protocol) == -1)) { this.httprequest.protocol = 0; }
1614 +
1615 if (this.httprequest.protocol == 10) {
1616 //
1617 // Basic file transfer
@@ -1882,7 +1886,7 @@ function onTunnelData(data) {
1886 }
1887 else if (this.httprequest.protocol == 2) {
1888 //
1885 - // Remote KVM
1889 + // Remote Desktop
1890 //
1891
1892 // Check user access rights for desktop
meshrelay.js
+11 -1
@@ -771,7 +771,17 @@ function CreateMeshRelayEx(parent, ws, req, domain, user, cookie) {
771 // Send connection request to agent
772 if (obj.id == null) { obj.id = ('' + Math.random()).substring(2); }
773 const rcookie = parent.parent.encodeCookie({ ruserid: user._id, nodeid: node._id }, parent.parent.loginCookieEncryptionKey);
774 - const command = { nodeid: node._id, action: 'msg', type: 'tunnel', userid: user._id, value: '*/meshrelay.ashx?p=' + cookie.p + '&id=' + obj.id + '&rauth=' + rcookie + '&nodeid=' + node._id, soptions: {}, usage: 2, rights: cookie.r, guestname: cookie.gn, consent: cookie.cf, remoteaddr: cleanRemoteAddr(obj.req.clientIp) };
774 + const command = { nodeid: node._id, action: 'msg', type: 'tunnel', userid: user._id, value: '*/meshrelay.ashx?p=' + cookie.p + '&id=' + obj.id + '&rauth=' + rcookie + '&nodeid=' + node._id, soptions: {}, rights: cookie.r, guestname: cookie.gn, consent: cookie.cf, remoteaddr: cleanRemoteAddr(obj.req.clientIp) };
775 +
776 + // Limit what this relay connection can do
777 + if (typeof cookie.p == 'number') {
778 + var usages = [];
779 + if (cookie.p & 1) { usages.push(1); usages.push(6); usages.push(8); usages.push(9); } // Terminal
780 + if (cookie.p & 2) { usages.push(2); } // Desktop
781 + if (cookie.p & 4) { usages.push(5); usages.push(10); } // Files
782 + command.soptions.usages = usages;
783 + }
784 +
785 if (typeof domain.consentmessages == 'object') {
786 if (typeof domain.consentmessages.title == 'string') { command.soptions.consentTitle = domain.consentmessages.title; }
787 if (typeof domain.consentmessages.desktop == 'string') { command.soptions.consentMsgDesktop = domain.consentmessages.desktop; }
meshuser.js
+17 -6
@@ -5015,7 +5015,7 @@ module.exports.CreateMeshUser = function (parent, db, ws, req, args, domain, use
5015 else if ((command.start != null) && (typeof command.start != 'number')) { err = 'Invalid start time'; } // Check the start time in seconds
5016 else if ((command.end != null) && (typeof command.end != 'number')) { err = 'Invalid end time'; } // Check the end time in seconds
5017 else if (common.validateInt(command.consent, 0, 256) == false) { err = 'Invalid flags'; } // Check the flags
5018 - else if (common.validateInt(command.p, 1, 2) == false) { err = 'Invalid protocol'; } // Check the protocol, 1 = Terminal, 2 = Desktop
5018 + else if (common.validateInt(command.p, 1, 7) == false) { err = 'Invalid protocol'; } // Check the protocol, 1 = Terminal, 2 = Desktop, 4 = Files
5019 else if ((command.expire == null) && ((command.start == null) || (command.end == null) || (command.start > command.end))) { err = 'No time specified'; } // Check that a time range is present
5020 else {
5021 if (command.nodeid.split('/').length == 1) { command.nodeid = 'node/' + domain.id + '/' + command.nodeid; }
@@ -5047,13 +5047,25 @@ module.exports.CreateMeshUser = function (parent, db, ws, req, args, domain, use
5047 }
5048
5049 // If we are limited to no terminal, don't allow terminal sharing
5050 - if ((command.p == 1) && (rights != MESHRIGHT_ADMIN) && ((rights & MESHRIGHT_NOTERMINAL) != 0)) {
5050 + if (((command.p & 1) != 0) && (rights != MESHRIGHT_ADMIN) && ((rights & MESHRIGHT_NOTERMINAL) != 0)) {
5051 if (command.responseid != null) { try { ws.send(JSON.stringify({ action: 'deviceShares', responseid: command.responseid, result: 'Access denied' })); } catch (ex) { } }
5052 - return;
5052 + return;
5053 + }
5054 +
5055 + // If we are limited to no desktop, don't allow desktop sharing
5056 + if (((command.p & 2) != 0) && (rights != MESHRIGHT_ADMIN) && ((rights & MESHRIGHT_NODESKTOP) != 0)) {
5057 + if (command.responseid != null) { try { ws.send(JSON.stringify({ action: 'deviceShares', responseid: command.responseid, result: 'Access denied' })); } catch (ex) { } }
5058 + return;
5059 + }
5060 +
5061 + // If we are limited to no files, don't allow file sharing
5062 + if (((command.p & 4) != 0) && (rights != MESHRIGHT_ADMIN) && ((rights & MESHRIGHT_NOFILES) != 0)) {
5063 + if (command.responseid != null) { try { ws.send(JSON.stringify({ action: 'deviceShares', responseid: command.responseid, result: 'Access denied' })); } catch (ex) { } }
5064 + return;
5065 }
5066
5067 // If we have view only remote desktop rights, force view-only on the guest share.
5056 - if ((rights != MESHRIGHT_ADMIN) && ((rights & MESHRIGHT_REMOTEVIEWONLY) != 0)) { command.viewOnly = true; }
5068 + if ((rights != MESHRIGHT_ADMIN) && ((rights & MESHRIGHT_REMOTEVIEWONLY) != 0)) { command.viewOnly = true; command.p = (command.p & 1); }
5069
5070 // Create cookie
5071 var publicid = getRandomPassword(), startTime, expireTime;
@@ -5079,8 +5091,7 @@ module.exports.CreateMeshUser = function (parent, db, ws, req, args, domain, use
5091 var httpsPort = ((args.aliasport == null) ? args.port : args.aliasport); // Use HTTPS alias port is specified
5092 var xdomain = (domain.dns == null) ? domain.id : '';
5093 if (xdomain != '') xdomain += '/';
5082 - var page = (command.p == 1) ? 'terminal' : 'desktop';
5083 - var url = 'https://' + serverName + ':' + httpsPort + '/' + xdomain + page + '?c=' + inviteCookie;
5094 + var url = 'https://' + serverName + ':' + httpsPort + '/' + xdomain + 'sharing?c=' + inviteCookie;
5095 if (serverName.split('.') == 1) { url = '/' + xdomain + page + '?c=' + inviteCookie; }
5096 command.url = url;
5097 if (command.responseid != null) { command.result = 'OK'; }
views/default.handlebars
+51 -19
@@ -626,7 +626,7 @@
626 </div>
627 </div>
628 </div>
629 - <div id=p11DeskConsoleMsg style="display:none;cursor:pointer;position:absolute;left:30px;top:17px;color:yellow;background-color:rgba(0,0,0,0.6);padding:10px;border-radius:5px;text-align:left" onclick=p11clearConsoleMsg()></div>
629 + <div id=p11DeskConsoleMsg style="display:none;text-align:left;cursor:pointer;position:absolute;left:30px;top:17px;color:yellow;background-color:rgba(0,0,0,0.6);padding:10px;border-radius:5px;text-align:left" onclick=p11clearConsoleMsg()></div>
630 <div id=p11DeskSessionSelector style="display:none;position:absolute;left:30px;top:17px;right:30px;bottom:17px;overflow-y:auto"></div>
631 </div>
632 <div id=deskarea4 class="areaFoot">
@@ -729,7 +729,7 @@
729 </td>
730 </tr>
731 </table>
732 - <div id=p12TermConsoleMsg style="display:none;cursor:pointer;position:absolute;left:30px;top:45px;color:yellow;background-color:rgba(0,0,0,0.6);padding:10px;border-radius:5px" onclick=p12clearConsoleMsg()></div>
732 + <div id=p12TermConsoleMsg style="display:none;text-align:left;cursor:pointer;position:absolute;left:30px;top:45px;color:yellow;background-color:rgba(0,0,0,0.6);padding:10px;border-radius:5px" onclick=p12clearConsoleMsg()></div>
733 </div>
734 </div>
735 <div id=p13 style="display:none">
@@ -788,7 +788,7 @@
788 </td>
789 </tr>
790 </table>
791 - <div id=p13FilesConsoleMsg style="display:none;cursor:pointer;position:absolute;left:30px;top:165px;color:yellow;background-color:rgba(0,0,0,0.6);padding:10px;border-radius:5px" onclick=p13clearConsoleMsg()></div>
791 + <div id=p13FilesConsoleMsg style="display:none;text-align:left;cursor:pointer;position:absolute;left:30px;top:165px;color:yellow;background-color:rgba(0,0,0,0.6);padding:10px;border-radius:5px" onclick=p13clearConsoleMsg()></div>
792 <div id="p13filetable" style="">
793 <div id="p13bigok" style="display:none"><b>&checkmark;</b></div>
794 <div id="p13bigfail" style="display:none"><b>&#10007;</b></div>
@@ -3211,7 +3211,7 @@
3211 QV('agentInvitationLinkDiv', true);
3212 break;
3213 }
3214 - case 'createDeviceShareLink': { // Remote desktop sharing link
3214 + case 'createDeviceShareLink': { // Guest sharing link
3215 if (xxdialogTag) break;
3216 var node = getNodeFromId(message.nodeid), x = '';
3217 if (node == null) break;
@@ -3221,12 +3221,13 @@
3221 x += addHtmlValue("Start Time", printDateTime(new Date(message.start)));
3222 x += addHtmlValue("Expire Time", printDateTime(new Date(message.expire)));
3223 var y = [];
3224 - if (message.consent & 1) { y.push("Notify"); }
3225 - if (message.consent & 8) { y.push("Prompt"); }
3226 - if (message.consent & 64) { y.push("Privacy bar"); }
3224 + if (message.consent & 0x0007) { y.push("Notify"); }
3225 + if (message.consent & 0x0038) { y.push("Prompt"); }
3226 + if (message.consent & 0x0040) { y.push("Privacy bar"); }
3227 if (y.length == 0) { y.push("None"); }
3228 x += addHtmlValue("User Consent", y.join(', '));
3229 - x += '<div id=agentInvitationLinkDiv style="text-align:center;font-size:large;margin:16px"><a href="' + message.url + '" id=agentInvitationLink rel="noreferrer noopener" target="_blank" style=cursor:pointer>' + ((message.p == 1)?"Remote Terminal Link":"Remote Desktop Link") + '</a> <img src=images/link4.png height=10 width=10 title="' + "Copy link to clipboard" + '" style=cursor:pointer onclick=d2CopyInviteToClip()></div></div>';
3229 + var type = ['', "Remote Terminal Link", "Remote Desktop Link", "Remote Desktop + Terminal Link", "Remote Files Link", "Remote Terminal + Files Link", "Remote Desktop + Files Link", "Remote Desktop + Terminal + Files Link"][message.p];
3230 + x += '<div id=agentInvitationLinkDiv style="text-align:center;font-size:large;margin:16px"><a href="' + message.url + '" id=agentInvitationLink rel="noreferrer noopener" target="_blank" style=cursor:pointer>' + type + '</a> <img src=images/link4.png height=10 width=10 title="' + "Copy link to clipboard" + '" style=cursor:pointer onclick=d2CopyInviteToClip()></div></div>';
3231 setDialogMode(2, "Share Device", 1, null, x);
3232 break;
3233 }
@@ -6524,12 +6525,13 @@
6525 for (var i = 0; i < deviceShares.length; i++) {
6526 var dshare = deviceShares[i];
6527 var trash = '<a href="' + dshare.url + '" rel="noreferrer noopener" target=_blank title="' + "Device Sharing Link" + '" style=cursor:pointer><img src=images/link2.png border=0 height=10 width=10></a> <a href=# onclick=\'return p30removeDeviceSharing(event,"' + encodeURIComponentEx(currentNode._id) + '","' + encodeURIComponentEx(dshare.publicid) + '","' + encodeURIComponentEx(dshare.guestName) + '")\' title="' + "Remove device sharing" + '" style=cursor:pointer><img src=images/trash.png border=0 height=10 width=10></a>';
6527 - var details = format("{0}, {1} to {2}", ((dshare.p == 1)?"Terminal":"Desktop"), printFlexDateTime(new Date(dshare.startTime)), printFlexDateTime(new Date(dshare.expireTime)));
6528 + var type = ['', "Terminal", "Desktop", "Desktop + Terminal", "Files", "Terminal + Files", "Desktop + Files", "Desktop + Terminal + Files"][dshare.p];
6529 + var details = format("{0}, {1} to {2}", type, printFlexDateTime(new Date(dshare.startTime)), printFlexDateTime(new Date(dshare.expireTime)));
6530 if (dshare.viewOnly === true) { details += ", View only"; }
6531 if (dshare.consent != null) {
6532 if (dshare.consent == 0) { details += ", No Consent"; } else {
6531 - if (((dshare.consent & 8) != 0) || ((dshare.consent & 16) != 0)) { details += ", Prompt for consent"; }
6532 - if ((dshare.consent & 0x40) != 0) { details += ", Toolbar"; }
6533 + if ((dshare.consent & 0x0038) != 0) { details += ", Prompt for consent"; }
6534 + if ((dshare.consent & 0x0040) != 0) { details += ", Toolbar"; }
6535 }
6536 }
6537 x += '<tr ' + (((++count % 2) == 0) ? 'style=background-color:#DDD' : '') + '><td style=width:30%><div class=m' + 2 + '></div><div>&nbsp;' + dshare.guestName + '<div></div></div></td><td style=width:70%><div style=float:right>' + trash + '</div><div>' + details + '</div></td></tr>';
@@ -6731,8 +6733,26 @@
6733 if ((rights != 0xFFFFFFFF) && ((rights & 0x100) != 0)) { deskFull = ''; }
6734 var fullTerm = '<option value=1>' + "Terminal" + '</option>';
6735 if ((rights != 0xFFFFFFFF) && ((rights & 0x200) != 0)) { fullTerm = ''; }
6734 - x += addHtmlValue("Type", '<select id=d2shareType style=float:right;width:250px onchange=showShareDeviceValidate()>' + ((currentNode.agent.caps & 1)?(deskFull + '<option value=3>' + "Desktop, View only" + '</option>'):'') + ((currentNode.agent.caps & 2)?fullTerm:'') + '</select>');
6736 + var fullFiles = '<option value=4>' + "Files" + '</option>';
6737 + if ((rights != 0xFFFFFFFF) && ((rights & 0x400) != 0)) { fullFiles = ''; }
6738 + var deskFiles = '<option value=5>' + "Desktop + Files" + '</option>';
6739 + if ((rights != 0xFFFFFFFF) && ((rights & 0x500) != 0)) { deskFiles = ''; }
6740 + var termFiles = '<option value=6>' + "Terminal + Files" + '</option>';
6741 + if ((rights != 0xFFFFFFFF) && ((rights & 0x600) != 0)) { termFiles = ''; }
6742 + var allFeatures = '<option value=7>' + "Desktop + Terminal + Files" + '</option>';
6743 + if ((rights != 0xFFFFFFFF) && ((rights & 0x700) != 0)) { allFeatures = ''; }
6744 +
6745 + var y = '';
6746 + if (currentNode.agent.caps & 1) { y += (deskFull + '<option value=3>' + "Desktop, View only" + '</option>'); } // Agent is desktop capable
6747 + if (currentNode.agent.caps & 2) { y += fullTerm; } // Agent is terminal capable
6748 + if (currentNode.agent.caps & 4) { y += fullFiles; } // Agent is files capable
6749 + if (currentNode.agent.caps & 5) { y += deskFiles; } // Agent is desktop + files capable
6750 + if (currentNode.agent.caps & 6) { y += termFiles; } // Agent is terminal + files capable
6751 + if (currentNode.agent.caps & 7) { y += allFeatures; } // Agent is desktop + terminal + files capable
6752 +
6753 + x += addHtmlValue("Type", '<select id=d2shareType style=float:right;width:250px onchange=showShareDeviceValidate()>' + y + '</select>');
6754 var options = { 1 : "1 minute", 5 : "5 minutes", 10 : "10 minutes", 15 : "15 minutes", 30 : "30 minutes", 45 : "45 minutes", 60 : "60 minutes", 120 : "2 hours", 240 : "4 hours", 480 : "8 hours", 720 : "12 hours", 960 : "16 hours", 1440 : "24 hours", 2880 : "2 days", 5760 : "4 days" }
6755 + y = '';
6756 for (var i in options) { y += '<option value=' + i + '>' + options[i] + '</option>'; }
6757 x += addHtmlValue("Validity", '<select id=d2timeRange style=float:right;width:250px onchange=showShareDeviceValidate()><option value=0>' + "Starting now" + '</option><option value=1>' + "Time range" + '</option></select>');
6758 x += '<div id=d2modenow>';
@@ -6758,16 +6778,28 @@
6778 }
6779
6780 function showShareDeviceEx(b, tag) {
6761 - var consent = 0, p = parseInt(Q('d2shareType').value), viewOnly = false;
6762 - if (currentNode.agent.caps & 1) {
6763 - if (Q('d2shareType').value == 1) { if (Q('d2userConsent').value == 1) { consent = 18; } else { consent = 2; } } // Terminal Consent: 2 = Notify, 16 = Prompt
6764 - if (Q('d2shareType').value > 1) { if (Q('d2userConsent').value == 1) { consent = 73; } else { consent = 65; } } // Desktop Consent: 1 = Notify, 8 = Prompt, 64 = Privacy bar
6781 + var consent = 0, p = parseInt(Q('d2shareType').value), viewOnly = false, q = 0;
6782 + if (p == 3) { viewOnly = true; }
6783 + var q = [0, 1, 2, 2, 4, 6, 5, 7][p]; // Protocol flags: 1 = Terminal, 2 = Desktop, 4 = Files.
6784 +
6785 + if (q & 1) {
6786 + consent |= 0x0002; // Terminal notify
6787 + if (Q('d2userConsent').value == 1) { consent |= 0x0010; } // Terminal prompt for user consent
6788 }
6766 - if (p == 3) { p = 2; viewOnly = true; }
6789 + if (q & 2) {
6790 + consent |= 0x0001; // Desktop notify
6791 + consent |= 0x0040; // Desktop connection toolbar
6792 + if (Q('d2userConsent').value == 1) { consent |= 0x0008; } // Desktop prompt for user consent
6793 + }
6794 + if (q & 4) {
6795 + consent |= 0x0004; // Files notify
6796 + if (Q('d2userConsent').value == 1) { consent |= 0x0020; } // Files prompt for user consent
6797 + }
6798 +
6799 if (Q('d2timeRange').value == 0) {
6768 - meshserver.send({ action: 'createDeviceShareLink', nodeid: currentNode._id, guestname: Q('d2inviteName').value.trim(), p: p, expire: parseInt(Q('d2inviteExpire').value), consent: consent, viewOnly: viewOnly });
6800 + meshserver.send({ action: 'createDeviceShareLink', nodeid: currentNode._id, guestname: Q('d2inviteName').value.trim(), p: q, expire: parseInt(Q('d2inviteExpire').value), consent: consent, viewOnly: viewOnly });
6801 } else {
6770 - meshserver.send({ action: 'createDeviceShareLink', nodeid: currentNode._id, guestname: Q('d2inviteName').value.trim(), p: p, start: Math.floor(tag.selectedDates[0].getTime() / 1000), end: Math.floor(tag.selectedDates[1].getTime() / 1000), consent: consent, viewOnly: viewOnly });
6802 + meshserver.send({ action: 'createDeviceShareLink', nodeid: currentNode._id, guestname: Q('d2inviteName').value.trim(), p: q, start: Math.floor(tag.selectedDates[0].getTime() / 1000), end: Math.floor(tag.selectedDates[1].getTime() / 1000), consent: consent, viewOnly: viewOnly });
6803 }
6804 }
6805
views/sharing.handlebars
+24 -7
@@ -30,7 +30,7 @@
30 <title>{{{title}}}</title>
31 </head>
32 <body style="overflow:hidden;background-color:black">
33 - <div id=LeftSideToolBar style="position:absolute;left:0;bottom:0;width:52px;top:0;background:#113962;background:linear-gradient(to bottom, #104893 0%,#113962 100%);color:white;border-right: 5px solid #BBB;">
33 + <div id=LeftSideToolBar style="display:none;position:absolute;left:0;bottom:0;width:52px;top:0;background:#113962;background:linear-gradient(to bottom, #104893 0%,#113962 100%);color:white;border-right: 5px solid #BBB;">
34 <div id=LeftMenuDesktop class="slbbutton slbbuttonsel2" title="Desktop" onclick=go(11)>
35 <div class="slb1" style="position:absolute;top:6px;left:6px"></div>
36 </div>
@@ -66,7 +66,7 @@
66 <div id=DeskParent>
67 <canvas id=Desk width=640 height=480 oncontextmenu="return false" onmousedown=dmousedown(event) onmouseup=dmouseup(event) onmousemove=dmousemove(event) onmousewheel=dmousewheel(event)></canvas>
68 </div>
69 - <div id=p11DeskConsoleMsg style="display:none;cursor:pointer;position:absolute;left:30px;top:17px;color:yellow;background-color:rgba(0,0,0,0.6);padding:10px;border-radius:5px" onclick=clearConsoleMsg()></div>
69 + <div id=p11DeskConsoleMsg style="display:none;text-align:left;cursor:pointer;position:absolute;left:30px;top:17px;color:yellow;background-color:rgba(0,0,0,0.6);padding:10px;border-radius:5px" onclick=clearConsoleMsg()></div>
70 </div>
71 <div id=deskarea4 class="areaFoot" style="min-height:24px">
72 <div class="toright2">
@@ -146,7 +146,7 @@
146 <input type=button onkeypress="return false" onkeydown="return false" class="bottombutton" id="pastebutton" value="Paste" title="Paste text into the terminal" onclick="showTermPasteDialog()" style="display:none" />
147 </div>
148 </div>
149 - <div id=p12TermConsoleMsg style="display:none;cursor:pointer;position:absolute;left:30px;top:45px;color:yellow;background-color:rgba(0,0,0,0.6);padding:10px;border-radius:5px" onclick=p12clearConsoleMsg()></div>
149 + <div id=p12TermConsoleMsg style="display:none;text-align:left;cursor:pointer;position:absolute;left:30px;top:45px;color:yellow;background-color:rgba(0,0,0,0.6);padding:10px;border-radius:5px" onclick=p12clearConsoleMsg()></div>
150 </div>
151 <div id=p13 class="noselect" style="overflow:hidden;position:absolute;left:54px;top:0;right:0;bottom:0;display:none;background-color:white">
152 <div id="p13toolbar" style="position:absolute;left:0;top:0;right:0;bottom:28px">
@@ -192,7 +192,7 @@
192 <div>&nbsp;&nbsp;<span id="p13currentpath"></span></div>
193 </div>
194 <div id="fileArea4" style="height:calc(100vh - 146px)">
195 - <div id=p13FilesConsoleMsg style="display:none;cursor:pointer;position:absolute;left:30px;top:165px;color:yellow;background-color:rgba(0,0,0,0.6);padding:10px;border-radius:5px" onclick=p13clearConsoleMsg()></div>
195 + <div id=p13FilesConsoleMsg style="display:none;text-align:left;cursor:pointer;position:absolute;left:30px;top:165px;color:yellow;background-color:rgba(0,0,0,0.6);padding:10px;border-radius:5px" onclick=p13clearConsoleMsg()></div>
196 <div id="p13filetable" style="width:100%;height:100%">
197 <div id="p13bigok" style="display:none"><b>&checkmark;</b></div>
198 <div id="p13bigfail" style="display:none"><b>&#10007;</b></div>
@@ -285,7 +285,6 @@
285 var domain = '{{{domain}}}';
286 var domainUrl = '{{{domainurl}}}';
287 var authCookie = '{{{authCookie}}}';
288 - var nodeid = '{{{nodeid}}}';
288 var viewOnly = parseInt('{{{viewOnly}}}');
289 var urlargs = parseUriArgs();
290 var debugmode = urlargs.debug;
@@ -300,6 +299,7 @@
299 QH('p12power', printFlexDateTime(new Date(parseInt(expire))));
300 QH('p13power', printFlexDateTime(new Date(parseInt(expire))));
301 }
302 + var features = parseInt('{{{features}}}');
303
304 // Terminal
305 var terminal = null;
@@ -341,7 +341,24 @@
341 Q('p13filetable').addEventListener('dragover', p13fileDragOver, false);
342 Q('p13filetable').addEventListener('dragleave', p13fileDragLeave, false);
343
344 - go(11); // Go to desktop
344 + // Setup feature visibility
345 + QV('LeftMenuDesktop', features & 2);
346 + QV('LeftMenuTerminal', features & 1);
347 + QV('LeftMenuFiles', features & 4);
348 + if (features & 2) { go(11); } // Goto desktop
349 + else if (features & 1) { go(12); } // Goto terminal
350 + else if (features & 4) { go(13); } // Goto files
351 +
352 + // Only show left bar if two or more features are visible
353 + var featureCount = 0;
354 + if (features & 1) { featureCount++; }
355 + if (features & 2) { featureCount++; }
356 + if (features & 4) { featureCount++; }
357 + QV('LeftSideToolBar', featureCount > 1);
358 + QS('p11')['left'] = (featureCount > 1) ? '54px' : '0px';
359 + QS('p12')['left'] = (featureCount > 1) ? '54px' : '0px';
360 + QS('p13')['left'] = (featureCount > 1) ? '54px' : '0px';
361 +
362 deskAdjust();
363 }
364
@@ -1717,7 +1734,7 @@
1734 //link = '<a href="devicefile.ashx?c=' + authCookie + '&m=' + currentNode.meshid.split('/')[2] + '&n=' + currentNode._id.split('/')[2] + '&f=' + encodeURIComponentEx(newlinkpath + '/' + name) + '" download="' + name + '" style=cursor:pointer>' + shortname + '</a>';
1735 // Server link
1736 //link = '<a onclick=downloadFile("devicefile.ashx?c=' + authCookie + '&m=' + currentNode.meshid.split('/')[2] + '&n=' + currentNode._id.split('/')[2] + '&f=' + encodeURIComponentEx(newlinkpath + '/' + name) + '","' + encodeURIComponentEx(name) + '") style=cursor:pointer>' + shortname + '</a>';
1720 - link = '<a onclick=downloadFile("devicefile.ashx?c=' + authCookie + '&n=' + nodeid.split('/')[2] + '&f=' + encodeURIComponentEx(newlinkpath + '/' + name) + '","' + encodeURIComponentEx(name) + '") style=cursor:pointer>' + shortname + '</a>';
1737 + link = '<a onclick=downloadFile("devicefile.ashx?c=' + authCookie + '&f=' + encodeURIComponentEx(newlinkpath + '/' + name) + '","' + encodeURIComponentEx(name) + '") style=cursor:pointer>' + shortname + '</a>';
1738 }
1739 h = '<div id=fileEntry cmenu=filesContextMenu fileIndex=' + i + ' class=filelist file=3><input file=3 style=float:left name=fd class=fcb type=checkbox onchange=p13setActions() value=\'' + f.nx + '\'>&nbsp;<span class=fsize>' + fdatestr + '</span><span style=float:right>' + EscapeHtml(fsize) + '</span><span><div class=fileIcon' + f.t + '></div>' + link + '</span></div>';
1740 }
webserver.js
+10 -4
@@ -2992,7 +2992,7 @@ module.exports.CreateWebServer = function (parent, db, args, certificates) {
2992 function handleDeviceFile(req, res) {
2993 const domain = checkUserIpAddress(req, res);
2994 if (domain == null) { return; }
2995 - if ((req.query.c == null) || (req.query.n == null) || (req.query.f == null)) { res.sendStatus(404); return; }
2995 + if ((req.query.c == null) || (req.query.f == null)) { res.sendStatus(404); return; }
2996
2997 // Check the inbound desktop sharing cookie
2998 var c = obj.parent.decodeCookie(req.query.c, obj.parent.loginCookieEncryptionKey, 60); // 60 minute timeout
@@ -3002,6 +3002,12 @@ module.exports.CreateWebServer = function (parent, db, args, certificates) {
3002 const user = obj.users[c.userid];
3003 if ((c == user)) { res.sendStatus(404); return; }
3004
3005 + // If this cookie has restricted usages, check that it's allowed to perform downloads
3006 + if (Array.isArray(c.usages) && (c.usages.indexOf(10) < 0)) { res.sendStatus(404); return; } // Check protocol #10
3007 +
3008 + if (c.nid != null) { req.query.n = c.nid.split('/')[2]; } // This cookie is restricted to a specific nodeid.
3009 + if (req.query.n == null) { res.sendStatus(404); return; }
3010 +
3011 // Check if this user has permission to manage this computer
3012 obj.GetNodeWithRights(domain, user, 'node/' + domain.id + '/' + req.query.n, function (node, rights, visible) {
3013 if ((node == null) || ((rights & MESHRIGHT_REMOTECONTROL) == 0) || (visible == false)) { res.sendStatus(404); return; } // We don't have remote control rights to this device
@@ -3310,7 +3316,7 @@ module.exports.CreateWebServer = function (parent, db, args, certificates) {
3316
3317 // Check the inbound desktop sharing cookie
3318 var c = obj.parent.decodeCookie(req.query.c, obj.parent.invitationLinkEncryptionKey, 60); // 60 minute timeout
3313 - if ((c == null) || (c.a !== 5) || ((c.p !== 2) && (c.p != null)) || (typeof c.uid != 'string') || (typeof c.nid != 'string') || (typeof c.gn != 'string') || (typeof c.cf != 'number') || (typeof c.start != 'number') || (typeof c.expire != 'number') || (typeof c.pid != 'string')) { res.sendStatus(404); return; }
3319 + if ((c == null) || (c.a !== 5) || (typeof c.p !== 'number') || (c.p < 1) || (c.p > 7) || (typeof c.uid != 'string') || (typeof c.nid != 'string') || (typeof c.gn != 'string') || (typeof c.cf != 'number') || (typeof c.start != 'number') || (typeof c.expire != 'number') || (typeof c.pid != 'string')) { res.sendStatus(404); return; }
3320
3321 // Check the expired time, expire message.
3322 if (c.expire <= Date.now()) { render(req, res, getRenderPage((domain.sitestyle == 2) ? 'message2' : 'message', req, domain), getRenderArgs({ titleid: 2, msgid: 12, domainurl: encodeURIComponent(domain.url).replace(/'/g, '%27') }, req, domain)); return; }
@@ -3335,13 +3341,13 @@ module.exports.CreateWebServer = function (parent, db, args, certificates) {
3341
3342 // Looks good, let's create the outbound session cookies.
3343 // Consent flags are 1 = Notify, 8 = Prompt, 64 = Privacy Bar.
3338 - const authCookie = obj.parent.encodeCookie({ userid: c.uid, domainid: domain.id, nid: c.nid, ip: req.clientIp, p: 2, gn: c.gn, cf: 65 | c.cf, r: 8, expire: c.expire, pid: c.pid, vo: c.vo }, obj.parent.loginCookieEncryptionKey);
3344 + const authCookie = obj.parent.encodeCookie({ userid: c.uid, domainid: domain.id, nid: c.nid, ip: req.clientIp, p: c.p, gn: c.gn, cf: c.cf, r: 8, expire: c.expire, pid: c.pid, vo: c.vo }, obj.parent.loginCookieEncryptionKey);
3345
3346 // Lets respond by sending out the desktop viewer.
3347 var httpsPort = ((obj.args.aliasport == null) ? obj.args.port : obj.args.aliasport); // Use HTTPS alias port is specified
3348 parent.debug('web', 'handleDesktopRequest: Sending guest sharing page for \"' + c.uid + '\", guest \"' + c.gn + '\".');
3349 res.set({ 'Cache-Control': 'no-store' });
3344 - render(req, res, getRenderPage('sharing', req, domain), getRenderArgs({ authCookie: authCookie, authRelayCookie: '', domainurl: encodeURIComponent(domain.url).replace(/'/g, '%27'), nodeid: c.nid, serverDnsName: obj.getWebServerName(domain), serverRedirPort: args.redirport, serverPublicPort: httpsPort, expire: c.expire, viewOnly: (c.vo == 1) ? 1 : 0, nodeName: encodeURIComponent(node.name) }, req, domain));
3350 + render(req, res, getRenderPage('sharing', req, domain), getRenderArgs({ authCookie: authCookie, authRelayCookie: '', domainurl: encodeURIComponent(domain.url).replace(/'/g, '%27'), nodeid: c.nid, serverDnsName: obj.getWebServerName(domain), serverRedirPort: args.redirport, serverPublicPort: httpsPort, expire: c.expire, viewOnly: (c.vo == 1) ? 1 : 0, nodeName: encodeURIComponent(node.name), features: c.p }, req, domain));
3351 });
3352 });
3353 }