From 302030e1eed35ac07a6e95a5d7a1d986b86954eb Mon Sep 17 00:00:00 2001 From: yflory Date: Mon, 21 Oct 2019 15:22:59 +0200 Subject: [PATCH] Shared folder password change --- .../src/less2/include/alertify.less | 12 +- www/common/common-ui-elements.js | 6 +- www/common/cryptpad-common.js | 48 ++-- www/common/drive-ui.js | 80 ++++++- www/common/outer/async-store.js | 48 +++- www/common/outer/sharedfolder.js | 220 +++++++++++------- www/common/outer/store-rpc.js | 1 + www/common/outer/team.js | 34 ++- www/common/outer/userObject.js | 15 ++ www/common/proxy-manager.js | 77 +++++- www/common/sframe-common-outer.js | 2 +- www/common/userObject.js | 26 ++- www/drive/inner.js | 8 + 13 files changed, 446 insertions(+), 131 deletions(-) diff --git a/customize.dist/src/less2/include/alertify.less b/customize.dist/src/less2/include/alertify.less index bbc87f9c7..be4def5d7 100644 --- a/customize.dist/src/less2/include/alertify.less +++ b/customize.dist/src/less2/include/alertify.less @@ -116,7 +116,7 @@ }*/ } - .dialog, .alert { + .dialog { & > div { background-color: @alertify-dialog-bg; &.half { @@ -205,6 +205,16 @@ } } + ::placeholder { /* Chrome, Firefox, Opera, Safari 10.1+ */ + color: darken(@alertify-input-fg, 15%); + opacity: 1; /* Firefox */ + } + :-ms-input-placeholder { /* Internet Explorer 10-11 */ + color: darken(@alertify-input-fg, 15%); + } + ::-ms-input-placeholder { /* Microsoft Edge */ + color: darken(@alertify-input-fg, 15%); + } input:not(.form-control), textarea { background-color: @alertify-input-bg; color: @alertify-input-fg; diff --git a/www/common/common-ui-elements.js b/www/common/common-ui-elements.js index cb270b3df..9d51f5500 100644 --- a/www/common/common-ui-elements.js +++ b/www/common/common-ui-elements.js @@ -548,6 +548,7 @@ define([ var sframeChan = common.getSframeChannel(); var changePwTitle = Messages.properties_changePassword; var changePwConfirm = Messages.properties_confirmChange; + var isSharedFolder = parsed.type === 'drive'; if (!hasPassword) { changePwTitle = Messages.properties_addPassword; changePwConfirm = Messages.properties_confirmNew; @@ -577,6 +578,7 @@ define([ password: newPass }, function (err, data) { if (err || data.error) { + console.error(err || data.error); return void UI.alert(Messages.properties_passwordError); } UI.findOKButton().click(); @@ -589,7 +591,9 @@ define([ }, {force: true}); } return void UI.alert(Messages.properties_passwordSuccess, function () { - common.gotoURL(hasPassword && newPass ? undefined : (data.href || data.roHref)); + if (!isSharedFolder) { + common.gotoURL(hasPassword && newPass ? undefined : (data.href || data.roHref)); + } }, {force: true}); }); }); diff --git a/www/common/cryptpad-common.js b/www/common/cryptpad-common.js index 7f16fa860..5d65e92b6 100644 --- a/www/common/cryptpad-common.js +++ b/www/common/cryptpad-common.js @@ -867,13 +867,15 @@ define([ } var newHref = '/' + parsed.type + '/#' + newHash; + var isSharedFolder = parsed.type === 'drive'; + var optsGet = {}; var optsPut = { password: newPassword, - metadata: {} + metadata: {}, + initialState: isSharedFolder ? '{}' : undefined }; - Nthen(function (waitFor) { if (parsed.hashData && parsed.hashData.password) { common.getPadAttribute('password', waitFor(function (err, password) { @@ -933,7 +935,9 @@ define([ } var expire = oldMetadata.expire; - optsPut.metadata.expire = (expire - (+new Date())) / 1000; // Lifetime in seconds + if (expire) { + optsPut.metadata.expire = (expire - (+new Date())) / 1000; // Lifetime in seconds + } }).nThen(function (waitFor) { Crypt.get(parsed.hash, waitFor(function (err, val) { if (err) { @@ -948,23 +952,22 @@ define([ }), optsPut); }), optsGet); }).nThen(function (waitFor) { + if (isSharedFolder) { + postMessage("UPDATE_SHARED_FOLDER_PASSWORD", { + href: href, + oldChannel: oldChannel, + password: newPassword + }, waitFor(function (obj) { + console.error(obj); + })); + return; + } pad.leavePad({ channel: oldChannel }, waitFor()); pad.onDisconnectEvent.fire(true); }).nThen(function (waitFor) { - common.removeOwnedChannel({ - channel: oldChannel, - teamId: teamId - }, waitFor(function (obj) { - if (obj && obj.error) { - waitFor.abort(); - return void cb(obj); - } - })); - common.unpinPads([oldChannel], waitFor(), teamId); - common.pinPads([newSecret.channel], waitFor(), teamId); - }).nThen(function (waitFor) { + // Set the new password to our pad data common.setPadAttribute('password', newPassword, waitFor(function (err) { if (err) { warning = true; } }), href); @@ -981,6 +984,21 @@ define([ common.setPadAttribute('href', newHref, waitFor(function (err) { if (err) { warning = true; } }), href); + }).nThen(function (waitFor) { + // delete the old pad + common.removeOwnedChannel({ + channel: oldChannel, + teamId: teamId + }, waitFor(function (obj) { + if (obj && obj.error) { + waitFor.abort(); + return void cb(obj); + } + })); + if (!isSharedFolder) { + common.unpinPads([oldChannel], waitFor(), teamId); + common.pinPads([newSecret.channel], waitFor(), teamId); + } }).nThen(function () { cb({ warning: warning, diff --git a/www/common/drive-ui.js b/www/common/drive-ui.js index 1561db904..551209fe3 100644 --- a/www/common/drive-ui.js +++ b/www/common/drive-ui.js @@ -3745,7 +3745,7 @@ define([ if (manager.isSharedFolder(el)) { delete data.roHref; //data.noPassword = true; - data.noEditPassword = true; + //data.noEditPassword = true; data.noExpiration = true; // this is here to allow users to check the channel id of a shared folder // we should remove it at some point @@ -4403,6 +4403,7 @@ define([ refresh(); UI.removeLoadingScreen(); + /* if (!APP.team) { sframeChan.query('Q_DRIVE_GETDELETED', null, function (err, data) { var ids = manager.findChannels(data); @@ -4417,6 +4418,83 @@ define([ UI.log(Messages._getKey('fm_deletedPads', [titles.join(', ')])); }); } + */ + var deprecated = files.sharedFoldersTemp; + var nt = nThen; + var passwordModal = function (fId, data, cb) { + var content = []; + var folderName = ''+ (data.lastTitlei || Messages.fm_newFolder) +''; + content.push(UI.setHTML(h('p'), Messages._getKey('drive_sfPassword', [folderName]))); + if (data.lastTitle) { + content.push(h('p', [ + Messages.fm_folderName, + ' ', + h('input', data.lastTitle) + ])); + } + var newPassword = UI.passwordInput({ + id: 'cp-app-prop-change-password', + placeholder: Messages.settings_changePasswordNew, + style: 'flex: 1;' + }); + var passwordOk = h('button', Messages.properties_changePasswordButton); + var changePass = h('span.cp-password-container', [ + newPassword, + passwordOk + ]); + content.push(changePass); + var div = h('div', content); + + var locked = false; + $(passwordOk).click(function () { + if (locked) { return; } + var pw = $(newPassword).find('.cp-password-input').val(); + locked = true; + $(div).find('.alert').remove(); + $(passwordOk).html('').append(h('span.fa.fa-spinner.fa-spin', {style: 'margin-left: 0'})); + manager.restoreSharedFolder(fId, pw, function (err, obj) { + if (obj && obj.error) { + var wrong = h('div.alert.alert-danger', Messages.drive_sfPasswordError); + $(div).prepend(wrong); + $(passwordOk).text(Messages.properties_changePasswordButton); + locked = false; + return; + } + UI.findCancelButton($(div).closest('.alertify')).click(); + cb(); + }); + }); + var buttons = [{ + className: 'primary', + name: Messages.forgetButton, + onClick: function () { + manager.delete([['sharedFoldersTemp', fId]], function () { + }); + }, + keys: [] + }, { + className: 'cancel', + name: Messages.later, + onClick: function () {}, + keys: [27] + }]; + return UI.dialog.customModal(div, { + buttons: buttons, + onClose: cb + }); + }; + if (typeof (deprecated) === "object") { + Object.keys(deprecated).forEach(function (fId) { + nt = nt(function (waitFor) { + var data = deprecated[fId]; + UI.openCustomModal(passwordModal(fId, data, waitFor())); + }).nThen; + }); + nt(function () { + refresh(); + }); + } + return { refresh: refresh, diff --git a/www/common/outer/async-store.js b/www/common/outer/async-store.js index 505a4cbc9..cf382caff 100644 --- a/www/common/outer/async-store.js +++ b/www/common/outer/async-store.js @@ -454,7 +454,7 @@ define([ Store.isNewChannel = function (clientId, data, cb) { if (!store.anon_rpc) { return void cb({error: 'ANON_RPC_NOT_READY'}); } - var channelId = Hash.hrefToHexChannelId(data.href, data.password); + var channelId = data.channel || Hash.hrefToHexChannelId(data.href, data.password); store.anon_rpc.send("IS_NEW_CHANNEL", channelId, function (e, response) { if (e) { return void cb({error: e}); } if (response && response.length && typeof(response[0]) === 'boolean') { @@ -1778,21 +1778,24 @@ define([ } }; }; - Store.loadSharedFolder = function (teamId, id, data, cb) { + Store.loadSharedFolder = function (teamId, id, data, cb, isNew) { var s = getStore(teamId); if (!s) { return void cb({ error: 'ENOTFOUND' }); } - var rt = SF.load({ + SF.load({ + isNew: isNew, network: store.network, - store: s + store: s, + isNewChannel: Store.isNewChannel }, id, data, cb); - return rt; }; - var loadSharedFolder = function (id, data, cb) { - Store.loadSharedFolder(null, id, data, cb); + var loadSharedFolder = function (id, data, cb, isNew) { + Store.loadSharedFolder(null, id, data, cb, isNew); }; Store.loadSharedFolderAnon = function (clientId, data, cb) { - Store.loadSharedFolder(null, data.id, data.data, function () { - cb(); + Store.loadSharedFolder(null, data.id, data.data, function (rt) { + cb({ + error: rt ? undefined : 'EDELETED' + }); }); }; Store.addSharedFolder = function (clientId, data, cb) { @@ -1805,6 +1808,9 @@ define([ cb(id); }); }; + Store.updateSharedFolderPassword = function (clientId, data, cb) { + SF.updatePassword(Store, data, store.network, cb); + }; // Drive Store.userObjectCommand = function (clientId, cmdData, cb) { @@ -1889,6 +1895,27 @@ define([ }); }; registerProxyEvents = function (proxy, fId) { + if (!fId) { + // Listen for shared folder password change + proxy.on('change', ['drive', UserObject.SHARED_FOLDERS], function (o, n, p) { + if (p.length > 3 && p[3] === 'password') { + var id = p[2]; + var data = proxy.drive[UserObject.SHARED_FOLDERS][id]; + var href = store.manager.user.userObject.getHref ? + store.manager.user.userObject.getHref(data) : data.href; + var parsed = Hash.parsePadUrl(href); + var secret = Hash.getSecrets(parsed.type, parsed.hash, o); + SF.updatePassword({ + oldChannel: secret.channel, + password: n, + href: href + }, store.network, function () { + console.log('Shared folder password changed'); + }); + return false; + } + }); + } proxy.on('change', [], function (o, n, p) { if (fId) { // Pin the new pads @@ -2039,7 +2066,8 @@ define([ pin: pin, unpin: unpin, loadSharedFolder: loadSharedFolder, - settings: proxy.settings + settings: proxy.settings, + Store: Store }, { outer: true, removeOwnedChannel: function (channel, cb) { Store.removeOwnedChannel('', channel, cb); }, diff --git a/www/common/outer/sharedfolder.js b/www/common/outer/sharedfolder.js index 6d48ae915..1778db3f3 100644 --- a/www/common/outer/sharedfolder.js +++ b/www/common/outer/sharedfolder.js @@ -22,78 +22,104 @@ define([ SF.load = function (config, id, data, cb) { var network = config.network; var store = config.store; - var teamId = store.id || -1; + var isNew = config.isNew; + var isNewChannel = config.isNewChannel; + var teamId = store.id; var handler = store.handleSharedFolder; var parsed = Hash.parsePadUrl(data.href); var secret = Hash.getSecrets('drive', parsed.hash, data.password); - var sf = allSharedFolders[secret.channel]; - if (sf && sf.ready && sf.rt) { - // The shared folder is already loaded, return its data - setTimeout(function () { - var leave = function () { SF.leave(secret.channel, teamId); }; - store.manager.addProxy(id, sf.rt.proxy, leave); - cb(sf.rt, sf.metadata); - }); - sf.team.push(teamId); - if (handler) { handler(id, sf.rt); } - return sf.rt; - } - if (sf && sf.queue && sf.rt) { - // The shared folder is loading, add our callbacks to the queue - sf.queue.push({ - cb: cb, - store: store, - id: id - }); - sf.team.push(teamId); - if (handler) { handler(id, sf.rt); } - return sf.rt; - } - - sf = allSharedFolders[secret.channel] = { - queue: [{ - cb: cb, - store: store, - id: id - }], - team: [store.id || -1] - }; - - var owners = data.owners; - var listmapConfig = { - data: {}, - channel: secret.channel, - readOnly: false, - crypto: Crypto.createEncryptor(secret.keys), - userName: 'sharedFolder', - logLevel: 1, - ChainPad: ChainPad, - classic: true, - network: network, - metadata: { - validateKey: secret.keys.validateKey || undefined, - owners: owners + // If we try to load en existing shared folder (isNew === false) but this folder + // doesn't exist in the database, abort and cb + nThen(function (waitFor) { + isNewChannel(null, { channel: secret.channel }, waitFor(function (obj) { + if (obj.isNew && !isNew) { + store.manager.deprecateProxy(id, secret.channel); + waitFor.abort(); + return void cb(null); + } + })); + }).nThen(function () { + var sf = allSharedFolders[secret.channel]; + if (sf && sf.ready && sf.rt) { + // The shared folder is already loaded, return its data + setTimeout(function () { + var leave = function () { SF.leave(secret.channel, teamId); }; + store.manager.addProxy(id, sf.rt.proxy, leave); + cb(sf.rt, sf.metadata); + }); + sf.teams.push(store); + if (handler) { handler(id, sf.rt); } + return sf.rt; } - }; - var rt = sf.rt = Listmap.create(listmapConfig); - rt.proxy.on('ready', function (info) { - if (!sf.queue) { - return; + if (sf && sf.queue && sf.rt) { + // The shared folder is loading, add our callbacks to the queue + sf.queue.push({ + cb: cb, + store: store, + id: id + }); + sf.teams.push(store); + if (handler) { handler(id, sf.rt); } + return sf.rt; } - sf.queue.forEach(function (obj) { - var leave = function () { SF.leave(secret.channel, teamId); }; - obj.store.manager.addProxy(obj.id, rt.proxy, leave); - obj.cb(rt, info.metadata); + + sf = allSharedFolders[secret.channel] = { + queue: [{ + cb: cb, + store: store, + id: id + }], + teams: [store] + }; + + var owners = data.owners; + var listmapConfig = { + data: {}, + channel: secret.channel, + readOnly: false, + crypto: Crypto.createEncryptor(secret.keys), + userName: 'sharedFolder', + logLevel: 1, + ChainPad: ChainPad, + classic: true, + network: network, + metadata: { + validateKey: secret.keys.validateKey || undefined, + owners: owners + } + }; + var rt = sf.rt = Listmap.create(listmapConfig); + rt.proxy.on('ready', function (info) { + if (!sf.queue) { + return; + } + sf.queue.forEach(function (obj) { + var leave = function () { SF.leave(secret.channel, teamId); }; + obj.store.manager.addProxy(obj.id, rt.proxy, leave); + obj.cb(rt, info.metadata); + }); + sf.metadata = info.metadata; + sf.ready = true; + delete sf.queue; + }); + rt.proxy.on('error', function (info) { + if (info && info.error) { + if (info.error === "EDELETED" ) { + try { + // Deprecate the shared folder from each team + sf.teams.forEach(function (store) { + store.manager.deprecateProxy(id, secret.channel); + }); + } catch (e) {} + delete allSharedFolders[secret.channel]; + } + } }); - sf.leave = info.leave; - sf.metadata = info.metadata; - sf.ready = true; - delete sf.queue; + + if (handler) { handler(id, rt); } }); - if (handler) { handler(id, rt); } - return rt; }; SF.leave = function (channel, teamId) { @@ -101,8 +127,14 @@ define([ if (!sf) { return; } var clients = sf.teams; if (!Array.isArray(clients)) { return; } - var idx = clients.indexOf(teamId); - if (idx === -1) { return; } + var idx; + clients.some(function (store, i) { + if (store.id === teamId) { + idx = i; + return true; + } + }); + if (typeof (idx) === "undefined") { return; } // Remove the selected team clients.splice(idx, 1); @@ -113,6 +145,38 @@ define([ } }; + SF.updatePassword = function (Store, data, network, cb) { + var oldChannel = data.oldChannel; + var href = data.href; + var password = data.password; + var parsed = Hash.parsePadUrl(href); + var secret = Hash.getSecrets(parsed.type, parsed.hash, password); + var sf = allSharedFolders[oldChannel]; + if (!sf) { return void cb({ error: 'ENOTFOUND' }); } + if (sf.rt && sf.rt.stop) { + sf.rt.stop(); + } + var nt = nThen; + sf.teams.forEach(function (s) { + nt = nt(function (waitFor) { + var sfId = s.manager.user.userObject.getSFIdFromHref(href); + var shared = Util.find(s.proxy, ['drive', UserObject.SHARED_FOLDERS]) || {}; + if (!sfId || !shared[sfId]) { return; } + var sf = JSON.parse(JSON.stringify(shared[sfId])); + sf.password = password; + SF.load({ + network: network, + store: s, + isNewChannel: Store.isNewChannel + }, sfId, sf, waitFor()); + if (!s.rpc) { return; } + s.rpc.unpin([oldChannel], waitFor()); + s.rpc.pin([secret.channel], waitFor()); + }).nThen; + }); + nt(cb); + }; + /* loadSharedFolders load all shared folder stored in a given drive - store: user or team main store @@ -121,35 +185,13 @@ define([ */ SF.loadSharedFolders = function (Store, network, store, userObject, waitFor) { var shared = Util.find(store.proxy, ['drive', UserObject.SHARED_FOLDERS]) || {}; - // Check if any of our shared folder is expired or deleted by its owner. - // If we don't check now, Listmap will create an empty proxy if it no longer exists on - // the server. nThen(function (waitFor) { - var checkExpired = Object.keys(shared).map(function (fId) { - return shared[fId].channel; - }); - Store.getDeletedPads(null, {list: checkExpired}, waitFor(function (chans) { - if (chans && chans.error) { return void console.error(chans.error); } - if (!Array.isArray(chans) || !chans.length) { return; } - var toDelete = []; - Object.keys(shared).forEach(function (fId) { - if (chans.indexOf(shared[fId].channel) !== -1 - && toDelete.indexOf(fId) === -1) { - toDelete.push(fId); - } - }); - toDelete.forEach(function (fId) { - var paths = userObject.findFile(Number(fId)); - userObject.delete(paths, waitFor(), true); - delete shared[fId]; - }); - })); - }).nThen(function (waitFor) { Object.keys(shared).forEach(function (id) { var sf = shared[id]; SF.load({ network: network, - store: store + store: store, + isNewChannel: Store.isNewChannel }, id, sf, waitFor()); }); }).nThen(waitFor()); diff --git a/www/common/outer/store-rpc.js b/www/common/outer/store-rpc.js index c75e69b70..9accc5f29 100644 --- a/www/common/outer/store-rpc.js +++ b/www/common/outer/store-rpc.js @@ -56,6 +56,7 @@ define([ ADD_SHARED_FOLDER: Store.addSharedFolder, LOAD_SHARED_FOLDER: Store.loadSharedFolderAnon, RESTORE_SHARED_FOLDER: Store.restoreSharedFolder, + UPDATE_SHARED_FOLDER_PASSWORD: Store.updateSharedFolderPassword, // Messaging ANSWER_FRIEND_REQUEST: Store.answerFriendRequest, SEND_FRIEND_REQUEST: Store.sendFriendRequest, diff --git a/www/common/outer/team.js b/www/common/outer/team.js index 8769e5e7a..df367932d 100644 --- a/www/common/outer/team.js +++ b/www/common/outer/team.js @@ -31,6 +31,27 @@ define([ var registerChangeEvents = function (ctx, team, proxy, fId) { if (!team) { return; } + if (!fId) { + // Listen for shared folder password change + proxy.on('change', ['drive', UserObject.SHARED_FOLDERS], function (o, n, p) { + if (p.length > 3 && p[3] === 'password') { + var id = p[2]; + var data = proxy.drive[UserObject.SHARED_FOLDERS][id]; + var href = team.manager.user.userObject.getHref ? + team.manager.user.userObject.getHref(data) : data.href; + var parsed = Hash.parsePadUrl(href); + var secret = Hash.getSecrets(parsed.type, parsed.hash, o); + SF.updatePassword(ctx.Store, { + oldChannel: secret.channel, + password: n, + href: href + }, ctx.store.network, function () { + console.log('Shared folder password changed'); + }); + return false; + } + }); + } proxy.on('change', [], function (o, n, p) { if (fId) { // Pin the new pads @@ -208,13 +229,13 @@ define([ }; })); }).nThen(function () { - var loadSharedFolder = function (id, data, cb) { + var loadSharedFolder = function (id, data, cb, isNew) { SF.load({ + isNew: isNew, network: ctx.store.network, - store: team - }, id, data, function (id, rt) { - cb(id, rt); - }); + store: team, + isNewChannel: ctx.Store.isNewChannel + }, id, data, cb); }; var manager = team.manager = ProxyManager.create(proxy.drive, { onSync: function (cb) { ctx.Store.onSync(id, cb); }, @@ -224,7 +245,8 @@ define([ loadSharedFolder: loadSharedFolder, settings: { drive: Util.find(ctx.store, ['proxy', 'settings', 'drive']) - } + }, + Store: ctx.Store }, { outer: true, removeOwnedChannel: function (channel, cb) { diff --git a/www/common/outer/userObject.js b/www/common/outer/userObject.js index d22d7d406..056beef28 100644 --- a/www/common/outer/userObject.js +++ b/www/common/outer/userObject.js @@ -28,6 +28,7 @@ define([ var TRASH = exp.TRASH; var TEMPLATE = exp.TEMPLATE; var SHARED_FOLDERS = exp.SHARED_FOLDERS; + var SHARED_FOLDERS_TEMP = exp.SHARED_FOLDERS_TEMP; var debug = exp.debug; @@ -74,6 +75,15 @@ define([ cb(null, id); }; + exp.deprecateSharedFolder = function (id) { + var data = files[SHARED_FOLDERS][id]; + if (!data) { return; } + files[SHARED_FOLDERS_TEMP][id] = JSON.parse(JSON.stringify(data)); + var paths = exp.findFile(Number(id)); + exp.delete(paths, null, true); + delete files[SHARED_FOLDERS][id]; + }; + // FILES DATA var spliceFileData = function (id) { delete files[FILES_DATA][id]; @@ -724,6 +734,10 @@ define([ var fixSharedFolders = function () { if (sharedFolder) { return; } if (typeof(files[SHARED_FOLDERS]) !== "object") { debug("SHARED_FOLDER was not an object"); files[SHARED_FOLDERS] = {}; } + if (typeof(files[SHARED_FOLDERS_TEMP]) !== "object") { + debug("SHARED_FOLDER_TEMP was not an object"); + files[SHARED_FOLDERS_TEMP] = {}; + } var sf = files[SHARED_FOLDERS]; var rootFiles = exp.getFiles([ROOT]); var root = exp.find([ROOT]); @@ -749,6 +763,7 @@ define([ } }; + var fixDrive = function () { Object.keys(files).forEach(function (key) { if (key.slice(0,1) === '/') { delete files[key]; } diff --git a/www/common/proxy-manager.js b/www/common/proxy-manager.js index ca914b57a..aae33e6d8 100644 --- a/www/common/proxy-manager.js +++ b/www/common/proxy-manager.js @@ -23,6 +23,12 @@ define([ // Only in outer userObject.fixFiles(); } + if (proxy.metadata && proxy.metadata.title) { + var sf = Env.user.proxy[UserObject.SHARED_FOLDERS][id]; + if (sf) { + sf.lastTitle = proxy.metadata.title; + } + } Env.folders[id] = { proxy: proxy, userObject: userObject, @@ -39,6 +45,12 @@ define([ delete Env.folders[id]; }; + // Password may have changed + var deprecateProxy = function (Env, id, channel) { + Env.unpinPads([channel], function () {}); + Env.user.userObject.deprecateSharedFolder(id); + }; + /* Tools */ @@ -447,6 +459,11 @@ define([ // 2b. load the proxy Env.loadSharedFolder(id, folderData, waitFor(function (rt, metadata) { + if (!rt) { + waitFor.abort(); + return void cb({ error: 'EDELETED' }); + } + if (!rt.proxy.metadata) { // Creating a new shared folder rt.proxy.metadata = { title: data.name || Messages.fm_newFolder }; } @@ -456,7 +473,7 @@ define([ if (metadata.owners) { fData.owners = metadata.owners; } if (metadata.expire) { fData.expire = +metadata.expire; } } - })); + }), !Boolean(data.folderData)); }).nThen(function () { Env.onSync(function () { cb(id); @@ -464,6 +481,42 @@ define([ }); }; + var _restoreSharedFolder = function (Env, _data, cb) { + var fId = _data.id; + var newPassword = _data.password; + var temp = Util.find(Env, ['user', 'proxy', UserObject.SHARED_FOLDERS_TEMP]); + var data = temp && temp[fId]; + if (!data) { return void cb({ error: 'EINVAL' }); } + if (!Env.Store) { return void cb({ error: 'ESTORE' }); } + var href = Env.user.userObject.getHref ? Env.user.userObject.getHref(data) : data.href; + var isNew = false; + nThen(function (waitFor) { + Env.Store.isNewChannel(null, { + href: href, + password: newPassword + }, waitFor(function (obj) { + if (!obj || obj.error) { + isNew = false; + return; + } + isNew = obj.isNew; + })); + }).nThen(function () { + if (isNew) { + return void cb({ error: 'ENOTFOUND' }); + } + data.password = newPassword; + _addSharedFolder(Env, { + path: ['root'], + folderData: data, + }, function () { + delete temp[fId]; + Env.onSync(cb); + }); + }); + + }; + // convert a folder to a Shared Folder var _convertFolderToSharedFolder = function (Env, data, cb) { return void cb({ @@ -570,6 +623,13 @@ define([ return void cb({error: 'E_NOTFOUND'}); } + // Deleted or password changed for a shared folder + if (data.paths.length === 1 && data.paths[0][0] === UserObject.SHARED_FOLDERS_TEMP) { + var temp = Util.find(Env, ['user', 'proxy', UserObject.SHARED_FOLDERS_TEMP]); + delete temp[data.paths[0][1]]; + return void Env.onSync(cb); + } + var toUnpin = []; var ownedRemoved; nThen(function (waitFor)  { @@ -666,6 +726,7 @@ define([ var el = Env.user.userObject.find(resolved.path); if (Env.user.userObject.isSharedFolder(el) && Env.folders[el]) { Env.folders[el].proxy.metadata.title = data.newName; + Env.user.proxy[UserObject.SHARED_FOLDERS][el].lastTitle = data.value; return void cb(); } } @@ -699,6 +760,8 @@ define([ _addFolder(Env, data, cb); break; case 'addSharedFolder': _addSharedFolder(Env, data, cb); break; + case 'restoreSharedFolder': + _restoreSharedFolder(Env, data, cb); break; case 'convertFolderToSharedFolder': _convertFolderToSharedFolder(Env, data, cb); break; case 'delete': @@ -925,6 +988,7 @@ define([ pinPads: data.pin, unpinPads: data.unpin, onSync: data.onSync, + Store: data.Store, loadSharedFolder: data.loadSharedFolder, cfg: uoConfig, edPublic: data.edPublic, @@ -947,6 +1011,7 @@ define([ // Manager addProxy: callWithEnv(addProxy), removeProxy: callWithEnv(removeProxy), + deprecateProxy: callWithEnv(deprecateProxy), addSharedFolder: callWithEnv(_addSharedFolder), // Drive command: callWithEnv(onCommand), @@ -1016,6 +1081,15 @@ define([ } }, cb); }; + var restoreSharedFolderInner = function (Env, fId, password, cb) { + return void Env.sframeChan.query("Q_DRIVE_USEROBJECT", { + cmd: "restoreSharedFolder", + data: { + id: fId, + password: password + } + }, cb); + }; var convertFolderToSharedFolderInner = function (Env, path, owned, password, cb) { return void Env.sframeChan.query("Q_DRIVE_USEROBJECT", { cmd: "convertFolderToSharedFolder", @@ -1228,6 +1302,7 @@ define([ emptyTrash: callWithEnv(emptyTrashInner), addFolder: callWithEnv(addFolderInner), addSharedFolder: callWithEnv(addSharedFolderInner), + restoreSharedFolder: callWithEnv(restoreSharedFolderInner), convertFolderToSharedFolder: callWithEnv(convertFolderToSharedFolderInner), delete: callWithEnv(deleteInner), restore: callWithEnv(restoreInner), diff --git a/www/common/sframe-common-outer.js b/www/common/sframe-common-outer.js index e919f1506..d14e89456 100644 --- a/www/common/sframe-common-outer.js +++ b/www/common/sframe-common-outer.js @@ -118,7 +118,7 @@ define([ msgEv.fire(msg); }); SFrameChannel.create(msgEv, postMsg, waitFor(function (sfc) { - sframeChan = sfc; + Utils.sframeChan = sframeChan = sfc; })); }); window.addEventListener('message', whenReady); diff --git a/www/common/userObject.js b/www/common/userObject.js index 7795f09b3..398ebe301 100644 --- a/www/common/userObject.js +++ b/www/common/userObject.js @@ -14,6 +14,7 @@ define([ var TRASH = module.TRASH = "trash"; var TEMPLATE = module.TEMPLATE = "template"; var SHARED_FOLDERS = module.SHARED_FOLDERS = "sharedFolders"; + var SHARED_FOLDERS_TEMP = module.SHARED_FOLDERS_TEMP = "sharedFoldersTemp"; // Maybe deleted or new password // Create untitled documents when no name is given var getLocaleDate = function () { @@ -45,6 +46,7 @@ define([ exp.TRASH = TRASH; exp.TEMPLATE = TEMPLATE; exp.SHARED_FOLDERS = SHARED_FOLDERS; + exp.SHARED_FOLDERS_TEMP = SHARED_FOLDERS_TEMP; var sharedFolder = exp.sharedFolder = config.sharedFolder; exp.id = config.id; @@ -379,11 +381,17 @@ define([ return Util.deduplicateString(ret); }; - var getIdFromHref = exp.getIdFromHref = function (href) { + var getIdFromHref = exp.getIdFromHref = function (_href) { var result; + var noPassword = function (str) { + if (!str) { return; } + var value = str.replace(/\/p\/?/, '/'); + return Hash.getRelativeHref(value); + }; + var href = noPassword(_href); getFiles([FILES_DATA]).some(function (id) { - if (files[FILES_DATA][id].href === href || - files[FILES_DATA][id].roHref === href) { + if (noPassword(files[FILES_DATA][id].href) === href || + noPassword(files[FILES_DATA][id].roHref) === href) { result = id; return true; } @@ -391,11 +399,17 @@ define([ return result; }; - exp.getSFIdFromHref = function (href) { + exp.getSFIdFromHref = function (_href) { var result; + var noPassword = function (str) { + if (!str) { return; } + var value = str.replace(/\/p\/?/, '/'); + return Hash.getRelativeHref(value); + }; + var href = noPassword(_href); getFiles([SHARED_FOLDERS]).some(function (id) { - if (files[SHARED_FOLDERS][id].href === href || - files[SHARED_FOLDERS][id].roHref === href) { + if (noPassword(files[SHARED_FOLDERS][id].href) === href || + noPassword(files[SHARED_FOLDERS][id].roHref) === href) { result = id; return true; } diff --git a/www/drive/inner.js b/www/drive/inner.js index 360944bbd..e75b09edd 100644 --- a/www/drive/inner.js +++ b/www/drive/inner.js @@ -44,6 +44,14 @@ define([ sframeChan.query('Q_DRIVE_GETOBJECT', { sharedFolder: fId }, waitFor(function (err, newObj) { + if (!APP.loggedIn && APP.newSharedFolder) { + if (!newObj || !Object.keys(newObj).length) { + // Empty anon drive: deleted + var msg = Messages.deletedError + '
' + Messages.errorRedirectToHome; + setTimeout(function () { UI.errorLoadingScreen(msg, false, function () {}); }); + APP.newSharedFolder = null; + } + } folders[fId] = folders[fId] || {}; copyObjectValue(folders[fId], newObj); if (manager && oldIds.indexOf(fId) === -1) {