diff --git a/www/common/cryptget.js b/www/common/cryptget.js index 9ca05164c..1fc248a19 100644 --- a/www/common/cryptget.js +++ b/www/common/cryptget.js @@ -21,9 +21,9 @@ define([ S.leave(); } catch (e) { console.log(e); } } - if (S.session && S.session.stop) { + if (S.realtime && S.realtime.stop) { try { - S.session.stop(); + S.realtime.stop(); } catch (e) { console.error(e); } } var abort = Util.find(S, ['session', 'realtime', 'abort']); diff --git a/www/settings/make-backup.js b/www/common/make-backup.js similarity index 65% rename from www/settings/make-backup.js rename to www/common/make-backup.js index b86f1f3b2..d9fb62d96 100644 --- a/www/settings/make-backup.js +++ b/www/common/make-backup.js @@ -1,11 +1,13 @@ define([ '/common/cryptget.js', + '/file/file-crypto.js', '/common/common-hash.js', - '/common/sframe-common-file.js', + '/common/common-util.js', '/bower_components/nthen/index.js', '/bower_components/saferphore/index.js', '/bower_components/jszip/dist/jszip.min.js', -], function (Crypt, Hash, SFCFile, nThen, Saferphore, JsZip) { +], function (Crypt, FileCrypto, Hash, Util, nThen, Saferphore, JsZip) { + var saveAs = window.saveAs; var sanitize = function (str) { return str.replace(/[\\/?%*:|"<>]/gi, '_')/*.toLowerCase()*/; @@ -43,6 +45,88 @@ define([ }); }; + + var _downloadFile = function (ctx, fData, cb, updateProgress) { + var cancelled = false; + var cancel = function () { + cancelled = true; + }; + var parsed = Hash.parsePadUrl(fData.href || fData.roHref); + var hash = parsed.hash; + var name = fData.filename || fData.title; + var secret = Hash.getSecrets('file', hash, fData.password); + var src = Hash.getBlobPathFromHex(secret.channel); + var key = secret.keys && secret.keys.cryptKey; + Util.fetch(src, function (err, u8) { + if (cancelled) { return; } + if (err) { return void cb('E404'); } + FileCrypto.decrypt(u8, key, function (err, res) { + if (cancelled) { return; } + if (err) { return void cb(err); } + if (!res.content) { return void cb('EEMPTY'); } + var dl = function () { + saveAs(res.content, name || res.metadata.name); + }; + cb(null, { + metadata: res.metadata, + content: res.content, + download: dl + }); + }, updateProgress && updateProgress.progress2); + }, updateProgress && updateProgress.progress); + return { + cancel: cancel + }; + + }; + + + var _downloadPad = function (ctx, pData, cb, updateProgress) { + var cancelled = false; + var cancel = function () { + cancelled = true; + }; + + var parsed = Hash.parsePadUrl(pData.href || pData.roHref); + var name = pData.filename || pData.title; + var opts = { + password: pData.password + }; + var done = false; + ctx.sframeChan.on("EV_CRYPTGET_PROGRESS", function (data) { + if (done || data.hash !== parsed.hash) { return; } + updateProgress.progress(data.progress); + if (data.progress === 1) { + done = true; + updateProgress.progress2(1); + } + }); + ctx.get({ + hash: parsed.hash, + opts: opts + }, function (err, val) { + if (cancelled) { return; } + if (err) { return; } + if (!val) { return; } + transform(ctx, parsed.type, val, function (res) { + if (cancelled) { return; } + if (!res.data) { return; } + var dl = function () { + saveAs(res.data, Util.fixFileName(name)); + }; + cb(null, { + metadata: res.metadata, + content: res.data, + download: dl + }); + }); + }); + return { + cancel: cancel + }; + + }; + // Add a file to the zip. We have to cryptget&transform it if it's a pad // or fetch&decrypt it if it's a file. var addFile = function (ctx, zip, fData, existingNames) { @@ -126,7 +210,7 @@ define([ // Files (mediatags...) var todoFile = function () { var it; - var dl = SFCFile.downloadFile(fData, function (err, res) { + var dl = _downloadFile(ctx, fData, function (err, res) { if (it) { clearInterval(it); } if (err) { return void error(err); } var opts = { @@ -189,6 +273,7 @@ define([ var ctx = { get: getPad, data: data.uo.drive, + folder: data.folder || ctx.data.root, sf: data.sf, zip: new JsZip(), errors: [], @@ -197,11 +282,12 @@ define([ max: 0, done: 0 }; + var filesData = data.sharedFolderId && ctx.sf[data.sharedFolderId] ? ctx.sf[data.sharedFolderId].filesData : ctx.data.filesData; progress('reading', -1); nThen(function (waitFor) { ctx.waitFor = waitFor; var zipRoot = ctx.zip.folder('Root'); - makeFolder(ctx, ctx.data.root, zipRoot, ctx.data.filesData); + makeFolder(ctx, ctx.folder, zipRoot, filesData); progress('download', {}); }).nThen(function () { console.log(ctx.zip); @@ -222,7 +308,33 @@ define([ }; }; + + var _downloadFolder = function (ctx, data, cb, updateProgress) { + create(data, ctx.get, function (blob, errors) { + console.error(errors); // TODO show user errors + var dl = function () { + saveAs(blob, data.folderName); + }; + cb(null, {download: dl}); + }, function (state, progress) { + if (state === "reading") { + updateProgress.folderProgress(0); + } + if (state === "download") { + if (typeof progress.current !== "number") { return; } + updateProgress.folderProgress(progress.current / progress.max); + } + else if (state === "done") { + updateProgress.folderProgress(1); + } + }); + }; + + return { - create: create + create: create, + downloadFile: _downloadFile, + downloadPad: _downloadPad, + downloadFolder: _downloadFolder, }; }); diff --git a/www/common/sframe-common-file.js b/www/common/sframe-common-file.js index 099176afd..32f031010 100644 --- a/www/common/sframe-common-file.js +++ b/www/common/sframe-common-file.js @@ -1,6 +1,7 @@ define([ 'jquery', '/file/file-crypto.js', + '/common/make-backup.js', '/common/common-thumbnail.js', '/common/common-interface.js', '/common/common-ui-elements.js', @@ -11,9 +12,8 @@ define([ '/bower_components/file-saver/FileSaver.min.js', '/bower_components/tweetnacl/nacl-fast.min.js', -], function ($, FileCrypto, Thumb, UI, UIElements, Util, Hash, h, Messages) { +], function ($, FileCrypto, MakeBackup, Thumb, UI, UIElements, Util, Hash, h, Messages) { var Nacl = window.nacl; - var saveAs = window.saveAs; var module = {}; var blobToArrayBuffer = function (blob, cb) { @@ -446,124 +446,108 @@ define([ createUploader(config.dropArea, config.hoverArea, config.body); - File.downloadFile = function (fData, cb) { - var parsed = Hash.parsePadUrl(fData.href || fData.roHref); - var hash = parsed.hash; - var name = fData.filename || fData.title; - var secret = Hash.getSecrets('file', hash, fData.password); - var src = Hash.getBlobPathFromHex(secret.channel); - var key = secret.keys && secret.keys.cryptKey; - common.getFileSize(secret.channel, function (e, data) { - var todo = function (file) { - if (queue.inProgress) { return; } - queue.inProgress = true; - var id = file.id; - - var $row = $table.find('tr[id="'+id+'"]'); - var $pv = $row.find('.cp-fileupload-table-progress-value'); - var $pb = $row.find('.cp-fileupload-table-progress-container'); - var $pc = $row.find('.cp-fileupload-table-progress'); - var $link = $row.find('.cp-fileupload-table-link'); - - var done = function () { - $row.find('.cp-fileupload-table-cancel').text('-'); - queue.inProgress = false; - queue.next(); - }; + var updateProgressbar = function (file, data, downloadFunction, cb) { + if (queue.inProgress) { return; } + queue.inProgress = true; + var id = file.id; - var updateDLProgress = function (progressValue) { - var text = Math.round(progressValue*100) + '%'; - text += ' ('+ Messages.download_step1 +'...)'; - $pv.text(text); - $pb.css({ - width: progressValue * $pc.width()+'px' - }); - }; - var updateProgress = function (progressValue) { - var text = Math.round(progressValue*100) + '%'; - text += progressValue === 1 ? '' : ' ('+ Messages.download_step2 +'...)'; - $pv.text(text); - $pb.css({ - width: progressValue * $pc.width()+'px' - }); - }; + var $row = $table.find('tr[id="'+id+'"]'); + var $pv = $row.find('.cp-fileupload-table-progress-value'); + var $pb = $row.find('.cp-fileupload-table-progress-container'); + var $pc = $row.find('.cp-fileupload-table-progress'); + var $link = $row.find('.cp-fileupload-table-link'); - var dl = module.downloadFile(fData, function (err, obj) { - $link.prepend($('', {'class': 'fa fa-external-link'})) - .attr('href', '#') - .click(function (e) { - e.preventDefault(); - obj.download(); - }); - done(); - if (obj) { obj.download(); } - cb(err, obj); - }, { - src: src, - key: key, - name: name, - progress: updateDLProgress, - progress2: updateProgress, - }); + var done = function () { + $row.find('.cp-fileupload-table-cancel').text('-'); + queue.inProgress = false; + queue.next(); + }; - var $cancel = $('', {'class': 'cp-fileupload-table-cancel-button fa fa-times'}).click(function () { - dl.cancel(); - $cancel.remove(); - $row.find('.cp-fileupload-table-progress-value').text(Messages.upload_cancelled); - done(); - }); - $row.find('.cp-fileupload-table-cancel').html('').append($cancel); - }; + var updateDLProgress = function (progressValue) { + var text = Math.round(progressValue * 100) + '%'; + text += ' (' + Messages.download_step1 + '...)'; + $pv.text(text); + $pb.css({ + width: progressValue * $pc.width() + 'px' + }); + }; + var updateDecryptProgress = function (progressValue) { + var text = Math.round(progressValue*100) + '%'; + text += progressValue === 1 ? '' : ' (' + Messages.download_step2 + '...)'; + $pv.text(text); + $pb.css({ + width: progressValue * $pc.width()+'px' + }); + }; + var updateProgress = function (progressValue) { + var text = Math.round(progressValue*100) + '%'; + $pv.text(text); + $pb.css({ + width: progressValue * $pc.width()+'px' + }); + }; + + var ctx = { + get: common.getPad, + sframeChan: sframeChan, + }; + downloadFunction(ctx, data, function (err, obj) { + $link.prepend($('', {'class': 'fa fa-external-link'})) + .attr('href', '#') + .click(function (e) { + e.preventDefault(); + obj.download(); + }); + done(); + if (obj) { obj.download(); } + cb(err, obj); + }, { + progress: updateDLProgress, + progress2: updateDecryptProgress, + folderProgress: updateProgress, + }); + +// var $cancel = $('', {'class': 'cp-fileupload-table-cancel-button fa fa-times'}).click(function () { +// dl.cancel(); +// $cancel.remove(); +// $row.find('.cp-fileupload-table-progress-value').text(Messages.upload_cancelled); +// done(); +// }); +// $row.find('.cp-fileupload-table-cancel').html('').append($cancel); + $row.find('.cp-fileupload-table-cancel').html(''); + }; + + + File.downloadFile = function (fData, cb) { + var name = fData.filename || fData.title; + common.getFileSize(fData.channel, function (e, data) { queue.push({ - dl: todo, + dl: function (file) { updateProgressbar(file, fData, MakeBackup.downloadFile, cb); }, size: data, name: name }); }); }; - return File; - }; - - module.downloadFile = function (fData, cb, obj) { - var cancelled = false; - var cancel = function () { - cancelled = true; + File.downloadPad = function (pData, cb) { + queue.push({ + dl: function (file) { updateProgressbar(file, pData, MakeBackup.downloadPad, cb); }, + size: 0, + name: pData.title, + }); }; - var src, key, name; - if (obj && obj.src && obj.key && obj.name) { - src = obj.src; - key = obj.key; - name = obj.name; - } else { - var parsed = Hash.parsePadUrl(fData.href || fData.roHref); - var hash = parsed.hash; - name = fData.filename || fData.title; - var secret = Hash.getSecrets('file', hash, fData.password); - src = Hash.getBlobPathFromHex(secret.channel); - key = secret.keys && secret.keys.cryptKey; - } - Util.fetch(src, function (err, u8) { - if (cancelled) { return; } - if (err) { return void cb('E404'); } - FileCrypto.decrypt(u8, key, function (err, res) { - if (cancelled) { return; } - if (err) { return void cb(err); } - if (!res.content) { return void cb('EEMPTY'); } - var dl = function () { - saveAs(res.content, name || res.metadata.name); - }; - cb(null, { - metadata: res.metadata, - content: res.content, - download: dl - }); - }, obj && obj.progress2); - }, obj && obj.progress); - return { - cancel: cancel + + File.downloadFolder = function (data, cb) { + queue.push({ + dl: function (file) { updateProgressbar(file, data, MakeBackup.downloadFolder, cb); }, + size: 0, + name: data.folderName, + }); }; + + return File; }; + return module; }); diff --git a/www/common/sframe-common-outer.js b/www/common/sframe-common-outer.js index 153126f7c..104bdaa12 100644 --- a/www/common/sframe-common-outer.js +++ b/www/common/sframe-common-outer.js @@ -883,7 +883,12 @@ define([ error: err, data: val }); - }, data.opts); + }, data.opts, function (progress) { + sframeChan.event("EV_CRYPTGET_PROGRESS", { + hash: data.hash, + progress: progress, + }); + }); }; //return void todo(); if (i > 30) { diff --git a/www/common/sframe-common.js b/www/common/sframe-common.js index f93021bd3..82c71ace2 100644 --- a/www/common/sframe-common.js +++ b/www/common/sframe-common.js @@ -459,6 +459,14 @@ define([ }); }; */ + funcs.getPad = function (data, cb) { + ctx.sframeChan.query("Q_CRYPTGET", data, function (err, obj) { + if (err) { return void cb(err); } + if (obj.error) { return void cb(obj.error); } + cb(null, obj.data); + }, { timeout: 60000 }); + }; + funcs.gotoURL = function (url) { ctx.sframeChan.event('EV_GOTO_URL', url); }; funcs.openURL = function (url) { ctx.sframeChan.event('EV_OPEN_URL', url); }; funcs.openUnsafeURL = function (url) { diff --git a/www/drive/inner.js b/www/drive/inner.js index 5dfdd80e8..4292e5a7f 100644 --- a/www/drive/inner.js +++ b/www/drive/inner.js @@ -91,7 +91,7 @@ define([ var faEmpty = 'fa-trash-o'; var faRestore = 'fa-repeat'; var faShowParent = 'fa-location-arrow'; - var faDownload = 'cptools-file'; + var faDownload = 'fa-download'; var $folderIcon = $('', { "class": faFolder + " cptools cp-app-drive-icon-folder cp-app-drive-content-icon" }); @@ -353,22 +353,20 @@ define([ 'data-icon': faCollapseAll, }, Messages.fc_collapseAll)), $separator.clone()[0], - h('li', h('a.cp-app-drive-context-color.dropdown-item.cp-app-drive-context-editable', { - 'tabindex': '-1', - 'data-icon': faColor, - }, Messages.fc_color)), - h('li', h('a.cp-app-drive-context-download.dropdown-item', { + h('li', h('a.cp-app-drive-context-openparent.dropdown-item', { 'tabindex': '-1', - 'data-icon': faDownload, - }, Messages.download_mt_button)), + 'data-icon': faShowParent, + }, Messages.fm_openParent)), + $separator.clone()[0], h('li', h('a.cp-app-drive-context-share.dropdown-item', { 'tabindex': '-1', 'data-icon': 'fa-shhare-alt', }, Messages.shareButton)), - h('li', h('a.cp-app-drive-context-openparent.dropdown-item', { + h('li', h('a.cp-app-drive-context-download.dropdown-item', { 'tabindex': '-1', - 'data-icon': faShowParent, - }, Messages.fm_openParent)), + 'data-icon': faDownload, + }, Messages.download_mt_button)), + $separator.clone()[0], h('li', h('a.cp-app-drive-context-newfolder.dropdown-item.cp-app-drive-context-editable', { 'tabindex': '-1', 'data-icon': faFolder, @@ -377,10 +375,6 @@ define([ 'tabindex': '-1', 'data-icon': faSharedFolder, }, Messages.fc_newsharedfolder)), - h('li', h('a.cp-app-drive-context-hashtag.dropdown-item.cp-app-drive-context-editable', { - 'tabindex': '-1', - 'data-icon': faTags, - }, Messages.fc_hashtag)), $separator.clone()[0], h('li', h('a.cp-app-drive-context-newdoc.dropdown-item.cp-app-drive-context-editable', { 'tabindex': '-1', @@ -439,6 +433,15 @@ define([ 'tabindex': '-1', 'data-icon': faRename, }, Messages.fc_rename)), + h('li', h('a.cp-app-drive-context-color.dropdown-item.cp-app-drive-context-editable', { + 'tabindex': '-1', + 'data-icon': faColor, + }, Messages.fc_color)), + h('li', h('a.cp-app-drive-context-hashtag.dropdown-item.cp-app-drive-context-editable', { + 'tabindex': '-1', + 'data-icon': faTags, + }, Messages.fc_hashtag)), + $separator.clone()[0], h('li', h('a.cp-app-drive-context-delete.dropdown-item.cp-app-drive-context-editable', { 'tabindex': '-1', 'data-icon': faTrash, @@ -455,6 +458,7 @@ define([ 'tabindex': '-1', 'data-icon': faDelete, }, Messages.fc_remove_sharedfolder)), + $separator.clone()[0], h('li', h('a.cp-app-drive-context-properties.dropdown-item', { 'tabindex': '-1', 'data-icon': faProperties, @@ -1091,9 +1095,6 @@ define([ // We can only open parent in virtual categories hide.push('openparent'); } - if (!$element.is('.cp-border-color-file')) { - hide.push('download'); - } if ($element.is('.cp-app-drive-element-file')) { // No folder in files hide.push('color'); @@ -1157,6 +1158,7 @@ define([ hide.push('openparent'); hide.push('hashtag'); hide.push('download'); + hide.push('share'); } if (containsFolder && paths.length > 1) { // Cannot open multiple folders @@ -3593,6 +3595,26 @@ define([ }); }); }; + + + var downloadFolder = function (folderElement, folderName, sfId) { + var todo = function (data) { + data.folder = folderElement; + data.sharedFolderId = sfId; + data.folderName = Util.fixFileName(folderName) + '.zip'; + + APP.FM.downloadFolder(data, function (err, obj) { + console.log(err, obj); + console.log('DONE'); + }); + }; + todo({ + uo: proxy, + sf: folders, + }); + }; + + $contextMenu.on("click", "a", function(e) { e.stopPropagation(); var paths = $contextMenu.data('paths'); @@ -3681,15 +3703,47 @@ define([ openRecursive(paths[0].path); refresh(); } + else if ($this.hasClass('cp-app-drive-context-download')) { if (paths.length !== 1) { return; } - el = manager.find(paths[0].path); - if (!manager.isFile(el)) { return; } - data = manager.getFileData(el); - APP.FM.downloadFile(data, function (err, obj) { - console.log(err, obj); - console.log('DONE'); - }); + var path = paths[0]; + el = manager.find(path.path); + // folder + if (manager.isFolder(el)) { + // folder + var name, folderEl; + if (!manager.isSharedFolder(el)) { + name = path.path[path.path.length - 1]; + folderEl = el; + downloadFolder(folderEl, name); + } + // shared folder + else { + data = manager.getSharedFolderData(el); + name = data.title; + folderEl = manager.find(path.path.concat("root")); + downloadFolder(folderEl, name, el); + } + } + // file + else if (manager.isFile(el)) { + // imported file + if (path.element.is(".cp-border-color-file")) { + data = manager.getFileData(el); + APP.FM.downloadFile(data, function (err, obj) { + console.log(err, obj); + console.log('DONE'); + }); + } + // pad + else { + data = manager.getFileData(el); + APP.FM.downloadPad(data, function (err, obj) { + console.log(err, obj); + console.log('DONE'); + }); + } + } } else if ($this.hasClass('cp-app-drive-context-share')) { if (paths.length !== 1) { return; } diff --git a/www/settings/inner.js b/www/settings/inner.js index e8e22a027..c084e9dcd 100644 --- a/www/settings/inner.js +++ b/www/settings/inner.js @@ -12,7 +12,7 @@ define([ '/customize/credential.js', '/customize/application_config.js', '/api/config', - '/settings/make-backup.js', + '/common/make-backup.js', '/common/common-feedback.js', '/common/jscolor.js', @@ -1083,18 +1083,9 @@ define([ var exportDrive = function () { Feedback.send('FULL_DRIVE_EXPORT_START'); var todo = function (data, filename) { - var getPad = function (data, cb) { - sframeChan.query("Q_CRYPTGET", data, function (err, obj) { - if (err) { return void cb(err); } - if (obj.error) { return void cb(obj.error); } - cb(null, obj.data); - }, { timeout: 60000 }); - }; - var ui = createExportUI(); - var bu = Backup.create(data, getPad, function (blob, errors) { - console.log(blob); + var bu = Backup.create(data, common.getPad, function (blob, errors) { saveAs(blob, filename); sframeChan.event('EV_CRYPTGET_DISCONNECT'); ui.complete(function () {