diff options
Diffstat (limited to 'packages/meshbay-client/src/main.js')
| -rw-r--r-- | packages/meshbay-client/src/main.js | 38 |
1 files changed, 24 insertions, 14 deletions
diff --git a/packages/meshbay-client/src/main.js b/packages/meshbay-client/src/main.js index 309d5f6..c1ff540 100644 --- a/packages/meshbay-client/src/main.js +++ b/packages/meshbay-client/src/main.js @@ -1160,8 +1160,24 @@ function registerBridge() { return { path: chosen, name: path.basename(chosen) }; }); + // The Mark-of-the-Web, as a browser leaves on every download: the file came + // from somebody else's machine, and Windows decides what that means — + // SmartScreen for a program, Protected View for a document. This application + // writes its files itself, so nothing else marks them. NTFS only; elsewhere + // there is no such stream, and nothing is lost by not having one. + function markFromInternet(file) { + if (process.platform !== 'win32') return; + try { + fs.writeFileSync(`${file}:Zone.Identifier`, '[ZoneTransfer]\r\nZoneId=3\r\n'); + } catch { /* FAT, exFAT, a network share: no alternate data streams */ } + } + handle('save:begin', async (_e, suggestedName, opts) => { - const wanted = path.basename(String(suggestedName || 'download')); + // Bidirectional controls replaced here as well as in the page + // (portable-name.js): "invoice\u202efdp.exe" would be saved, and listed by + // the file manager, as "invoiceexe.pdf". + const wanted = path.basename(String(suggestedName || 'download')) + .replace(/[\u061c\u200e\u200f\u202a-\u202e\u2066-\u2069]/g, '_'); const chosen = chosenDownloadDir(); let target = null; @@ -1231,6 +1247,7 @@ function registerBridge() { console.error('[MeshBay] could not finalise download:', err.message); return false; } + markFromInternet(sink.path); completedPaths.set(String(id), sink.path); return true; }); @@ -2184,9 +2201,11 @@ function registerBridge() { attachGroup: async (a) => { const body = { name: aText(a.name, 'the group name'), shared_dir: aText(a.path, 'the folder', 4096), - writable: a.writable !== false }; - await confirmOrRefuse('native.attach_confirm', - { name: body.name, path: body.shared_dir }); + writable: a.writable !== false, + // The person's choice on the creation form; the node never + // takes it from the hub. + join_policy: a.joinPolicy === 'open' ? 'open' : 'invite' }; + await confirmFolder(body.shared_dir); return ['POST', '/api/groups/attach', body]; }, detachGroup: (a) => ['POST', '/api/groups/detach', { name: aText(a.name, 'the group name') }], @@ -2203,16 +2222,7 @@ function registerBridge() { ejectRoot: (a) => ['PUT', `${root(a)}/eject`], plugRoot: (a) => ['PUT', `${root(a)}/plug`], removeRoot: (a) => ['DELETE', root(a)], - // Creating a missing key replaces nothing -- the node keeps an existing one - // -- so only a rotation is asked about. - initGek: async (a) => { - const target = group(a); - if (a.rotate) { - await confirmOrRefuse('node.gek_rotate_confirm'); - return ['POST', `${target}/gek?rotate=true`]; - } - return ['POST', `${target}/gek`]; - }, + initGek: (a) => ['POST', `${group(a)}/gek${a.rotate ? '?rotate=true' : ''}`], pairOperator: () => ['POST', '/api/operator/pair'], roster: (a) => ['GET', a.groupId ? `/api/roster?group_id=${anId(a.groupId, 'the group')}` : '/api/roster'], |