Merge branch 'donkey' into staging
This commit is contained in:
@@ -203,6 +203,10 @@
|
|||||||
padding: @alertify_padding-base;
|
padding: @alertify_padding-base;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
span.cp-password-container {
|
||||||
|
margin-bottom: 15px;
|
||||||
|
}
|
||||||
|
|
||||||
input[type="checkbox"], input[type="radio"] {
|
input[type="checkbox"], input[type="radio"] {
|
||||||
width: auto;
|
width: auto;
|
||||||
padding: 0;
|
padding: 0;
|
||||||
|
|||||||
@@ -5,6 +5,7 @@
|
|||||||
input {
|
input {
|
||||||
flex: 1;
|
flex: 1;
|
||||||
min-width: 0;
|
min-width: 0;
|
||||||
|
margin-bottom: 0 !important; // Override margin from alertify
|
||||||
}
|
}
|
||||||
label, .fa {
|
label, .fa {
|
||||||
margin-left: 10px;
|
margin-left: 10px;
|
||||||
|
|||||||
@@ -597,9 +597,9 @@ define(function () {
|
|||||||
out.settings_templateSkipHint = "Quand vous créez un nouveau pad, et si vous possédez des modèles pour ce type de pad, une fenêtre peut apparaître pour demander si vous souhaitez importer un modèle. Ici vous pouvez choisir de ne jamais montrer cette fenêtre et donc de ne jamais utiliser de modèle.";
|
out.settings_templateSkipHint = "Quand vous créez un nouveau pad, et si vous possédez des modèles pour ce type de pad, une fenêtre peut apparaître pour demander si vous souhaitez importer un modèle. Ici vous pouvez choisir de ne jamais montrer cette fenêtre et donc de ne jamais utiliser de modèle.";
|
||||||
|
|
||||||
out.upload_title = "Hébergement de fichiers";
|
out.upload_title = "Hébergement de fichiers";
|
||||||
out.upload_rename = "Souhaitez-vous renommer <b>{0}</b> avant son stockage en ligne ?<br>" +
|
out.upload_modal_title = "Options d'importation du fichier";
|
||||||
"<em>L'extension du fichier ({1}) sera ajoutée automatiquement. "+
|
out.upload_modal_filename = "Nom (extension <em>{0}</em> ajoutée automatiquement)";
|
||||||
"Ce nom sera permanent et visible par les autres utilisateurs</em>.";
|
out.upload_modal_owner = "Être propriétaire du fichier";
|
||||||
out.upload_serverError = "Erreur interne: impossible d'importer le fichier pour l'instant.";
|
out.upload_serverError = "Erreur interne: impossible d'importer le fichier pour l'instant.";
|
||||||
out.upload_uploadPending = "Vous avez déjà un fichier en cours d'importation. Souhaitez-vous l'annuler et importer ce nouveau fichier ?";
|
out.upload_uploadPending = "Vous avez déjà un fichier en cours d'importation. Souhaitez-vous l'annuler et importer ce nouveau fichier ?";
|
||||||
out.upload_success = "Votre fichier ({0}) a été importé avec succès et ajouté à votre CryptDrive.";
|
out.upload_success = "Votre fichier ({0}) a été importé avec succès et ajouté à votre CryptDrive.";
|
||||||
|
|||||||
@@ -601,9 +601,9 @@ define(function () {
|
|||||||
out.settings_templateSkipHint = "When you create a new empty pad, if you have stored templates for this type of pad, a modal appears to ask if you want to use a template. Here you can choose to never show this modal and so to never use a template.";
|
out.settings_templateSkipHint = "When you create a new empty pad, if you have stored templates for this type of pad, a modal appears to ask if you want to use a template. Here you can choose to never show this modal and so to never use a template.";
|
||||||
|
|
||||||
out.upload_title = "File upload";
|
out.upload_title = "File upload";
|
||||||
out.upload_rename = "Do you want to rename <b>{0}</b> before uploading it to the server?<br>" +
|
out.upload_modal_title = "File upload options";
|
||||||
"<em>The file extension ({1}) will be added automatically. "+
|
out.upload_modal_filename = "File name (extension <em>{0}</em> added automatically)";
|
||||||
"This name will be permanent and visible to other users.</em>";
|
out.upload_modal_owner = "Owned file";
|
||||||
out.upload_serverError = "Server Error: unable to upload your file at this time.";
|
out.upload_serverError = "Server Error: unable to upload your file at this time.";
|
||||||
out.upload_uploadPending = "You already have an upload in progress. Cancel it and upload your new file?";
|
out.upload_uploadPending = "You already have an upload in progress. Cancel it and upload your new file?";
|
||||||
out.upload_success = "Your file ({0}) has been successfully uploaded and added to your drive.";
|
out.upload_success = "Your file ({0}) has been successfully uploaded and added to your drive.";
|
||||||
|
|||||||
263
rpc.js
263
rpc.js
@@ -36,6 +36,7 @@ var isValidId = function (chan) {
|
|||||||
[32, 48].indexOf(chan.length) > -1;
|
[32, 48].indexOf(chan.length) > -1;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/*
|
||||||
var uint8ArrayToHex = function (a) {
|
var uint8ArrayToHex = function (a) {
|
||||||
// call slice so Uint8Arrays work as expected
|
// call slice so Uint8Arrays work as expected
|
||||||
return Array.prototype.slice.call(a).map(function (e) {
|
return Array.prototype.slice.call(a).map(function (e) {
|
||||||
@@ -52,14 +53,24 @@ var uint8ArrayToHex = function (a) {
|
|||||||
}
|
}
|
||||||
}).join('');
|
}).join('');
|
||||||
};
|
};
|
||||||
|
*/
|
||||||
|
|
||||||
|
var testFileId = function (id) {
|
||||||
|
if (id.length !== 48 || /[^a-f0-9]/.test(id)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
};
|
||||||
|
|
||||||
|
/*
|
||||||
var createFileId = function () {
|
var createFileId = function () {
|
||||||
var id = uint8ArrayToHex(Nacl.randomBytes(24));
|
var id = uint8ArrayToHex(Nacl.randomBytes(24));
|
||||||
if (id.length !== 48 || /[^a-f0-9]/.test(id)) {
|
if (!testFileId(id)) {
|
||||||
throw new Error('file ids must consist of 48 hex characters');
|
throw new Error('file ids must consist of 48 hex characters');
|
||||||
}
|
}
|
||||||
return id;
|
return id;
|
||||||
};
|
};
|
||||||
|
*/
|
||||||
|
|
||||||
var makeToken = function () {
|
var makeToken = function () {
|
||||||
return Number(Math.floor(Math.random() * Number.MAX_SAFE_INTEGER))
|
return Number(Math.floor(Math.random() * Number.MAX_SAFE_INTEGER))
|
||||||
@@ -811,6 +822,17 @@ var makeFileStream = function (root, id, cb) {
|
|||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
var isFile = function (filePath, cb) {
|
||||||
|
/*:: if (typeof(filePath) !== 'string') { throw new Error('should never happen'); } */
|
||||||
|
Fs.stat(filePath, function (e, stats) {
|
||||||
|
if (e) {
|
||||||
|
if (e.code === 'ENOENT') { return void cb(void 0, false); }
|
||||||
|
return void cb(e.message);
|
||||||
|
}
|
||||||
|
return void cb(void 0, stats.isFile());
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
var clearOwnedChannel = function (Env, channelId, unsafeKey, cb) {
|
var clearOwnedChannel = function (Env, channelId, unsafeKey, cb) {
|
||||||
if (typeof(channelId) !== 'string' || channelId.length !== 32) {
|
if (typeof(channelId) !== 'string' || channelId.length !== 32) {
|
||||||
return cb('INVALID_ARGUMENTS');
|
return cb('INVALID_ARGUMENTS');
|
||||||
@@ -834,11 +856,66 @@ var clearOwnedChannel = function (Env, channelId, unsafeKey, cb) {
|
|||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
var removeOwnedBlob = function (Env, blobId, unsafeKey, cb) {
|
||||||
|
var safeKey = escapeKeyCharacters(unsafeKey);
|
||||||
|
var safeKeyPrefix = safeKey.slice(0,3);
|
||||||
|
var blobPrefix = blobId.slice(0,2);
|
||||||
|
|
||||||
|
var blobPath = makeFilePath(Env.paths.blob, blobId);
|
||||||
|
var ownPath = Path.join(Env.paths.blob, safeKeyPrefix, safeKey, blobPrefix, blobId);
|
||||||
|
|
||||||
|
nThen(function (w) {
|
||||||
|
// Check if the blob exists
|
||||||
|
isFile(blobPath, w(function (e, isFile) {
|
||||||
|
if (e) {
|
||||||
|
w.abort();
|
||||||
|
return void cb(e);
|
||||||
|
}
|
||||||
|
if (!isFile) {
|
||||||
|
WARN('removeOwnedBlob', 'The provided blob ID is not a file!');
|
||||||
|
w.abort();
|
||||||
|
return void cb('EINVAL_BLOBID');
|
||||||
|
}
|
||||||
|
}));
|
||||||
|
}).nThen(function (w) {
|
||||||
|
// Check if you're the owner
|
||||||
|
isFile(ownPath, w(function (e, isFile) {
|
||||||
|
if (e) {
|
||||||
|
w.abort();
|
||||||
|
return void cb(e);
|
||||||
|
}
|
||||||
|
if (!isFile) {
|
||||||
|
WARN('removeOwnedBlob', 'Incorrect owner');
|
||||||
|
w.abort();
|
||||||
|
return void cb('INSUFFICIENT_PERMISSIONS');
|
||||||
|
}
|
||||||
|
}));
|
||||||
|
}).nThen(function (w) {
|
||||||
|
// Delete the blob
|
||||||
|
/*:: if (typeof(blobPath) !== 'string') { throw new Error('should never happen'); } */
|
||||||
|
Fs.unlink(blobPath, w(function (e) {
|
||||||
|
if (e) {
|
||||||
|
w.abort();
|
||||||
|
return void cb(e);
|
||||||
|
}
|
||||||
|
}));
|
||||||
|
}).nThen(function () {
|
||||||
|
// Delete the proof of ownership
|
||||||
|
Fs.unlink(ownPath, function (e) {
|
||||||
|
cb(e);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
var removeOwnedChannel = function (Env, channelId, unsafeKey, cb) {
|
var removeOwnedChannel = function (Env, channelId, unsafeKey, cb) {
|
||||||
if (typeof(channelId) !== 'string' || channelId.length !== 32) {
|
if (typeof(channelId) !== 'string' || !isValidId(channelId)) {
|
||||||
return cb('INVALID_ARGUMENTS');
|
return cb('INVALID_ARGUMENTS');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (testFileId(channelId)) {
|
||||||
|
return void removeOwnedBlob(Env, channelId, unsafeKey, cb);
|
||||||
|
}
|
||||||
|
|
||||||
if (!(Env.msgStore && Env.msgStore.removeChannel && Env.msgStore.getChannelMetadata)) {
|
if (!(Env.msgStore && Env.msgStore.removeChannel && Env.msgStore.getChannelMetadata)) {
|
||||||
return cb("E_NOT_IMPLEMENTED");
|
return cb("E_NOT_IMPLEMENTED");
|
||||||
}
|
}
|
||||||
@@ -876,7 +953,7 @@ var upload = function (Env, publicKey, content, cb) {
|
|||||||
var session = getSession(Env.Sessions, publicKey);
|
var session = getSession(Env.Sessions, publicKey);
|
||||||
|
|
||||||
if (typeof(session.currentUploadSize) !== 'number' ||
|
if (typeof(session.currentUploadSize) !== 'number' ||
|
||||||
typeof(session.currentUploadSize) !== 'number') {
|
typeof(session.pendingUploadSize) !== 'number') {
|
||||||
// improperly initialized... maybe they didn't check before uploading?
|
// improperly initialized... maybe they didn't check before uploading?
|
||||||
// reject it, just in case
|
// reject it, just in case
|
||||||
return cb('NOT_READY');
|
return cb('NOT_READY');
|
||||||
@@ -902,12 +979,12 @@ var upload = function (Env, publicKey, content, cb) {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
var upload_cancel = function (Env, publicKey, cb) {
|
var upload_cancel = function (Env, publicKey, fileSize, cb) {
|
||||||
var paths = Env.paths;
|
var paths = Env.paths;
|
||||||
|
|
||||||
var session = getSession(Env.Sessions, publicKey);
|
var session = getSession(Env.Sessions, publicKey);
|
||||||
delete session.currentUploadSize;
|
session.pendingUploadSize = fileSize;
|
||||||
delete session.pendingUploadSize;
|
session.currentUploadSize = 0;
|
||||||
if (session.blobstage) { session.blobstage.close(); }
|
if (session.blobstage) { session.blobstage.close(); }
|
||||||
|
|
||||||
var path = makeFilePath(paths.staging, publicKey);
|
var path = makeFilePath(paths.staging, publicKey);
|
||||||
@@ -923,17 +1000,7 @@ var upload_cancel = function (Env, publicKey, cb) {
|
|||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
var isFile = function (filePath, cb) {
|
var upload_complete = function (Env, publicKey, id, cb) {
|
||||||
Fs.stat(filePath, function (e, stats) {
|
|
||||||
if (e) {
|
|
||||||
if (e.code === 'ENOENT') { return void cb(void 0, false); }
|
|
||||||
return void cb(e.message);
|
|
||||||
}
|
|
||||||
return void cb(void 0, stats.isFile());
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
var upload_complete = function (Env, publicKey, cb) {
|
|
||||||
var paths = Env.paths;
|
var paths = Env.paths;
|
||||||
var session = getSession(Env.Sessions, publicKey);
|
var session = getSession(Env.Sessions, publicKey);
|
||||||
|
|
||||||
@@ -942,14 +1009,18 @@ var upload_complete = function (Env, publicKey, cb) {
|
|||||||
delete session.blobstage;
|
delete session.blobstage;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!testFileId(id)) {
|
||||||
|
WARN('uploadComplete', "id is invalid");
|
||||||
|
return void cb('EINVAL_ID');
|
||||||
|
}
|
||||||
|
|
||||||
var oldPath = makeFilePath(paths.staging, publicKey);
|
var oldPath = makeFilePath(paths.staging, publicKey);
|
||||||
if (!oldPath) {
|
if (!oldPath) {
|
||||||
WARN('safeMkdir', "oldPath is null");
|
WARN('safeMkdir', "oldPath is null");
|
||||||
return void cb('RENAME_ERR');
|
return void cb('RENAME_ERR');
|
||||||
}
|
}
|
||||||
|
|
||||||
var tryRandomLocation = function (cb) {
|
var tryLocation = function (cb) {
|
||||||
var id = createFileId();
|
|
||||||
var prefix = id.slice(0, 2);
|
var prefix = id.slice(0, 2);
|
||||||
var newPath = makeFilePath(paths.blob, id);
|
var newPath = makeFilePath(paths.blob, id);
|
||||||
if (typeof(newPath) !== 'string') {
|
if (typeof(newPath) !== 'string') {
|
||||||
@@ -968,7 +1039,8 @@ var upload_complete = function (Env, publicKey, cb) {
|
|||||||
return void cb(e);
|
return void cb(e);
|
||||||
}
|
}
|
||||||
if (yes) {
|
if (yes) {
|
||||||
return void tryRandomLocation(cb);
|
WARN('isFile', 'FILE EXISTS!');
|
||||||
|
return void cb('RENAME_ERR');
|
||||||
}
|
}
|
||||||
|
|
||||||
cb(void 0, newPath, id);
|
cb(void 0, newPath, id);
|
||||||
@@ -976,40 +1048,25 @@ var upload_complete = function (Env, publicKey, cb) {
|
|||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
var retries = 3;
|
|
||||||
|
|
||||||
var handleMove = function (e, newPath, id) {
|
var handleMove = function (e, newPath, id) {
|
||||||
if (e || !oldPath || !newPath) {
|
if (e || !oldPath || !newPath) {
|
||||||
if (retries--) {
|
return void cb(e || 'PATH_ERR');
|
||||||
setTimeout(function () {
|
|
||||||
return tryRandomLocation(handleMove);
|
|
||||||
}, 750);
|
|
||||||
} else {
|
|
||||||
cb(e);
|
|
||||||
}
|
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// lol wut handle ur errors
|
// lol wut handle ur errors
|
||||||
Fs.rename(oldPath, newPath, function (e) {
|
Fs.rename(oldPath, newPath, function (e) {
|
||||||
if (e) {
|
if (e) {
|
||||||
WARN('rename', e);
|
WARN('rename', e);
|
||||||
|
|
||||||
if (retries--) {
|
|
||||||
return void setTimeout(function () {
|
|
||||||
tryRandomLocation(handleMove);
|
|
||||||
}, 750);
|
|
||||||
}
|
|
||||||
|
|
||||||
return void cb('RENAME_ERR');
|
return void cb('RENAME_ERR');
|
||||||
}
|
}
|
||||||
cb(void 0, id);
|
cb(void 0, id);
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
tryRandomLocation(handleMove);
|
tryLocation(handleMove);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/*
|
||||||
var owned_upload_complete = function (Env, safeKey, cb) {
|
var owned_upload_complete = function (Env, safeKey, cb) {
|
||||||
var session = getSession(Env.Sessions, safeKey);
|
var session = getSession(Env.Sessions, safeKey);
|
||||||
|
|
||||||
@@ -1069,7 +1126,7 @@ var owned_upload_complete = function (Env, safeKey, cb) {
|
|||||||
var finalPath;
|
var finalPath;
|
||||||
nThen(function (w) {
|
nThen(function (w) {
|
||||||
// make the requisite directory structure using Mkdirp
|
// make the requisite directory structure using Mkdirp
|
||||||
Mkdirp(plannedPath, w(function (e /*, path */) {
|
Mkdirp(plannedPath, w(function (e) {
|
||||||
if (e) { // does not throw error if the directory already existed
|
if (e) { // does not throw error if the directory already existed
|
||||||
w.abort();
|
w.abort();
|
||||||
return void cb(e);
|
return void cb(e);
|
||||||
@@ -1088,8 +1145,8 @@ var owned_upload_complete = function (Env, safeKey, cb) {
|
|||||||
// move the existing file to its new path
|
// move the existing file to its new path
|
||||||
|
|
||||||
// flow is dumb and I need to guard against this which will never happen
|
// flow is dumb and I need to guard against this which will never happen
|
||||||
/*:: if (typeof(oldPath) === 'object') { throw new Error('should never happen'); } */
|
// / *:: if (typeof(oldPath) === 'object') { throw new Error('should never happen'); } * /
|
||||||
Fs.rename(oldPath /* XXX */, finalPath, w(function (e) {
|
Fs.rename(oldPath, finalPath, w(function (e) {
|
||||||
if (e) {
|
if (e) {
|
||||||
w.abort();
|
w.abort();
|
||||||
return void cb(e.code);
|
return void cb(e.code);
|
||||||
@@ -1102,6 +1159,119 @@ var owned_upload_complete = function (Env, safeKey, cb) {
|
|||||||
cb(void 0, blobId);
|
cb(void 0, blobId);
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
*/
|
||||||
|
|
||||||
|
var owned_upload_complete = 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 upload_status = function (Env, publicKey, filesize, cb) {
|
||||||
var paths = Env.paths;
|
var paths = Env.paths;
|
||||||
@@ -1504,20 +1674,23 @@ RPC.create = function (
|
|||||||
});
|
});
|
||||||
case 'UPLOAD_COMPLETE':
|
case 'UPLOAD_COMPLETE':
|
||||||
if (!privileged) { return deny(); }
|
if (!privileged) { return deny(); }
|
||||||
return void upload_complete(Env, safeKey, function (e, hash) {
|
return void upload_complete(Env, safeKey, msg[1], function (e, hash) {
|
||||||
WARN(e, hash);
|
WARN(e, hash);
|
||||||
Respond(e, hash);
|
Respond(e, hash);
|
||||||
});
|
});
|
||||||
case 'OWNED_UPLOAD_COMPLETE':
|
case 'OWNED_UPLOAD_COMPLETE':
|
||||||
if (!privileged) { return deny(); }
|
if (!privileged) { return deny(); }
|
||||||
return void owned_upload_complete(Env, safeKey, function (e, blobId) {
|
return void owned_upload_complete(Env, safeKey, msg[1], function (e, blobId) {
|
||||||
WARN(e, blobId);
|
WARN(e, blobId);
|
||||||
Respond(e, blobId);
|
Respond(e, blobId);
|
||||||
});
|
});
|
||||||
case 'UPLOAD_CANCEL':
|
case 'UPLOAD_CANCEL':
|
||||||
if (!privileged) { return deny(); }
|
if (!privileged) { return deny(); }
|
||||||
return void upload_cancel(Env, safeKey, function (e) {
|
// msg[1] is fileSize
|
||||||
WARN(e);
|
// if we pass it here, we can start an upload right away without calling
|
||||||
|
// UPLOAD_STATUS again
|
||||||
|
return void upload_cancel(Env, safeKey, msg[1], function (e) {
|
||||||
|
WARN(e, 'UPLOAD_CANCEL');
|
||||||
Respond(e);
|
Respond(e);
|
||||||
});
|
});
|
||||||
default:
|
default:
|
||||||
|
|||||||
@@ -331,14 +331,11 @@ define([
|
|||||||
dropArea: $('.CodeMirror'),
|
dropArea: $('.CodeMirror'),
|
||||||
body: $('body'),
|
body: $('body'),
|
||||||
onUploaded: function (ev, data) {
|
onUploaded: function (ev, data) {
|
||||||
//var cursor = editor.getCursor();
|
|
||||||
//var cleanName = data.name.replace(/[\[\]]/g, '');
|
|
||||||
//var text = '';
|
|
||||||
// PASSWORD_FILES
|
|
||||||
var parsed = Hash.parsePadUrl(data.url);
|
var parsed = Hash.parsePadUrl(data.url);
|
||||||
var hexFileName = Util.base64ToHex(parsed.hashData.channel);
|
var secret = Hash.getSecrets('file', parsed.hash, data.password);
|
||||||
var src = '/blob/' + hexFileName.slice(0,2) + '/' + hexFileName;
|
var src = Hash.getBlobPathFromHex(secret.channel);
|
||||||
var mt = '<media-tag src="' + src + '" data-crypto-key="cryptpad:' + parsed.hashData.key + '"></media-tag>';
|
var key = Hash.encodeBase64(secret.keys.cryptKey);
|
||||||
|
var mt = '<media-tag src="' + src + '" data-crypto-key="cryptpad:' + key + '"></media-tag>';
|
||||||
editor.replaceSelection(mt);
|
editor.replaceSelection(mt);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -11,6 +11,7 @@ define([
|
|||||||
var uint8ArrayToHex = Util.uint8ArrayToHex;
|
var uint8ArrayToHex = Util.uint8ArrayToHex;
|
||||||
var hexToBase64 = Util.hexToBase64;
|
var hexToBase64 = Util.hexToBase64;
|
||||||
var base64ToHex = Util.base64ToHex;
|
var base64ToHex = Util.base64ToHex;
|
||||||
|
Hash.encodeBase64 = Nacl.util.encodeBase64;
|
||||||
|
|
||||||
// This implementation must match that on the server
|
// This implementation must match that on the server
|
||||||
// it's used for a checksum
|
// it's used for a checksum
|
||||||
@@ -59,25 +60,13 @@ define([
|
|||||||
return '/1/' + hexToBase64(secret.channel) + '/' +
|
return '/1/' + hexToBase64(secret.channel) + '/' +
|
||||||
Crypto.b64RemoveSlashes(data.fileKeyStr) + '/';
|
Crypto.b64RemoveSlashes(data.fileKeyStr) + '/';
|
||||||
}
|
}
|
||||||
|
if (version === 2) {
|
||||||
|
if (!data.fileKeyStr) { return; }
|
||||||
|
var pass = secret.password ? 'p/' : '';
|
||||||
|
return '/2/' + secret.type + '/' + Crypto.b64RemoveSlashes(data.fileKeyStr) + '/' + pass;
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
// V1
|
|
||||||
/*var getEditHashFromKeys = Hash.getEditHashFromKeys = function (chanKey, keys) {
|
|
||||||
if (typeof keys === 'string') {
|
|
||||||
return chanKey + keys;
|
|
||||||
}
|
|
||||||
if (!keys.editKeyStr) { return; }
|
|
||||||
return '/1/edit/' + hexToBase64(chanKey) + '/'+Crypto.b64RemoveSlashes(keys.editKeyStr)+'/';
|
|
||||||
};
|
|
||||||
var getViewHashFromKeys = Hash.getViewHashFromKeys = function (chanKey, keys) {
|
|
||||||
if (typeof keys === 'string') {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
return '/1/view/' + hexToBase64(chanKey) + '/'+Crypto.b64RemoveSlashes(keys.viewKeyStr)+'/';
|
|
||||||
};
|
|
||||||
var getFileHashFromKeys = Hash.getFileHashFromKeys = function (fileKey, cryptKey) {
|
|
||||||
return '/1/' + hexToBase64(fileKey) + '/' + Crypto.b64RemoveSlashes(cryptKey) + '/';
|
|
||||||
};*/
|
|
||||||
Hash.getUserHrefFromKeys = function (origin, username, pubkey) {
|
Hash.getUserHrefFromKeys = function (origin, username, pubkey) {
|
||||||
return origin + '/user/#/1/' + username + '/' + pubkey.replace(/\//g, '-');
|
return origin + '/user/#/1/' + username + '/' + pubkey.replace(/\//g, '-');
|
||||||
};
|
};
|
||||||
@@ -95,12 +84,22 @@ define([
|
|||||||
};
|
};
|
||||||
|
|
||||||
Hash.createRandomHash = function (type, password) {
|
Hash.createRandomHash = function (type, password) {
|
||||||
var cryptor = Crypto.createEditCryptor2(void 0, void 0, password);
|
var cryptor;
|
||||||
|
if (type === 'file') {
|
||||||
|
cryptor = Crypto.createFileCryptor2(void 0, password);
|
||||||
|
return getFileHashFromKeys({
|
||||||
|
password: Boolean(password),
|
||||||
|
version: 2,
|
||||||
|
type: type,
|
||||||
|
keys: cryptor
|
||||||
|
});
|
||||||
|
}
|
||||||
|
cryptor = Crypto.createEditCryptor2(void 0, void 0, password);
|
||||||
return getEditHashFromKeys({
|
return getEditHashFromKeys({
|
||||||
password: Boolean(password),
|
password: Boolean(password),
|
||||||
version: 2,
|
version: 2,
|
||||||
type: type,
|
type: type,
|
||||||
keys: { editKeyStr: cryptor.editKeyStr }
|
keys: cryptor
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -113,6 +112,7 @@ Version 1
|
|||||||
|
|
||||||
var parseTypeHash = Hash.parseTypeHash = function (type, hash) {
|
var parseTypeHash = Hash.parseTypeHash = function (type, hash) {
|
||||||
if (!hash) { return; }
|
if (!hash) { return; }
|
||||||
|
var options;
|
||||||
var parsed = {};
|
var parsed = {};
|
||||||
var hashArr = fixDuplicateSlashes(hash).split('/');
|
var hashArr = fixDuplicateSlashes(hash).split('/');
|
||||||
if (['media', 'file', 'user', 'invite'].indexOf(type) === -1) {
|
if (['media', 'file', 'user', 'invite'].indexOf(type) === -1) {
|
||||||
@@ -125,7 +125,6 @@ Version 1
|
|||||||
parsed.version = 0;
|
parsed.version = 0;
|
||||||
return parsed;
|
return parsed;
|
||||||
}
|
}
|
||||||
var options;
|
|
||||||
if (hashArr[1] && hashArr[1] === '1') { // Version 1
|
if (hashArr[1] && hashArr[1] === '1') { // Version 1
|
||||||
parsed.version = 1;
|
parsed.version = 1;
|
||||||
parsed.mode = hashArr[2];
|
parsed.mode = hashArr[2];
|
||||||
@@ -175,6 +174,25 @@ Version 1
|
|||||||
parsed.key = hashArr[3].replace(/-/g, '/');
|
parsed.key = hashArr[3].replace(/-/g, '/');
|
||||||
return parsed;
|
return parsed;
|
||||||
}
|
}
|
||||||
|
if (hashArr[1] && hashArr[1] === '2') { // Version 2
|
||||||
|
parsed.version = 2;
|
||||||
|
parsed.app = hashArr[2];
|
||||||
|
parsed.key = hashArr[3];
|
||||||
|
|
||||||
|
options = hashArr.slice(4);
|
||||||
|
parsed.password = options.indexOf('p') !== -1;
|
||||||
|
parsed.present = options.indexOf('present') !== -1;
|
||||||
|
parsed.embed = options.indexOf('embed') !== -1;
|
||||||
|
|
||||||
|
parsed.getHash = function (opts) {
|
||||||
|
var hash = hashArr.slice(0, 4).join('/') + '/';
|
||||||
|
if (parsed.password) { hash += 'p/'; }
|
||||||
|
if (opts.embed) { hash += 'embed/'; }
|
||||||
|
if (opts.present) { hash += 'present/'; }
|
||||||
|
return hash;
|
||||||
|
};
|
||||||
|
return parsed;
|
||||||
|
}
|
||||||
return parsed;
|
return parsed;
|
||||||
}
|
}
|
||||||
if (['user'].indexOf(type) !== -1) {
|
if (['user'].indexOf(type) !== -1) {
|
||||||
@@ -309,11 +327,12 @@ Version 1
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else if (parsed.type === "file") {
|
} else if (parsed.type === "file") {
|
||||||
// version 2 hashes are to be used for encrypted blobs
|
secret.channel = base64ToHex(parsed.channel);
|
||||||
secret.channel = parsed.channel;
|
secret.keys = {
|
||||||
secret.keys = { fileKeyStr: parsed.key };
|
fileKeyStr: parsed.key,
|
||||||
|
cryptKey: Nacl.util.decodeBase64(parsed.key)
|
||||||
|
};
|
||||||
} else if (parsed.type === "user") {
|
} else if (parsed.type === "user") {
|
||||||
// version 2 hashes are to be used for encrypted blobs
|
|
||||||
throw new Error("User hashes can't be opened (yet)");
|
throw new Error("User hashes can't be opened (yet)");
|
||||||
}
|
}
|
||||||
} else if (parsed.version === 2) {
|
} else if (parsed.version === 2) {
|
||||||
@@ -338,7 +357,12 @@ Version 1
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else if (parsed.type === "file") {
|
} else if (parsed.type === "file") {
|
||||||
throw new Error("File hashes should be version 1");
|
secret.keys = Crypto.createFileCryptor2(parsed.key, password);
|
||||||
|
secret.channel = base64ToHex(secret.keys.chanId);
|
||||||
|
secret.key = secret.keys.fileKeyStr;
|
||||||
|
if (secret.channel.length !== 48 || secret.key.length !== 24) {
|
||||||
|
throw new Error("The channel key and/or the encryption key is invalid");
|
||||||
|
}
|
||||||
} else if (parsed.type === "user") {
|
} else if (parsed.type === "user") {
|
||||||
throw new Error("User hashes can't be opened (yet)");
|
throw new Error("User hashes can't be opened (yet)");
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -425,7 +425,8 @@ define([
|
|||||||
cb = cb || function () {};
|
cb = cb || function () {};
|
||||||
opt = opt || {};
|
opt = opt || {};
|
||||||
|
|
||||||
var input = dialog.textInput();
|
var inputBlock = opt.password ? UI.passwordInput() : dialog.textInput();
|
||||||
|
var input = opt.password ? $(inputBlock).find('input')[0] : inputBlock;
|
||||||
input.value = typeof(def) === 'string'? def: '';
|
input.value = typeof(def) === 'string'? def: '';
|
||||||
|
|
||||||
var message;
|
var message;
|
||||||
@@ -441,7 +442,7 @@ define([
|
|||||||
var cancel = dialog.cancelButton(opt.cancel);
|
var cancel = dialog.cancelButton(opt.cancel);
|
||||||
var frame = dialog.frame([
|
var frame = dialog.frame([
|
||||||
message,
|
message,
|
||||||
input,
|
inputBlock,
|
||||||
dialog.nav([ cancel, ok, ]),
|
dialog.nav([ cancel, ok, ]),
|
||||||
]);
|
]);
|
||||||
|
|
||||||
|
|||||||
@@ -250,17 +250,15 @@ define([
|
|||||||
var k = getKey(parsed.type, channel);
|
var k = getKey(parsed.type, channel);
|
||||||
common.setThumbnail(k, b64, cb);
|
common.setThumbnail(k, b64, cb);
|
||||||
};
|
};
|
||||||
Thumb.displayThumbnail = function (common, href, channel, $container, cb) {
|
Thumb.displayThumbnail = function (common, href, channel, password, $container, cb) {
|
||||||
cb = cb || function () {};
|
cb = cb || function () {};
|
||||||
var parsed = Hash.parsePadUrl(href);
|
var parsed = Hash.parsePadUrl(href);
|
||||||
var k = getKey(parsed.type, channel);
|
var k = getKey(parsed.type, channel);
|
||||||
var whenNewThumb = function () {
|
var whenNewThumb = function () {
|
||||||
// PASSWORD_FILES
|
var secret = Hash.getSecrets('file', parsed.hash, password);
|
||||||
var secret = Hash.getSecrets('file', parsed.hash);
|
var hexFileName = channel;
|
||||||
var hexFileName = Util.base64ToHex(secret.channel);
|
|
||||||
var src = Hash.getBlobPathFromHex(hexFileName);
|
var src = Hash.getBlobPathFromHex(hexFileName);
|
||||||
var cryptKey = secret.keys && secret.keys.fileKeyStr;
|
var key = secret.keys && secret.keys.cryptKey;
|
||||||
var key = Nacl.util.decodeBase64(cryptKey);
|
|
||||||
FileCrypto.fetchDecryptedMetadata(src, key, function (e, metadata) {
|
FileCrypto.fetchDecryptedMetadata(src, key, function (e, metadata) {
|
||||||
if (e) {
|
if (e) {
|
||||||
if (e === 'XHR_ERROR') { return; }
|
if (e === 'XHR_ERROR') { return; }
|
||||||
|
|||||||
@@ -162,7 +162,6 @@ define([
|
|||||||
$pwInput.val(data.password).click(function () {
|
$pwInput.val(data.password).click(function () {
|
||||||
$pwInput[0].select();
|
$pwInput[0].select();
|
||||||
});
|
});
|
||||||
$(password).find('.cp-checkmark').css('margin-bottom', '15px');
|
|
||||||
$d.append(password);
|
$d.append(password);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -960,7 +959,7 @@ define([
|
|||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
var setHTML = function (e, html) {
|
var setHTML = UIElements.setHTML = function (e, html) {
|
||||||
e.innerHTML = html;
|
e.innerHTML = html;
|
||||||
return e;
|
return e;
|
||||||
};
|
};
|
||||||
@@ -1172,8 +1171,8 @@ define([
|
|||||||
// No password for avatars
|
// No password for avatars
|
||||||
var secret = Hash.getSecrets('file', parsed.hash);
|
var secret = Hash.getSecrets('file', parsed.hash);
|
||||||
if (secret.keys && secret.channel) {
|
if (secret.keys && secret.channel) {
|
||||||
var cryptKey = secret.keys && secret.keys.fileKeyStr;
|
var hexFileName = secret.channel;
|
||||||
var hexFileName = Util.base64ToHex(secret.channel);
|
var cryptKey = Hash.encodeBase64(secret.keys && secret.keys.cryptKey);
|
||||||
var src = Hash.getBlobPathFromHex(hexFileName);
|
var src = Hash.getBlobPathFromHex(hexFileName);
|
||||||
Common.getFileSize(hexFileName, function (e, data) {
|
Common.getFileSize(hexFileName, function (e, data) {
|
||||||
if (e || !data) {
|
if (e || !data) {
|
||||||
|
|||||||
@@ -204,8 +204,8 @@ define([
|
|||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
common.uploadComplete = function (cb) {
|
common.uploadComplete = function (id, owned, cb) {
|
||||||
postMessage("UPLOAD_COMPLETE", null, function (obj) {
|
postMessage("UPLOAD_COMPLETE", {id: id, owned: owned}, function (obj) {
|
||||||
if (obj && obj.error) { return void cb(obj.error); }
|
if (obj && obj.error) { return void cb(obj.error); }
|
||||||
cb(null, obj);
|
cb(null, obj);
|
||||||
});
|
});
|
||||||
@@ -218,8 +218,8 @@ define([
|
|||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
common.uploadCancel = function (cb) {
|
common.uploadCancel = function (size, cb) {
|
||||||
postMessage("UPLOAD_CANCEL", null, function (obj) {
|
postMessage("UPLOAD_CANCEL", {size: size}, function (obj) {
|
||||||
if (obj && obj.error) { return void cb(obj.error); }
|
if (obj && obj.error) { return void cb(obj.error); }
|
||||||
cb(null, obj);
|
cb(null, obj);
|
||||||
});
|
});
|
||||||
@@ -578,7 +578,6 @@ define([
|
|||||||
}
|
}
|
||||||
var parsed = Hash.parsePadUrl(window.location.href);
|
var parsed = Hash.parsePadUrl(window.location.href);
|
||||||
if (!parsed.type || !parsed.hashData) { return void cb('E_INVALID_HREF'); }
|
if (!parsed.type || !parsed.hashData) { return void cb('E_INVALID_HREF'); }
|
||||||
if (parsed.type === 'file' && typeof(parsed.channel) === 'string') { secret.channel = Util.base64ToHex(secret.channel); }
|
|
||||||
hashes = Hash.getHashes(secret);
|
hashes = Hash.getHashes(secret);
|
||||||
|
|
||||||
if (secret.version === 0) {
|
if (secret.version === 0) {
|
||||||
|
|||||||
@@ -41,11 +41,15 @@ define([
|
|||||||
};
|
};
|
||||||
renderer.image = function (href, title, text) {
|
renderer.image = function (href, title, text) {
|
||||||
if (href.slice(0,6) === '/file/') {
|
if (href.slice(0,6) === '/file/') {
|
||||||
// PASSWORD_FILES
|
// DEPRECATED
|
||||||
|
// Mediatag using markdown syntax should not be used anymore so they don't support
|
||||||
|
// password-protected files
|
||||||
|
console.log('DEPRECATED: mediatag using markdown syntax!');
|
||||||
var parsed = Hash.parsePadUrl(href);
|
var parsed = Hash.parsePadUrl(href);
|
||||||
var hexFileName = Util.base64ToHex(parsed.hashData.channel);
|
var secret = Hash.getSecrets('file', parsed.hash);
|
||||||
var src = '/blob/' + hexFileName.slice(0,2) + '/' + hexFileName;
|
var src = Hash.getBlobPathFromHex(secret.channel);
|
||||||
var mt = '<media-tag src="' + src + '" data-crypto-key="cryptpad:' + parsed.hashData.key + '">';
|
var key = Hash.encodeBase64(secret.keys.cryptKey);
|
||||||
|
var mt = '<media-tag src="' + src + '" data-crypto-key="cryptpad:' + key + '"></media-tag>';
|
||||||
if (mediaMap[src]) {
|
if (mediaMap[src]) {
|
||||||
mt += mediaMap[src];
|
mt += mediaMap[src];
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -115,13 +115,8 @@ define([
|
|||||||
parsed = Hash.parsePadUrl(el.href);
|
parsed = Hash.parsePadUrl(el.href);
|
||||||
if (!el.href) { return; }
|
if (!el.href) { return; }
|
||||||
if (!el.channel) {
|
if (!el.channel) {
|
||||||
if (parsed.hashData && parsed.hashData.type === "file") {
|
|
||||||
// PASSWORD_FILES
|
|
||||||
el.channel = Util.base64ToHex(parsed.hashData.channel);
|
|
||||||
} else {
|
|
||||||
var secret = Hash.getSecrets(parsed.type, parsed.hash, el.password);
|
var secret = Hash.getSecrets(parsed.type, parsed.hash, el.password);
|
||||||
el.channel = secret.channel;
|
el.channel = secret.channel;
|
||||||
}
|
|
||||||
progress(6, Math.round(100*i/padsLength));
|
progress(6, Math.round(100*i/padsLength));
|
||||||
console.log('Adding missing channel in filesData ', el.channel);
|
console.log('Adding missing channel in filesData ', el.channel);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -92,7 +92,7 @@ define([
|
|||||||
var profileChan = profile.edit ? Hash.hrefToHexChannelId('/profile/#' + profile.edit, null) : null;
|
var profileChan = profile.edit ? Hash.hrefToHexChannelId('/profile/#' + profile.edit, null) : null;
|
||||||
if (profileChan) { list.push(profileChan); }
|
if (profileChan) { list.push(profileChan); }
|
||||||
var avatarChan = profile.avatar ? Hash.hrefToHexChannelId(profile.avatar, null) : null;
|
var avatarChan = profile.avatar ? Hash.hrefToHexChannelId(profile.avatar, null) : null;
|
||||||
if (avatarChan) { list.push(Util.base64ToHex(avatarChan)); }
|
if (avatarChan) { list.push(avatarChan); }
|
||||||
}
|
}
|
||||||
|
|
||||||
if (store.proxy.friends) {
|
if (store.proxy.friends) {
|
||||||
@@ -232,7 +232,16 @@ define([
|
|||||||
|
|
||||||
Store.uploadComplete = function (data, cb) {
|
Store.uploadComplete = function (data, cb) {
|
||||||
if (!store.rpc) { return void cb({error: 'RPC_NOT_READY'}); }
|
if (!store.rpc) { return void cb({error: 'RPC_NOT_READY'}); }
|
||||||
store.rpc.uploadComplete(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}); }
|
if (err) { return void cb({error:err}); }
|
||||||
cb(res);
|
cb(res);
|
||||||
});
|
});
|
||||||
@@ -248,7 +257,7 @@ define([
|
|||||||
|
|
||||||
Store.uploadCancel = function (data, cb) {
|
Store.uploadCancel = function (data, cb) {
|
||||||
if (!store.rpc) { return void cb({error: 'RPC_NOT_READY'}); }
|
if (!store.rpc) { return void cb({error: 'RPC_NOT_READY'}); }
|
||||||
store.rpc.uploadCancel(function (err, res) {
|
store.rpc.uploadCancel(data.size, function (err, res) {
|
||||||
if (err) { return void cb({error:err}); }
|
if (err) { return void cb({error:err}); }
|
||||||
cb(res);
|
cb(res);
|
||||||
});
|
});
|
||||||
@@ -678,6 +687,7 @@ define([
|
|||||||
if (Store.channel && Store.channel.wc && channel === Store.channel.wc.id) {
|
if (Store.channel && Store.channel.wc && channel === Store.channel.wc.id) {
|
||||||
owners = Store.channel.data.owners || undefined;
|
owners = Store.channel.data.owners || undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
var expire;
|
var expire;
|
||||||
if (Store.channel && Store.channel.wc && channel === Store.channel.wc.id) {
|
if (Store.channel && Store.channel.wc && channel === Store.channel.wc.id) {
|
||||||
expire = +Store.channel.data.expire || undefined;
|
expire = +Store.channel.data.expire || undefined;
|
||||||
@@ -726,7 +736,11 @@ define([
|
|||||||
contains = true;
|
contains = true;
|
||||||
pad.atime = +new Date();
|
pad.atime = +new Date();
|
||||||
pad.title = title;
|
pad.title = title;
|
||||||
|
if (owners || h.type !== "file") {
|
||||||
|
// OWNED_FILES
|
||||||
|
// Never remove owner for files
|
||||||
pad.owners = owners;
|
pad.owners = owners;
|
||||||
|
}
|
||||||
pad.expire = expire;
|
pad.expire = expire;
|
||||||
|
|
||||||
// If the href is different, it means we have a stronger one
|
// If the href is different, it means we have a stronger one
|
||||||
|
|||||||
@@ -1,8 +1,9 @@
|
|||||||
define([
|
define([
|
||||||
'/file/file-crypto.js',
|
'/file/file-crypto.js',
|
||||||
'/common/common-hash.js',
|
'/common/common-hash.js',
|
||||||
|
'/bower_components/nthen/index.js',
|
||||||
'/bower_components/tweetnacl/nacl-fast.min.js',
|
'/bower_components/tweetnacl/nacl-fast.min.js',
|
||||||
], function (FileCrypto, Hash) {
|
], function (FileCrypto, Hash, nThen) {
|
||||||
var Nacl = window.nacl;
|
var Nacl = window.nacl;
|
||||||
var module = {};
|
var module = {};
|
||||||
|
|
||||||
@@ -10,10 +11,44 @@ define([
|
|||||||
var u8 = file.blob; // This is not a blob but a uint8array
|
var u8 = file.blob; // This is not a blob but a uint8array
|
||||||
var metadata = file.metadata;
|
var metadata = file.metadata;
|
||||||
|
|
||||||
|
var owned = file.isOwned;
|
||||||
|
// XXX
|
||||||
|
owned = true;
|
||||||
|
|
||||||
// if it exists, path contains the new pad location in the drive
|
// if it exists, path contains the new pad location in the drive
|
||||||
var path = file.path;
|
var path = file.path;
|
||||||
|
|
||||||
var key = Nacl.randomBytes(32);
|
var password = file.password;
|
||||||
|
var hash, secret, key, id, href;
|
||||||
|
|
||||||
|
var getNewHash = function () {
|
||||||
|
hash = Hash.createRandomHash('file', password);
|
||||||
|
secret = Hash.getSecrets('file', hash, password);
|
||||||
|
key = secret.keys.cryptKey;
|
||||||
|
id = secret.channel;
|
||||||
|
href = '/file/#' + hash;
|
||||||
|
};
|
||||||
|
|
||||||
|
var getValidHash = function (cb) {
|
||||||
|
getNewHash();
|
||||||
|
common.getFileSize(href, password, function (err, size) {
|
||||||
|
if (err || typeof(size) !== "number") { throw new Error(err || "Invalid size!"); }
|
||||||
|
if (size === 0) { return void cb(); }
|
||||||
|
getValidHash();
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
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);
|
var next = FileCrypto.encrypt(u8, metadata, key);
|
||||||
|
|
||||||
var estimate = FileCrypto.computeEncryptedSize(u8.length, metadata);
|
var estimate = FileCrypto.computeEncryptedSize(u8.length, metadata);
|
||||||
@@ -44,38 +79,28 @@ define([
|
|||||||
}
|
}
|
||||||
|
|
||||||
// if not box then done
|
// if not box then done
|
||||||
common.uploadComplete(function (e, id) {
|
common.uploadComplete(id, owned, function (e) {
|
||||||
if (e) { return void console.error(e); }
|
if (e) { return void console.error(e); }
|
||||||
var uri = ['', 'blob', id.slice(0,2), id].join('/');
|
var uri = ['', 'blob', id.slice(0,2), id].join('/');
|
||||||
console.log("encrypted blob is now available as %s", uri);
|
console.log("encrypted blob is now available as %s", uri);
|
||||||
|
|
||||||
var b64Key = Nacl.util.encodeBase64(key);
|
|
||||||
|
|
||||||
var secret = {
|
|
||||||
version: 1,
|
|
||||||
channel: id,
|
|
||||||
keys: {
|
|
||||||
fileKeyStr: b64Key
|
|
||||||
}
|
|
||||||
};
|
|
||||||
var hash = Hash.getFileHashFromKeys(secret);
|
|
||||||
var href = '/file/#' + hash;
|
|
||||||
|
|
||||||
var title = metadata.name;
|
var title = metadata.name;
|
||||||
|
|
||||||
if (noStore) { return void onComplete(href); }
|
if (noStore) { return void onComplete(href); }
|
||||||
|
|
||||||
// PASSWORD_FILES
|
|
||||||
var data = {
|
var data = {
|
||||||
title: title || "",
|
title: title || "",
|
||||||
href: href,
|
href: href,
|
||||||
path: path,
|
path: path,
|
||||||
|
password: password,
|
||||||
channel: id
|
channel: id
|
||||||
};
|
};
|
||||||
common.setPadTitle(data, function (err) {
|
common.setPadTitle(data, function (err) {
|
||||||
if (err) { return void console.error(err); }
|
if (err) { return void console.error(err); }
|
||||||
onComplete(href);
|
onComplete(href);
|
||||||
common.setPadAttribute('fileType', metadata.type, null, href);
|
common.setPadAttribute('fileType', metadata.type, null, href);
|
||||||
|
common.setPadAttribute('owners', metadata.owners, null, href);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
@@ -90,17 +115,18 @@ define([
|
|||||||
if (pending) {
|
if (pending) {
|
||||||
return void onPending(function () {
|
return void onPending(function () {
|
||||||
// if the user wants to cancel the pending upload to execute that one
|
// if the user wants to cancel the pending upload to execute that one
|
||||||
common.uploadCancel(function (e, res) {
|
common.uploadCancel(estimate, function (e) {
|
||||||
if (e) {
|
if (e) {
|
||||||
return void console.error(e);
|
return void console.error(e);
|
||||||
}
|
}
|
||||||
console.log(res);
|
|
||||||
next(again);
|
next(again);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
next(again);
|
next(again);
|
||||||
});
|
});
|
||||||
|
});
|
||||||
|
|
||||||
};
|
};
|
||||||
return module;
|
return module;
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -589,13 +589,8 @@ define([
|
|||||||
// Fix channel
|
// Fix channel
|
||||||
if (!el.channel) {
|
if (!el.channel) {
|
||||||
try {
|
try {
|
||||||
if (parsed.hashData && parsed.hashData.type === "file") {
|
|
||||||
// PASSWORD_FILES
|
|
||||||
el.channel = Util.base64ToHex(parsed.hashData.channel);
|
|
||||||
} else {
|
|
||||||
var secret = Hash.getSecrets(parsed.type, parsed.hash, el.password);
|
var secret = Hash.getSecrets(parsed.type, parsed.hash, el.password);
|
||||||
el.channel = secret.channel;
|
el.channel = secret.channel;
|
||||||
}
|
|
||||||
console.log('Adding missing channel in filesData ', el.channel);
|
console.log('Adding missing channel in filesData ', el.channel);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.error(e);
|
console.error(e);
|
||||||
|
|||||||
@@ -151,7 +151,7 @@ define([
|
|||||||
};
|
};
|
||||||
|
|
||||||
exp.removeOwnedChannel = function (channel, cb) {
|
exp.removeOwnedChannel = function (channel, cb) {
|
||||||
if (typeof(channel) !== 'string' || channel.length !== 32) {
|
if (typeof(channel) !== 'string' || [32,48].indexOf(channel.length) === -1) {
|
||||||
// can't use this on files because files can't be owned...
|
// can't use this on files because files can't be owned...
|
||||||
return void cb('INVALID_ARGUMENTS');
|
return void cb('INVALID_ARGUMENTS');
|
||||||
}
|
}
|
||||||
@@ -176,8 +176,19 @@ define([
|
|||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
exp.uploadComplete = function (cb) {
|
exp.uploadComplete = function (id, cb) {
|
||||||
rpc.send('UPLOAD_COMPLETE', null, function (e, res) {
|
rpc.send('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.ownedUploadComplete = function (id, cb) {
|
||||||
|
rpc.send('OWNED_UPLOAD_COMPLETE', id, function (e, res) {
|
||||||
if (e) { return void cb(e); }
|
if (e) { return void cb(e); }
|
||||||
var id = res[0];
|
var id = res[0];
|
||||||
if (typeof(id) !== 'string') {
|
if (typeof(id) !== 'string') {
|
||||||
@@ -203,8 +214,8 @@ define([
|
|||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
exp.uploadCancel = function (cb) {
|
exp.uploadCancel = function (size, cb) {
|
||||||
rpc.send('UPLOAD_CANCEL', void 0, function (e) {
|
rpc.send('UPLOAD_CANCEL', size, function (e) {
|
||||||
if (e) { return void cb(e); }
|
if (e) { return void cb(e); }
|
||||||
cb();
|
cb();
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -329,15 +329,11 @@ define([
|
|||||||
dropArea: $('.CodeMirror'),
|
dropArea: $('.CodeMirror'),
|
||||||
body: $('body'),
|
body: $('body'),
|
||||||
onUploaded: function (ev, data) {
|
onUploaded: function (ev, data) {
|
||||||
//var cursor = editor.getCursor();
|
|
||||||
//var cleanName = data.name.replace(/[\[\]]/g, '');
|
|
||||||
//var text = '';
|
|
||||||
// PASSWORD_FILES
|
|
||||||
var parsed = Hash.parsePadUrl(data.url);
|
var parsed = Hash.parsePadUrl(data.url);
|
||||||
var hexFileName = Util.base64ToHex(parsed.hashData.channel);
|
var secret = Hash.getSecrets('file', parsed.hash, data.password);
|
||||||
var src = '/blob/' + hexFileName.slice(0,2) + '/' + hexFileName;
|
var src = Hash.getBlobPathFromHex(secret.channel);
|
||||||
var mt = '<media-tag src="' + src + '" data-crypto-key="cryptpad:' +
|
var key = Hash.encodeBase64(secret.keys.cryptKey);
|
||||||
parsed.hashData.key + '"></media-tag>';
|
var mt = '<media-tag src="' + src + '" data-crypto-key="cryptpad:' + key + '"></media-tag>';
|
||||||
editor.replaceSelection(mt);
|
editor.replaceSelection(mt);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -3,11 +3,13 @@ define([
|
|||||||
'/file/file-crypto.js',
|
'/file/file-crypto.js',
|
||||||
'/common/common-thumbnail.js',
|
'/common/common-thumbnail.js',
|
||||||
'/common/common-interface.js',
|
'/common/common-interface.js',
|
||||||
|
'/common/common-ui-elements.js',
|
||||||
'/common/common-util.js',
|
'/common/common-util.js',
|
||||||
|
'/common/hyperscript.js',
|
||||||
'/customize/messages.js',
|
'/customize/messages.js',
|
||||||
|
|
||||||
'/bower_components/tweetnacl/nacl-fast.min.js',
|
'/bower_components/tweetnacl/nacl-fast.min.js',
|
||||||
], function ($, FileCrypto, Thumb, UI, Util, Messages) {
|
], function ($, FileCrypto, Thumb, UI, UIElements, Util, h, Messages) {
|
||||||
var Nacl = window.nacl;
|
var Nacl = window.nacl;
|
||||||
var module = {};
|
var module = {};
|
||||||
|
|
||||||
@@ -26,6 +28,7 @@ define([
|
|||||||
|
|
||||||
module.create = function (common, config) {
|
module.create = function (common, config) {
|
||||||
var File = {};
|
var File = {};
|
||||||
|
var origin = common.getMetadataMgr().getPrivateData().origin;
|
||||||
|
|
||||||
var queue = File.queue = {
|
var queue = File.queue = {
|
||||||
queue: [],
|
queue: [],
|
||||||
@@ -53,6 +56,7 @@ define([
|
|||||||
|
|
||||||
data.name = file.metadata.name;
|
data.name = file.metadata.name;
|
||||||
data.url = href;
|
data.url = href;
|
||||||
|
data.password = file.password;
|
||||||
if (file.metadata.type.slice(0,6) === 'image/') {
|
if (file.metadata.type.slice(0,6) === 'image/') {
|
||||||
data.mediatag = true;
|
data.mediatag = true;
|
||||||
}
|
}
|
||||||
@@ -212,29 +216,61 @@ define([
|
|||||||
queue.next();
|
queue.next();
|
||||||
};
|
};
|
||||||
|
|
||||||
// Don't show the rename prompt if we don't want to store the file in the drive (avatar)
|
// Get the upload options
|
||||||
var showNamePrompt = !config.noStore;
|
var fileUploadModal = function (file, cb) {
|
||||||
|
|
||||||
var promptName = function (file, cb) {
|
|
||||||
var extIdx = file.name.lastIndexOf('.');
|
var extIdx = file.name.lastIndexOf('.');
|
||||||
var name = extIdx !== -1 ? file.name.slice(0,extIdx) : file.name;
|
var name = extIdx !== -1 ? file.name.slice(0,extIdx) : file.name;
|
||||||
var ext = extIdx !== -1 ? file.name.slice(extIdx) : "";
|
var ext = extIdx !== -1 ? file.name.slice(extIdx) : "";
|
||||||
var msg = Messages._getKey('upload_rename', [
|
|
||||||
Util.fixHTML(file.name),
|
var createHelper = function (href, text) {
|
||||||
Util.fixHTML(ext)
|
var q = h('a.fa.fa-question-circle', {
|
||||||
|
style: 'text-decoration: none !important;',
|
||||||
|
title: text,
|
||||||
|
href: origin + href,
|
||||||
|
target: "_blank",
|
||||||
|
'data-tippy-placement': "right"
|
||||||
|
});
|
||||||
|
return q;
|
||||||
|
};
|
||||||
|
|
||||||
|
// Ask for name, password and owner
|
||||||
|
var content = h('div', [
|
||||||
|
h('h4', Messages.upload_modal_title),
|
||||||
|
UIElements.setHTML(h('label', {for: 'cp-upload-name'}),
|
||||||
|
Messages._getKey('upload_modal_filename', [ext])),
|
||||||
|
h('input#cp-upload-name', {type: 'text', placeholder: name}),
|
||||||
|
h('label', {for: 'cp-upload-password'}, Messages.creation_passwordValue),
|
||||||
|
UI.passwordInput({id: 'cp-upload-password'}),
|
||||||
|
h('span', {
|
||||||
|
style: 'display:flex;align-items:center;justify-content:space-between'
|
||||||
|
}, [
|
||||||
|
UI.createCheckbox('cp-upload-owned', Messages.upload_modal_owner, true),
|
||||||
|
createHelper('/faq.html#keywords-owned', Messages.creation_owned1)
|
||||||
|
]),
|
||||||
]);
|
]);
|
||||||
UI.prompt(msg, name, function (newName) {
|
|
||||||
if (newName === null) {
|
UI.confirm(content, function (yes) {
|
||||||
showNamePrompt = false;
|
if (!yes) { return void cb(); }
|
||||||
return void cb (file.name);
|
|
||||||
}
|
// Get the values
|
||||||
if (!newName || !newName.trim()) { return void cb (file.name); }
|
var newName = $(content).find('#cp-upload-name').val();
|
||||||
|
var password = $(content).find('#cp-upload-password').val() || undefined;
|
||||||
|
var owned = $(content).find('#cp-upload-owned').is(':checked');
|
||||||
|
|
||||||
|
// Add extension to the name if needed
|
||||||
|
if (!newName || !newName.trim()) { newName = file.name; }
|
||||||
var newExtIdx = newName.lastIndexOf('.');
|
var newExtIdx = newName.lastIndexOf('.');
|
||||||
var newExt = newExtIdx !== -1 ? newName.slice(newExtIdx) : "";
|
var newExt = newExtIdx !== -1 ? newName.slice(newExtIdx) : "";
|
||||||
if (newExt !== ext) { newName += ext; }
|
if (newExt !== ext) { newName += ext; }
|
||||||
cb(newName);
|
|
||||||
}, {cancel: Messages.doNotAskAgain}, true);
|
cb({
|
||||||
|
name: newName,
|
||||||
|
password: password,
|
||||||
|
owned: owned
|
||||||
|
});
|
||||||
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
var handleFileState = {
|
var handleFileState = {
|
||||||
queue: [],
|
queue: [],
|
||||||
inProgress: false
|
inProgress: false
|
||||||
@@ -246,7 +282,10 @@ define([
|
|||||||
var thumb;
|
var thumb;
|
||||||
var file_arraybuffer;
|
var file_arraybuffer;
|
||||||
var name = file.name;
|
var name = file.name;
|
||||||
var finish = function () {
|
var password;
|
||||||
|
var owned = true;
|
||||||
|
var finish = function (abort) {
|
||||||
|
if (!abort) {
|
||||||
var metadata = {
|
var metadata = {
|
||||||
name: name,
|
name: name,
|
||||||
type: file.type,
|
type: file.type,
|
||||||
@@ -255,8 +294,11 @@ define([
|
|||||||
queue.push({
|
queue.push({
|
||||||
blob: file_arraybuffer,
|
blob: file_arraybuffer,
|
||||||
metadata: metadata,
|
metadata: metadata,
|
||||||
|
password: password,
|
||||||
|
owned: owned,
|
||||||
dropEvent: e
|
dropEvent: e
|
||||||
});
|
});
|
||||||
|
}
|
||||||
handleFileState.inProgress = false;
|
handleFileState.inProgress = false;
|
||||||
if (handleFileState.queue.length) {
|
if (handleFileState.queue.length) {
|
||||||
var next = handleFileState.queue.shift();
|
var next = handleFileState.queue.shift();
|
||||||
@@ -264,9 +306,16 @@ define([
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
var getName = function () {
|
var getName = function () {
|
||||||
if (!showNamePrompt) { return void finish(); }
|
// If "noStore", it means we don't want to store this file in our drive (avatar)
|
||||||
promptName(file, function (newName) {
|
// In this case, we don't want a password or a filename, and we own the file
|
||||||
name = newName;
|
if (config.noStore) { return void finish(); }
|
||||||
|
|
||||||
|
// Otherwise, ask for password, name and ownership
|
||||||
|
fileUploadModal(file, function (obj) {
|
||||||
|
if (!obj) { return void finish(true); }
|
||||||
|
name = obj.name;
|
||||||
|
password = obj.password;
|
||||||
|
owned = obj.owned;
|
||||||
finish();
|
finish();
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -150,12 +150,12 @@ define([
|
|||||||
todo();
|
todo();
|
||||||
} else {
|
} else {
|
||||||
// Ask for the password and check if the pad exists
|
// Ask for the password and check if the pad exists
|
||||||
// If the pad doesn't exist, it means the password is oncorrect
|
// If the pad doesn't exist, it means the password isn't correct
|
||||||
// or the pad has been deleted
|
// or the pad has been deleted
|
||||||
var correctPassword = waitFor();
|
var correctPassword = waitFor();
|
||||||
sframeChan.on('Q_PAD_PASSWORD_VALUE', function (data, cb) {
|
sframeChan.on('Q_PAD_PASSWORD_VALUE', function (data, cb) {
|
||||||
password = data;
|
password = data;
|
||||||
Cryptpad.isNewChannel(window.location.href, password, function (e, isNew) {
|
var next = function (e, isNew) {
|
||||||
if (Boolean(isNew)) {
|
if (Boolean(isNew)) {
|
||||||
// Ask again in the inner iframe
|
// Ask again in the inner iframe
|
||||||
// We should receive a new Q_PAD_PASSWORD_VALUE
|
// We should receive a new Q_PAD_PASSWORD_VALUE
|
||||||
@@ -165,7 +165,17 @@ define([
|
|||||||
correctPassword();
|
correctPassword();
|
||||||
cb(true);
|
cb(true);
|
||||||
}
|
}
|
||||||
|
};
|
||||||
|
if (parsed.type === "file") {
|
||||||
|
// `isNewChannel` doesn't work for files (not a channel)
|
||||||
|
// `getFileSize` is not adapted to channels because of metadata
|
||||||
|
Cryptpad.getFileSize(window.location.href, password, function (e, size) {
|
||||||
|
next(e, size === 0);
|
||||||
});
|
});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// Not a file, so we can use `isNewChannel`
|
||||||
|
Cryptpad.isNewChannel(window.location.href, password, next);
|
||||||
});
|
});
|
||||||
sframeChan.event("EV_PAD_PASSWORD");
|
sframeChan.event("EV_PAD_PASSWORD");
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -113,16 +113,16 @@ define([
|
|||||||
return '<script src="' + origin + '/common/media-tag-nacl.min.js"></script>';
|
return '<script src="' + origin + '/common/media-tag-nacl.min.js"></script>';
|
||||||
};
|
};
|
||||||
funcs.getMediatagFromHref = function (href) {
|
funcs.getMediatagFromHref = function (href) {
|
||||||
// PASSWORD_FILES
|
// XXX: Should only be used with the current href
|
||||||
var parsed = Hash.parsePadUrl(href);
|
|
||||||
var secret = Hash.getSecrets('file', parsed.hash);
|
|
||||||
var data = ctx.metadataMgr.getPrivateData();
|
var data = ctx.metadataMgr.getPrivateData();
|
||||||
|
var parsed = Hash.parsePadUrl(href);
|
||||||
|
var secret = Hash.getSecrets('file', parsed.hash, data.password);
|
||||||
if (secret.keys && secret.channel) {
|
if (secret.keys && secret.channel) {
|
||||||
var cryptKey = secret.keys && secret.keys.fileKeyStr;
|
var key = Hash.encodeBase64(secret.keys && secret.keys.cryptKey);
|
||||||
var hexFileName = Util.base64ToHex(secret.channel);
|
var hexFileName = secret.channel;
|
||||||
var origin = data.fileHost || data.origin;
|
var origin = data.fileHost || data.origin;
|
||||||
var src = origin + Hash.getBlobPathFromHex(hexFileName);
|
var src = origin + Hash.getBlobPathFromHex(hexFileName);
|
||||||
return '<media-tag src="' + src + '" data-crypto-key="cryptpad:' + cryptKey + '">' +
|
return '<media-tag src="' + src + '" data-crypto-key="cryptpad:' + key + '">' +
|
||||||
'</media-tag>';
|
'</media-tag>';
|
||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
|
|||||||
@@ -590,7 +590,6 @@ define([
|
|||||||
}
|
}
|
||||||
}, cb);
|
}, cb);
|
||||||
}
|
}
|
||||||
console.log(path, newName);
|
|
||||||
if (path.length <= 1) {
|
if (path.length <= 1) {
|
||||||
logError('Renaming `root` is forbidden');
|
logError('Renaming `root` is forbidden');
|
||||||
return;
|
return;
|
||||||
|
|||||||
@@ -1305,7 +1305,7 @@ define([
|
|||||||
$span.attr('title', name);
|
$span.attr('title', name);
|
||||||
|
|
||||||
var type = Messages.type[hrefData.type] || hrefData.type;
|
var type = Messages.type[hrefData.type] || hrefData.type;
|
||||||
common.displayThumbnail(data.href, data.channel, $span, function ($thumb) {
|
common.displayThumbnail(data.href, data.channel, data.password, $span, function ($thumb) {
|
||||||
// Called only if the thumbnail exists
|
// Called only if the thumbnail exists
|
||||||
// Remove the .hide() added by displayThumnail() because it hides the icon in
|
// Remove the .hide() added by displayThumnail() because it hides the icon in
|
||||||
// list mode too
|
// list mode too
|
||||||
|
|||||||
@@ -54,17 +54,14 @@ define([
|
|||||||
|
|
||||||
var uploadMode = false;
|
var uploadMode = false;
|
||||||
var secret;
|
var secret;
|
||||||
var hexFileName;
|
|
||||||
var metadataMgr = common.getMetadataMgr();
|
var metadataMgr = common.getMetadataMgr();
|
||||||
var priv = metadataMgr.getPrivateData();
|
var priv = metadataMgr.getPrivateData();
|
||||||
|
|
||||||
if (!priv.filehash) {
|
if (!priv.filehash) {
|
||||||
uploadMode = true;
|
uploadMode = true;
|
||||||
} else {
|
} else {
|
||||||
// PASSWORD_FILES
|
secret = Hash.getSecrets('file', priv.filehash, priv.password);
|
||||||
secret = Hash.getSecrets('file', priv.filehash);
|
|
||||||
if (!secret.keys) { throw new Error("You need a hash"); }
|
if (!secret.keys) { throw new Error("You need a hash"); }
|
||||||
hexFileName = Util.base64ToHex(secret.channel);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
var Title = common.createTitle({});
|
var Title = common.createTitle({});
|
||||||
@@ -87,9 +84,10 @@ define([
|
|||||||
toolbar.$rightside.html('');
|
toolbar.$rightside.html('');
|
||||||
|
|
||||||
if (!uploadMode) {
|
if (!uploadMode) {
|
||||||
|
var hexFileName = secret.channel;
|
||||||
var src = Hash.getBlobPathFromHex(hexFileName);
|
var src = Hash.getBlobPathFromHex(hexFileName);
|
||||||
var cryptKey = secret.keys && secret.keys.fileKeyStr;
|
var key = secret.keys && secret.keys.cryptKey;
|
||||||
var key = Nacl.util.decodeBase64(cryptKey);
|
var cryptKey = Nacl.util.encodeBase64(key);
|
||||||
|
|
||||||
FileCrypto.fetchDecryptedMetadata(src, key, function (e, metadata) {
|
FileCrypto.fetchDecryptedMetadata(src, key, function (e, metadata) {
|
||||||
if (e) {
|
if (e) {
|
||||||
@@ -98,17 +96,27 @@ define([
|
|||||||
}
|
}
|
||||||
return void console.error(e);
|
return void console.error(e);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Add pad attributes when the file is saved in the drive
|
||||||
|
Title.onTitleChange(function () {
|
||||||
|
var owners = metadata.owners;
|
||||||
|
if (owners) {
|
||||||
|
common.setPadAttribute('owners', owners);
|
||||||
|
}
|
||||||
|
common.setPadAttribute('fileType', metadata.type);
|
||||||
|
});
|
||||||
|
|
||||||
|
// Save to the drive or update the acces time
|
||||||
var title = document.title = metadata.name;
|
var title = document.title = metadata.name;
|
||||||
Title.updateTitle(title || Title.defaultTitle);
|
Title.updateTitle(title || Title.defaultTitle);
|
||||||
|
|
||||||
toolbar.addElement(['pageTitle'], {pageTitle: title});
|
toolbar.addElement(['pageTitle'], {pageTitle: title});
|
||||||
toolbar.$rightside.append(common.createButton('forget', true));
|
toolbar.$rightside.append(common.createButton('forget', true));
|
||||||
|
toolbar.$rightside.append(common.createButton('properties', true));
|
||||||
if (common.isLoggedIn()) {
|
if (common.isLoggedIn()) {
|
||||||
toolbar.$rightside.append(common.createButton('hashtag', true));
|
toolbar.$rightside.append(common.createButton('hashtag', true));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
common.setPadAttribute('fileType', metadata.type);
|
|
||||||
|
|
||||||
var displayFile = function (ev, sizeMb, CB) {
|
var displayFile = function (ev, sizeMb, CB) {
|
||||||
var called_back;
|
var called_back;
|
||||||
var cb = function (e) {
|
var cb = function (e) {
|
||||||
@@ -118,9 +126,7 @@ define([
|
|||||||
};
|
};
|
||||||
|
|
||||||
var $mt = $dlview.find('media-tag');
|
var $mt = $dlview.find('media-tag');
|
||||||
var cryptKey = secret.keys && secret.keys.fileKeyStr;
|
$mt.attr('src', src);
|
||||||
var hexFileName = Util.base64ToHex(secret.channel);
|
|
||||||
$mt.attr('src', '/blob/' + hexFileName.slice(0,2) + '/' + hexFileName);
|
|
||||||
$mt.attr('data-crypto-key', 'cryptpad:'+cryptKey);
|
$mt.attr('data-crypto-key', 'cryptpad:'+cryptKey);
|
||||||
|
|
||||||
var rightsideDisplayed = false;
|
var rightsideDisplayed = false;
|
||||||
@@ -263,7 +269,7 @@ define([
|
|||||||
dropArea: $form,
|
dropArea: $form,
|
||||||
hoverArea: $label,
|
hoverArea: $label,
|
||||||
body: $body,
|
body: $body,
|
||||||
keepTable: true // Don't fadeOut the tbale with the uploaded files
|
keepTable: true // Don't fadeOut the table with the uploaded files
|
||||||
};
|
};
|
||||||
|
|
||||||
var FM = common.createFileManager(fmConfig);
|
var FM = common.createFileManager(fmConfig);
|
||||||
|
|||||||
@@ -40,14 +40,14 @@ define([
|
|||||||
var parsed = Hash.parsePadUrl(data.url);
|
var parsed = Hash.parsePadUrl(data.url);
|
||||||
hideFileDialog();
|
hideFileDialog();
|
||||||
if (parsed.type === 'file') {
|
if (parsed.type === 'file') {
|
||||||
// PASSWORD_FILES
|
var secret = Hash.getSecrets('file', parsed.hash, data.password);
|
||||||
var hexFileName = Util.base64ToHex(parsed.hashData.channel);
|
var src = Hash.getBlobPathFromHex(secret.channel);
|
||||||
var src = '/blob/' + hexFileName.slice(0,2) + '/' + hexFileName;
|
var key = Hash.encodeBase64(secret.keys.cryptKey);
|
||||||
sframeChan.event("EV_FILE_PICKED", {
|
sframeChan.event("EV_FILE_PICKED", {
|
||||||
type: parsed.type,
|
type: parsed.type,
|
||||||
src: src,
|
src: src,
|
||||||
name: data.name,
|
name: data.name,
|
||||||
key: parsed.hashData.key
|
key: key
|
||||||
});
|
});
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -69,8 +69,8 @@ define([
|
|||||||
APP.FM = common.createFileManager(fmConfig);
|
APP.FM = common.createFileManager(fmConfig);
|
||||||
|
|
||||||
// Create file picker
|
// Create file picker
|
||||||
var onSelect = function (url, name) {
|
var onSelect = function (url, name, password) {
|
||||||
onFilePicked({url: url, name: name});
|
onFilePicked({url: url, name: name, password: password});
|
||||||
};
|
};
|
||||||
var data = {
|
var data = {
|
||||||
FM: APP.FM
|
FM: APP.FM
|
||||||
@@ -135,11 +135,13 @@ define([
|
|||||||
$('<span>', {'class': 'cp-filepicker-content-element-name'}).text(name)
|
$('<span>', {'class': 'cp-filepicker-content-element-name'}).text(name)
|
||||||
.appendTo($span);
|
.appendTo($span);
|
||||||
$span.click(function () {
|
$span.click(function () {
|
||||||
if (typeof onSelect === "function") { onSelect(data.href, name); }
|
if (typeof onSelect === "function") {
|
||||||
|
onSelect(data.href, name, data.password);
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
// Add thumbnail if it exists
|
// Add thumbnail if it exists
|
||||||
common.displayThumbnail(data.href, data.channel, $span);
|
common.displayThumbnail(data.href, data.channel, data.password, $span);
|
||||||
});
|
});
|
||||||
$input.focus();
|
$input.focus();
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -552,11 +552,11 @@ define([
|
|||||||
ckeditor: editor,
|
ckeditor: editor,
|
||||||
body: $('body'),
|
body: $('body'),
|
||||||
onUploaded: function (ev, data) {
|
onUploaded: function (ev, data) {
|
||||||
// PASSWORD_FILES
|
|
||||||
var parsed = Hash.parsePadUrl(data.url);
|
var parsed = Hash.parsePadUrl(data.url);
|
||||||
var hexFileName = Util.base64ToHex(parsed.hashData.channel);
|
var secret = Hash.getSecrets('file', parsed.hash, data.password);
|
||||||
var src = '/blob/' + hexFileName.slice(0,2) + '/' + hexFileName;
|
var src = Hash.getBlobPathFromHex(secret.channel);
|
||||||
var mt = '<media-tag contenteditable="false" src="' + src + '" data-crypto-key="cryptpad:' + parsed.hashData.key + '" tabindex="1"></media-tag>';
|
var key = Hash.encodeBase64(secret.keys.cryptKey);
|
||||||
|
var mt = '<media-tag contenteditable="false" src="' + src + '" data-crypto-key="cryptpad:' + key + '"></media-tag>';
|
||||||
// MEDIATAG
|
// MEDIATAG
|
||||||
var element = window.CKEDITOR.dom.element.createFromHtml(mt);
|
var element = window.CKEDITOR.dom.element.createFromHtml(mt);
|
||||||
editor.insertElement(element);
|
editor.insertElement(element);
|
||||||
|
|||||||
@@ -500,11 +500,11 @@ define([
|
|||||||
dropArea: $('.CodeMirror'),
|
dropArea: $('.CodeMirror'),
|
||||||
body: $('body'),
|
body: $('body'),
|
||||||
onUploaded: function (ev, data) {
|
onUploaded: function (ev, data) {
|
||||||
// PASSWORD_FILES
|
|
||||||
var parsed = Hash.parsePadUrl(data.url);
|
var parsed = Hash.parsePadUrl(data.url);
|
||||||
var hexFileName = Util.base64ToHex(parsed.hashData.channel);
|
var secret = Hash.getSecrets('file', parsed.hash, data.password);
|
||||||
var src = '/blob/' + hexFileName.slice(0,2) + '/' + hexFileName;
|
var src = Hash.getBlobPathFromHex(secret.channel);
|
||||||
var mt = '<media-tag src="' + src + '" data-crypto-key="cryptpad:' + parsed.hashData.key + '"></media-tag>';
|
var key = Hash.encodeBase64(secret.keys.cryptKey);
|
||||||
|
var mt = '<media-tag src="' + src + '" data-crypto-key="cryptpad:' + key + '"></media-tag>';
|
||||||
editor.replaceSelection(mt);
|
editor.replaceSelection(mt);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|||||||
Reference in New Issue
Block a user