diff --git a/rpc.js b/rpc.js index 486c01e42..a69bfc4ef 100644 --- a/rpc.js +++ b/rpc.js @@ -943,10 +943,14 @@ var upload_complete = function (Env, publicKey, id, cb) { var paths = Env.paths; var session = getSession(Env.Sessions, publicKey); + if (session.blobstage && session.blobstage.close) { + session.blobstage.close(); + delete session.blobstage; + } + if (!testFileId(id)) { - console.log(id); WARN('uploadComplete', "id is invalid"); - return void cb('RENAME_ERR'); + return void cb('EINVAL_ID'); } var oldPath = makeFilePath(paths.staging, publicKey); @@ -988,11 +992,6 @@ var upload_complete = function (Env, publicKey, id, cb) { return void cb(e || 'PATH_ERR'); } - if (session.blobstage && session.blobstage.close) { - session.blobstage.close(); - delete session.blobstage; - } - // lol wut handle ur errors Fs.rename(oldPath, newPath, function (e) { if (e) { @@ -1099,6 +1098,118 @@ var owned_upload_complete = function (Env, safeKey, cb) { }); }; +var owned_upload_complete_2 = function (Env, safeKey, id, cb) { + var session = getSession(Env.Sessions, safeKey); + + // the file has already been uploaded to the staging area + // close the pending writestream + if (session.blobstage && session.blobstage.close) { + session.blobstage.close(); + delete session.blobstage; + } + + if (!testFileId(id)) { + WARN('ownedUploadComplete', "id is invalid"); + return void cb('EINVAL_ID'); + } + + var oldPath = makeFilePath(Env.paths.staging, safeKey); + if (typeof(oldPath) !== 'string') { + return void cb('EINVAL_CONFIG'); + } + + // construct relevant paths + var root = Env.paths.blob; + + //var safeKey = escapeKeyCharacters(safeKey); + var safeKeyPrefix = safeKey.slice(0, 3); + + //var blobId = createFileId(); + var blobIdPrefix = id.slice(0, 2); + + var ownPath = Path.join(root, safeKeyPrefix, safeKey, blobIdPrefix); + var filePath = Path.join(root, blobIdPrefix); + + var tryId = function (path, cb) { + Fs.access(path, Fs.constants.R_OK | Fs.constants.W_OK, function (e) { + if (!e) { + // generate a new id (with the same prefix) and recurse + WARN('ownedUploadComplete', 'id is already used '+ id); + return void cb('EEXISTS'); + } else if (e.code === 'ENOENT') { + // no entry, so it's safe for us to proceed + return void cb(); + } else { + // it failed in an unexpected way. log it + WARN(e, 'ownedUploadComplete'); + return void cb(e.code); + } + }); + }; + + // the user wants to move it into blob and create a empty file with the same id + // in their own space: + // /blob/safeKeyPrefix/safeKey/blobPrefix/blobID + + var finalPath; + var finalOwnPath; + nThen(function (w) { + // make the requisite directory structure using Mkdirp + Mkdirp(filePath, w(function (e /*, path */) { + if (e) { // does not throw error if the directory already existed + w.abort(); + return void cb(e); + } + })); + Mkdirp(ownPath, w(function (e /*, path */) { + if (e) { // does not throw error if the directory already existed + w.abort(); + return void cb(e); + } + })); + }).nThen(function (w) { + // make sure the id does not collide with another + finalPath = Path.join(filePath, id); + finalOwnPath = Path.join(ownPath, id); + tryId(finalPath, w(function (e) { + if (e) { + w.abort(); + return void cb(e); + } + })); + }).nThen(function (w) { + // Create the empty file proving ownership + Fs.writeFile(finalOwnPath, '', w(function (e) { + if (e) { + w.abort(); + return void cb(e.code); + } + // otherwise it worked... + })); + }).nThen(function (w) { + // move the existing file to its new path + + // flow is dumb and I need to guard against this which will never happen + /*:: if (typeof(oldPath) === 'object') { throw new Error('should never happen'); } */ + Fs.rename(oldPath /* XXX */, finalPath, w(function (e) { + if (e) { + // Remove the ownership file + // XXX not needed if we have a cleanup script? + Fs.unlink(finalOwnPath, function (e) { + WARN(e, 'Removing ownership file ownedUploadComplete'); + }); + w.abort(); + return void cb(e.code); + } + // otherwise it worked... + })); + }).nThen(function () { + // clean up their session when you're done + // call back with the blob id... + cb(void 0, id); + }); +}; + var upload_status = function (Env, publicKey, filesize, cb) { var paths = Env.paths; @@ -1506,7 +1617,7 @@ RPC.create = function ( }); case 'OWNED_UPLOAD_COMPLETE': if (!privileged) { return deny(); } - return void owned_upload_complete(Env, safeKey, function (e, blobId) { + return void owned_upload_complete_2(Env, safeKey, msg[1], function (e, blobId) { WARN(e, blobId); Respond(e, blobId); }); diff --git a/www/common/cryptpad-common.js b/www/common/cryptpad-common.js index 0d934d20b..cb8d94bf8 100644 --- a/www/common/cryptpad-common.js +++ b/www/common/cryptpad-common.js @@ -204,8 +204,8 @@ define([ }); }; - common.uploadComplete = function (id, cb) { - postMessage("UPLOAD_COMPLETE", id, function (obj) { + common.uploadComplete = function (id, owned, cb) { + postMessage("UPLOAD_COMPLETE", {id: id, owned, owned}, function (obj) { if (obj && obj.error) { return void cb(obj.error); } cb(null, obj); }); diff --git a/www/common/outer/async-store.js b/www/common/outer/async-store.js index f64f9efbe..680712f17 100644 --- a/www/common/outer/async-store.js +++ b/www/common/outer/async-store.js @@ -230,9 +230,18 @@ define([ }); }; - Store.uploadComplete = function (id, cb) { + Store.uploadComplete = function (data, cb) { if (!store.rpc) { return void cb({error: 'RPC_NOT_READY'}); } - store.rpc.uploadComplete(id, function (err, res) { + if (data.owned) { + // Owned file + store.rpc.ownedUploadComplete(data.id, function (err, res) { + if (err) { return void cb({error:err}); } + cb(res); + }); + return; + } + // Normal upload + store.rpc.uploadComplete(data.id, function (err, res) { if (err) { return void cb({error:err}); } cb(res); }); @@ -678,6 +687,7 @@ define([ if (Store.channel && Store.channel.wc && channel === Store.channel.wc.id) { owners = Store.channel.data.owners || undefined; } + var expire; if (Store.channel && Store.channel.wc && channel === Store.channel.wc.id) { expire = +Store.channel.data.expire || undefined; @@ -726,7 +736,11 @@ define([ contains = true; pad.atime = +new Date(); pad.title = title; - pad.owners = owners; + if (owners || h.type !== "file") { + // OWNED_FILES + // Never remove owner for files + pad.owners = owners; + } pad.expire = expire; // If the href is different, it means we have a stronger one diff --git a/www/common/outer/upload.js b/www/common/outer/upload.js index 6ca3478fb..65f674517 100644 --- a/www/common/outer/upload.js +++ b/www/common/outer/upload.js @@ -11,6 +11,10 @@ define([ var u8 = file.blob; // This is not a blob but a uint8array var metadata = file.metadata; + var owned = file.isOwned; +// XXX +owned = true; + // if it exists, path contains the new pad location in the drive var path = file.path; @@ -34,9 +38,16 @@ define([ }); }; + var edPublic; nThen(function (waitFor) { // Generate a hash and check if the resulting id is valid (not already used) getValidHash(waitFor()); + }).nThen(function (waitFor) { + if (!owned) { return; } + common.getMetadata(waitFor(function (err, m) { + edPublic = m.priv.edPublic; + metadata.owners = [edPublic]; + })); }).nThen(function () { var next = FileCrypto.encrypt(u8, metadata, key); @@ -68,7 +79,7 @@ define([ } // if not box then done - common.uploadComplete(id, function (e) { + common.uploadComplete(id, owned, function (e) { if (e) { return void console.error(e); } var uri = ['', 'blob', id.slice(0,2), id].join('/'); console.log("encrypted blob is now available as %s", uri); @@ -89,6 +100,7 @@ define([ if (err) { return void console.error(err); } onComplete(href); common.setPadAttribute('fileType', metadata.type, null, href); + common.setPadAttribute('owners', metadata.owners, null, href); }); }); }; diff --git a/www/common/pinpad.js b/www/common/pinpad.js index 1f31a884a..6d6295989 100644 --- a/www/common/pinpad.js +++ b/www/common/pinpad.js @@ -187,6 +187,17 @@ define([ }); }; + exp.ownedUploadComplete = function (id, cb) { + rpc.send('OWNED_UPLOAD_COMPLETE', id, function (e, res) { + if (e) { return void cb(e); } + var id = res[0]; + if (typeof(id) !== 'string') { + return void cb('INVALID_ID'); + } + cb(void 0, id); + }); + }; + exp.uploadStatus = function (size, cb) { if (typeof(size) !== 'number') { return void setTimeout(function () { diff --git a/www/file/inner.js b/www/file/inner.js index d472cb95d..f9b2a462a 100644 --- a/www/file/inner.js +++ b/www/file/inner.js @@ -104,6 +104,10 @@ define([ toolbar.$rightside.append(common.createButton('hashtag', true)); } + var owners = metadata.owners; + if (owners) { + common.setPadAttribute('owners', owners); + } common.setPadAttribute('fileType', metadata.type);