Merge branch 'rohref' into sharedfolder

pull/1/head
yflory 7 years ago
commit 66591a3654

1
.gitignore vendored

@ -15,4 +15,5 @@ npm-debug.log
pins/ pins/
blob/ blob/
blobstage/ blobstage/
block/
privileged.conf privileged.conf

@ -1,3 +1,45 @@
# Echidna release (v2.4.0)
## Goals
For version 2.4.0 we chose to use our time to address difficulties that some users had, and to release some features which have been in development for some time. With the recent release of the _password-protected-pads_ feature, some users desired to be able to change the passwords that they'd already set, or to add a password to a pad retroactively. Other users wanted to recover information that had accidentally been deleted from their pads, but found that the history feature was difficult to use on networks with poor connectivity. Others still found that loading pads in general was too slow.
## Update notes
* We have released new clientside dependencies, so server administrators will need to run `bower update`
* This release also depends on new serverside dependencies, so administrators will also need to run `npm update`
* This release (optionally) takes advantage of Webworker APIs, so administrators may need to update their Content Security Headers to include worker-src (and child-src for safari)
* see cryptpad/docs/example.nginx.conf for more details regarding configuration for nginx as a reverse proxy
* to enable webworkers as an experimental feature, add `AppConfig.disableWorkers = false;` to your `cryptpad/customize/application-config.js`
* Finally, administrators will need to restart their servers after updating, as clients will require new functionality
## What's new
### Features
* CryptPad now takes advantage of some very modern browser APIs
* Shared Workers allow common tasks for all CryptPad editors to be handled by a single background process which runs in the background. This results in better performance savings for anyone using multiple editors at once in different tabs
* Webworkers are used in situations where shared workers are not supported, for most of the same tasks. They are not shared amongst different tabs, but can allow for a more responsive user experience since some heavy commands will be run in the background
* Not all browsers feature complete support for webworkers. For cases where they are not supported at all, or where cryptographic APIs are not supported within their context (https://developer.microsoft.com/en-us/microsoft-edge/platform/issues/7607496/), we fall back to an asynchronous context in the same thread
* Pads with no password can now be updated to include a password, and pads with a password can have their passwords changed
* right-click on the pad in question, and see its properties. The following dialog will present the option to change its password
* changing a pad's password will remove its history
* Accessing a pad's history used to require that clients fetch the entire history of the pad before they could view any of it. History retrieval is now done on an on-demand basis, approximately 100 versions of the pad at a time
* this also features an updated UI with a slider
* We've refactored our whiteboard application to be compatible with our internal framework. As a result, it will be easier to maintain and will have all the same features as the other editors built with the same framework
* We've defined some new server-side features which will allow clients to change their user passwords in a coming release
* We've updated our messaging server implementation
* the aspect of the server which stores and distributes history has been untangled from the aspect which tracks user lists and broadcasts messages
* the server will now store the time when each message was received, so as to be able to allow users to view the time of edits in a later release
### Bug fixes
* When a user tries to register, but enters credentials which have already been used for that CryptPad instance, we prompt them to log in as that user. We discovered that the login had stopped working at some point. This has been fixed
* Server administrators may have seen warnings from npm when attempting to update. We have fixed invalid entries and added missing entries where appropriate such that there are no more warnings
* Static info pages have been restyled to be more responsive, thanks to @CatalinScr
* Support for friend requests in pads with version 0 hashes has been repaired
* We noticed a regression in how default titles for pads were suggested, and have implemented the intended behaviour
# Donkey release (v2.3.0) # Donkey release (v2.3.0)
## Goals ## Goals

@ -42,6 +42,7 @@
out.chainpadError = 'Ein kritischer Fehler hat stattgefunden, bei den Updates deines Dokuments. Dieses Dokument ist schreibgeschützt, damit du sicher machen kannst, dass keine Inhalt verloren geht.<br>'+ out.chainpadError = 'Ein kritischer Fehler hat stattgefunden, bei den Updates deines Dokuments. Dieses Dokument ist schreibgeschützt, damit du sicher machen kannst, dass keine Inhalt verloren geht.<br>'+
'Druck auf <em>Esc</em>, um das Dokument schreibgeschützt zu lesen, oder lade es neu, um das Editierien wiederanzufangen.'; 'Druck auf <em>Esc</em>, um das Dokument schreibgeschützt zu lesen, oder lade es neu, um das Editierien wiederanzufangen.';
out.errorCopy = ' Du kannst noch den Inhalt woanders kopieren, nachdem du <em>Esc</em> drucken.<br>Wenn du die Seite verlässt, verschwindet der Inhalt für immer!'; out.errorCopy = ' Du kannst noch den Inhalt woanders kopieren, nachdem du <em>Esc</em> drucken.<br>Wenn du die Seite verlässt, verschwindet der Inhalt für immer!';
out.errorRedirectToHome = 'Drucke <em>Esc</em>, um zu deinem CryptDrive zu gehen.';
out.loading = "Laden..."; out.loading = "Laden...";
out.error = "Fehler"; out.error = "Fehler";

@ -6,12 +6,11 @@
server { server {
listen 443 ssl http2; listen 443 ssl http2;
server_name your-main-domain.com your-sandbox-domain.com;
server_name cryptpad.fr www.cryptpad.fr beta.cryptpad.fr; ssl_certificate /home/cryptpad/.acme.sh/your-main-domain.com/fullchain.cer;
ssl_certificate_key /home/cryptpad/.acme.sh/your-main-domain.com/your-main-domain.com.key;
ssl_certificate /home/cryptpad/.acme.sh/alpha.cryptpad.fr/fullchain.cer; ssl_trusted_certificate /home/cryptpad/.acme.sh/your-main-domain.com/ca.cer;
ssl_certificate_key /home/cryptpad/.acme.sh/alpha.cryptpad.fr/alpha.cryptpad.fr.key;
ssl_trusted_certificate /home/cryptpad/.acme.sh/alpha.cryptpad.fr/ca.cer;
ssl_dhparam /etc/nginx/dhparam.pem; ssl_dhparam /etc/nginx/dhparam.pem;
ssl_session_timeout 5m; ssl_session_timeout 5m;
@ -27,6 +26,7 @@ server {
root /home/cryptpad/cryptpad; root /home/cryptpad/cryptpad;
index index.html; index index.html;
error_page 404 /customize.dist/404.html;
if ($args ~ ver=) { if ($args ~ ver=) {
set $cacheControl max-age=31536000; set $cacheControl max-age=31536000;
@ -34,25 +34,31 @@ server {
# Will not set any header if it is emptystring # Will not set any header if it is emptystring
add_header Cache-Control $cacheControl; add_header Cache-Control $cacheControl;
set $styleSrc "'unsafe-inline' 'self'"; set $styleSrc "'unsafe-inline' 'self' your-main-domain.com";
set $scriptSrc "'self'"; set $scriptSrc "'self' your-main-domain.com";
set $connectSrc "'self' wss://cryptpad.fr wss://api.cryptpad.fr"; set $connectSrc "'self' https://your-main-domain.com wss://your-main-domain.com https://api.your-main-domain.com wss://your-main-domain.com your-main-domain.com blob: your-main-domain.com";
set $fontSrc "'self'"; set $fontSrc "'self' data: your-main-domain.com";
set $imgSrc "data: * blob:"; set $imgSrc "data: * blob:";
set $frameSrc "'self' beta.cryptpad.fr"; set $frameSrc "'self' your-sandbox-domain.com blob:";
set $mediaSrc "* blob:";
if ($uri = /pad/inner.html) { set $childSrc "https://your-main-domain.com";
set $scriptSrc "'self' 'unsafe-eval' 'unsafe-inline'"; set $workerSrc "https://your-main-domain.com";
set $unsafe 0;
if ($uri = "/pad/inner.html") { set $unsafe 1; }
if ($host != sandbox.cryptpad.info) { set $unsafe 0; }
if ($unsafe) {
set $scriptSrc "'self' 'unsafe-eval' 'unsafe-inline' new2.cryptpad.fr cryptpad.fr";
} }
add_header Content-Security-Policy "default-src 'none'; style-src $styleSrc; script-src $scriptSrc; connect-src $connectSrc; font-src $fontSrc; img-src $imgSrc; frame-src $frameSrc;"; add_header Content-Security-Policy "default-src 'none'; child-src $childSrc; worker-src $workerSrc; media-src $mediaSrc; style-src $styleSrc; script-src $scriptSrc; connect-src $connectSrc; font-src $fontSrc; img-src $imgSrc; frame-src $frameSrc;";
location = /cryptpad_websocket { location ^~ /cryptpad_websocket {
proxy_pass http://localhost:3000; proxy_pass http://localhost:3000;
proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Real-IP $remote_addr;
proxy_set_header Host $host; proxy_set_header Host $host;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
# add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always;
# WebSocket support (nginx 1.4) # WebSocket support (nginx 1.4)
proxy_http_version 1.1; proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade; proxy_set_header Upgrade $http_upgrade;
@ -72,7 +78,7 @@ server {
proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Real-IP $remote_addr;
proxy_set_header Host $host; proxy_set_header Host $host;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
} }
location ^~ /blob/ { location ^~ /blob/ {
add_header Cache-Control max-age=31536000; add_header Cache-Control max-age=31536000;
@ -84,11 +90,9 @@ server {
try_files $uri =404; try_files $uri =404;
} }
## TODO fix in the code so that we don't need this location ~ ^/(register|login|settings|user|pad|drive|poll|slide|code|whiteboard|file|media|profile|contacts|todo|filepicker|debug|kanban)$ {
location ~ ^/(register|login|settings|user|pad|drive|poll|slide|code|whiteboard|file|media)$ {
rewrite ^(.*)$ $1/ redirect; rewrite ^(.*)$ $1/ redirect;
} }
try_files /www/$uri /www/$uri/index.html /customize/$uri; try_files /www/$uri /www/$uri/index.html /customize/$uri;
} }

@ -398,10 +398,16 @@ Version 1
Hash.findWeaker = function (href, channel, recents) { Hash.findWeaker = function (href, channel, recents) {
var parsed = parsePadUrl(href); var parsed = parsePadUrl(href);
if (!parsed.hash) { return false; } if (!parsed.hash) { return false; }
// We can't have a weaker hash if we're already in view mode
if (parsed.hashData && parsed.hashData.mode === 'view') { return; }
var weaker; var weaker;
Object.keys(recents).some(function (id) { Object.keys(recents).some(function (id) {
var pad = recents[id]; var pad = recents[id];
var p = parsePadUrl(pad.href); if (pad.href || !pad.roHref) {
// This pad has an edit link, so it can't be weaker
return;
}
var p = parsePadUrl(pad.roHref);
if (p.type !== parsed.type) { return; } // Not the same type if (p.type !== parsed.type) { return; } // Not the same type
if (p.hash === parsed.hash) { return; } // Same hash, not stronger if (p.hash === parsed.hash) { return; } // Same hash, not stronger
if (channel !== pad.channel) { return; } // Not the same channel if (channel !== pad.channel) { return; } // Not the same channel
@ -430,6 +436,10 @@ Version 1
var stronger; var stronger;
Object.keys(recents).some(function (id) { Object.keys(recents).some(function (id) {
var pad = recents[id]; var pad = recents[id];
if (!pad.href) {
// This pad doesn't have an edit link, so it can't be stronger
return;
}
var p = parsePadUrl(pad.href); var p = parsePadUrl(pad.href);
if (p.type !== parsed.type) { return; } // Not the same type if (p.type !== parsed.type) { return; } // Not the same type
if (p.hash === parsed.hash) { return; } // Same hash, not stronger if (p.hash === parsed.hash) { return; } // Same hash, not stronger

@ -665,7 +665,7 @@ define([
// Update the current state // Update the current state
loading.driveState = data.state; loading.driveState = data.state;
data.progress = data.progress || 100; data.progress = data.progress || 100;
data.msg = Messages['loading_drive_'+data.state] || ''; data.msg = Messages['loading_drive_'+ Math.floor(data.state)] || '';
$progress.html(data.msg); $progress.html(data.msg);
if (data.progress) { if (data.progress) {
$progress.append(h('div.cp-loading-progress-bar', [ $progress.append(h('div.cp-loading-progress-bar', [
@ -761,7 +761,7 @@ define([
UI.getFileIcon = function (data) { UI.getFileIcon = function (data) {
var $icon = UI.getIcon(); var $icon = UI.getIcon();
if (!data) { return $icon; } if (!data) { return $icon; }
var href = data.href; var href = data.href || data.roHref;
var type = data.type; var type = data.type;
if (!href && !type) { return $icon; } if (!href && !type) { return $icon; }

@ -73,26 +73,14 @@ define([
data.password = val; data.password = val;
})); }));
}).nThen(function (waitFor) { }).nThen(function (waitFor) {
var base = common.getMetadataMgr().getPrivateData().origin;
common.getPadAttribute('href', waitFor(function (err, val) { common.getPadAttribute('href', waitFor(function (err, val) {
var base = common.getMetadataMgr().getPrivateData().origin; if (!val) { return; }
var parsed = Hash.parsePadUrl(val);
if (parsed.hashData.mode === "view") {
data.roHref = base + val;
return;
}
// We're not in a read-only pad
data.href = base + val; data.href = base + val;
}));
// Get Read-only href common.getPadAttribute('roHref', waitFor(function (err, val) {
if (parsed.hashData.type !== "pad") { return; } if (!val) { return; }
var i = data.href.indexOf('#') + 1; data.roHref = base + val;
var hBase = data.href.slice(0, i);
var hrefsecret = Hash.getSecrets(parsed.type, parsed.hash, data.password);
if (!hrefsecret.keys) { return; }
var viewHash = Hash.getViewHashFromKeys(hrefsecret);
data.roHref = hBase + viewHash;
})); }));
common.getPadAttribute('channel', waitFor(function (err, val) { common.getPadAttribute('channel', waitFor(function (err, val) {
data.channel = val; data.channel = val;
@ -162,7 +150,7 @@ define([
$d.append(password); $d.append(password);
} }
var parsed = Hash.parsePadUrl(data.href); var parsed = Hash.parsePadUrl(data.href || data.roHref);
if (owned && parsed.hashData.type === 'pad') { if (owned && parsed.hashData.type === 'pad') {
var sframeChan = common.getSframeChannel(); var sframeChan = common.getSframeChannel();
var changePwTitle = Messages.properties_changePassword; var changePwTitle = Messages.properties_changePassword;
@ -186,7 +174,7 @@ define([
UI.confirm(changePwConfirm, function (yes) { UI.confirm(changePwConfirm, function (yes) {
if (!yes) { return; } if (!yes) { return; }
sframeChan.query("Q_PAD_PASSWORD_CHANGE", { sframeChan.query("Q_PAD_PASSWORD_CHANGE", {
href: data.href, href: data.href || data.roHref,
password: $(newPassword).find('input').val() password: $(newPassword).find('input').val()
}, function (err, data) { }, function (err, data) {
if (err || data.error) { if (err || data.error) {
@ -195,11 +183,11 @@ define([
UI.findOKButton().click(); UI.findOKButton().click();
if (data.warning) { if (data.warning) {
return void UI.alert(Messages.properties_passwordWarning, function () { return void UI.alert(Messages.properties_passwordWarning, function () {
common.gotoURL(hasPassword ? undefined : data.href); common.gotoURL(hasPassword ? undefined : (data.href || data.roHref));
}, {force: true}); }, {force: true});
} }
return void UI.alert(Messages.properties_passwordSuccess, function () { return void UI.alert(Messages.properties_passwordSuccess, function () {
common.gotoURL(hasPassword ? undefined : data.href); common.gotoURL(hasPassword ? undefined : (data.href || data.roHref));
}, {force: true}); }, {force: true});
}); });
}); });

@ -613,7 +613,7 @@ define([
if (!parsed.hash) { return void cb({ error: 'EINVAL_HREF' }); } if (!parsed.hash) { return void cb({ error: 'EINVAL_HREF' }); }
var warning = false; var warning = false;
var newHash; var newHash, newRoHref;
var oldChannel; var oldChannel;
if (parsed.hashData.password) { if (parsed.hashData.password) {
newHash = parsed.hash; newHash = parsed.hash;
@ -678,6 +678,11 @@ define([
common.setPadAttribute('channel', secret.channel, waitFor(function (err) { common.setPadAttribute('channel', secret.channel, waitFor(function (err) {
if (err) { warning = true; } if (err) { warning = true; }
}), href); }), href);
var viewHash = Hash.getViewHashFromKeys(secret);
newRoHref = '/' + parsed.type + '/#' + viewHash;
common.setPadAttribute('roHref', newRoHref, waitFor(function (err) {
if (err) { warning = true; }
}), href);
if (parsed.hashData.password) { return; } // same hash if (parsed.hashData.password) { return; } // same hash
common.setPadAttribute('href', newHref, waitFor(function (err) { common.setPadAttribute('href', newHref, waitFor(function (err) {
@ -687,7 +692,8 @@ define([
cb({ cb({
warning: warning, warning: warning,
hash: newHash, hash: newHash,
href: newHref href: newHref,
roHref: newRoHref
}); });
}); });
}; };

@ -115,23 +115,25 @@ define([
var newRecentPads = proxy.drive[newFo.FILES_DATA]; var newRecentPads = proxy.drive[newFo.FILES_DATA];
var oldFiles = oldFo.getFiles([newFo.FILES_DATA]); var oldFiles = oldFo.getFiles([newFo.FILES_DATA]);
var newHrefs = Object.keys(newRecentPads).map(function (id) { var newHrefs = Object.keys(newRecentPads).map(function (id) {
return newRecentPads[id].href; return newRecentPads[id].href || newRecentPads[id].roHref;
}); });
oldFiles.forEach(function (id) { oldFiles.forEach(function (id) {
var href = oldRecentPads[id].href; var href = oldRecentPads[id].href || oldRecentPads[id].roHref;
var isRo = href === oldRecentPads[id].roHref;
// Do not migrate a pad if we already have it, it would create a duplicate in the drive // Do not migrate a pad if we already have it, it would create a duplicate in the drive
if (newHrefs.indexOf(href) !== -1) { return; } if (newHrefs.indexOf(href) !== -1) { return; }
// If we have a stronger version, do not add the current href // If we have a stronger version, do not add the current href
if (Hash.findStronger(href, oldRecentPads[id].channel, newRecentPads)) { return; } // If the current href is read-only, don't check, we won't have a stronger
if (isRo && Hash.findStronger(href, oldRecentPads[id].channel, newRecentPads)) { return; }
// If we have a weaker version, replace the href by the new one // If we have a weaker version, replace the href by the new one
// NOTE: if that weaker version is in the trash, the strong one will be put in unsorted // If the current href is an edit link, don't check, we won't have a weaker
var weaker = Hash.findWeaker(href, oldRecentPads[id].channel, newRecentPads); if (!isRo) {
if (weaker) { var weaker = Hash.findWeaker(href, oldRecentPads[id].channel, newRecentPads);
// Update RECENTPADS if (weaker) {
weaker.href = href; // Update RECENTPADS
// Update the file in the drive weaker.href = href;
newFo.replace(weaker.href, href); return;
return; }
} }
// Here it means we have a new href, so we should add it to the drive at its old location // Here it means we have a new href, so we should add it to the drive at its old location
var paths = oldFo.findFile(id); var paths = oldFo.findFile(id);

@ -123,12 +123,58 @@ define([
})); }));
}); });
}); });
n.nThen(waitFor()); n.nThen(waitFor(function () {
Feedback.send('Migrate-6', true);
userObject.version = version = 6;
}));
}; };
if (version < 6) { if (version < 6) {
addChannelId(); addChannelId();
Feedback.send('Migrate-6', true); }
userObject.version = version = 6; }).nThen(function (waitFor) {
var addRoHref = function () {
var data = userObject.drive.filesData;
var el, parsed;
var n = nThen(function () {});
var padsLength = Object.keys(data).length;
Object.keys(data).forEach(function (k, i) {
n = n.nThen(function (w) {
setTimeout(w(function () {
el = data[k];
if (!el.href || (el.roHref && false)) {
// Already migrated
return void progress(7, Math.round(100*i/padsLength));
}
parsed = Hash.parsePadUrl(el.href);
if (parsed.hashData.type !== "pad") {
// No read-only mode for files
return void progress(7, Math.round(100*i/padsLength));
}
if (parsed.hashData.mode === "view") {
// This is a read-only pad in our drive
el.roHref = el.href;
delete el.href;
console.log('Move href to roHref in filesData ', el.roHref);
} else {
var secret = Hash.getSecrets(parsed.type, parsed.hash, el.password);
var hash = Hash.getViewHashFromKeys(secret);
if (hash) {
// Version 0 won't have a view hash available
el.roHref = '/' + parsed.type + '/#' + hash;
console.log('Adding missing roHref in filesData ', el.href);
}
}
progress(6, Math.round(100*i/padsLength));
}));
});
});
n.nThen(waitFor(function () {
Feedback.send('Migrate-7', true);
userObject.version = version = 7;
}));
};
if (version < 7) {
addRoHref();
} }
/*}).nThen(function (waitFor) { /*}).nThen(function (waitFor) {
// Test progress bar in the loading screen // Test progress bar in the loading screen

@ -426,10 +426,11 @@ define([
cb(JSON.parse(JSON.stringify(metadata))); cb(JSON.parse(JSON.stringify(metadata)));
}; };
var makePad = function (href, title) { var makePad = function (href, roHref, title) {
var now = +new Date(); var now = +new Date();
return { return {
href: href, href: href,
roHref: roHref,
atime: now, atime: now,
ctime: now, ctime: now,
title: title || Hash.getDefaultName(Hash.parsePadUrl(href)), title: title || Hash.getDefaultName(Hash.parsePadUrl(href)),
@ -437,8 +438,15 @@ define([
}; };
Store.addPad = function (clientId, data, cb) { Store.addPad = function (clientId, data, cb) {
if (!data.href) { return void cb({error:'NO_HREF'}); } if (!data.href && !data.roHref) { return void cb({error:'NO_HREF'}); }
var pad = makePad(data.href, data.title); if (!data.roHref) {
var parsed = Hash.parsePadUrl(data.href);
if (parsed.hashData.type === "pad") {
var secret = Hash.getSecrets(parsed.type, parsed.hash, data.password);
data.roHref = '/' + parsed.type + '/#' + Hash.getViewHashFromKeys(secret);
}
}
var pad = makePad(data.href, data.roHref, data.title);
if (data.owners) { pad.owners = data.owners; } if (data.owners) { pad.owners = data.owners; }
if (data.expire) { pad.expire = data.expire; } if (data.expire) { pad.expire = data.expire; }
if (data.password) { pad.password = data.password; } if (data.password) { pad.password = data.password; }
@ -736,9 +744,9 @@ define([
// Edit > Edit (present) > View > View (present) // Edit > Edit (present) > View > View (present)
for (var id in allPads) { for (var id in allPads) {
var pad = allPads[id]; var pad = allPads[id];
if (!pad.href) { continue; } if (!pad.href && !pad.roHref) { continue; }
var p2 = Hash.parsePadUrl(pad.href); var p2 = Hash.parsePadUrl(pad.href || pad.roHref);
var h2 = p2.hashData; var h2 = p2.hashData;
// Different types, proceed to the next one // Different types, proceed to the next one
@ -789,8 +797,14 @@ define([
// Add the pad if it does not exist in our drive // Add the pad if it does not exist in our drive
if (!contains) { if (!contains) {
var roHref;
if (h.mode === "view") {
roHref = href;
href = undefined;
}
Store.addPad(clientId, { Store.addPad(clientId, {
href: href, href: href,
roHref: roHref,
channel: channel, channel: channel,
title: title, title: title,
owners: owners, owners: owners,
@ -827,7 +841,7 @@ define([
}; };
store.userObject.getFiles(where).forEach(function (id) { store.userObject.getFiles(where).forEach(function (id) {
var data = store.userObject.getFileData(id); var data = store.userObject.getFileData(id);
var parsed = Hash.parsePadUrl(data.href); var parsed = Hash.parsePadUrl(data.href || data.roHref);
if ((!types || types.length === 0 || types.indexOf(parsed.type) !== -1) && if ((!types || types.length === 0 || types.indexOf(parsed.type) !== -1) &&
hashes.indexOf(parsed.hash) === -1 && hashes.indexOf(parsed.hash) === -1 &&
!isFiltered(parsed.type, data)) { !isFiltered(parsed.type, data)) {
@ -1421,7 +1435,7 @@ define([
}).nThen(function (waitFor) { }).nThen(function (waitFor) {
Migrate(proxy, waitFor(), function (version, progress) { Migrate(proxy, waitFor(), function (version, progress) {
postMessage(clientId, 'LOADING_DRIVE', { postMessage(clientId, 'LOADING_DRIVE', {
state: 2, state: (2 + (version / 10)),
progress: progress progress: progress
}); });
}); });

@ -50,19 +50,6 @@ define([
var data = exp.getFileData(id); var data = exp.getFileData(id);
cb(null, clone(data[attr])); cb(null, clone(data[attr]));
}; };
var removePadAttribute = exp.removePadAttribute = function (f) {
if (typeof(f) !== 'string') {
console.error("Can't find pad attribute for an undefined pad");
return;
}
Object.keys(files).forEach(function (key) {
var hash = f.indexOf('#') !== -1 ? f.slice(f.indexOf('#') + 1) : null;
if (hash && key.indexOf(hash) === 0) {
exp.debug("Deleting pad attribute in the realtime object");
delete files[key];
}
});
};
exp.pushData = function (data, cb) { exp.pushData = function (data, cb) {
if (typeof cb !== "function") { cb = function () {}; } if (typeof cb !== "function") { cb = function () {}; }
@ -145,12 +132,9 @@ define([
if (!loggedIn && !config.testMode) { if (!loggedIn && !config.testMode) {
allFilesPaths.forEach(function (path) { allFilesPaths.forEach(function (path) {
var el = exp.find(path); var id = path[1];
if (!el) { return; }
var id = exp.getIdFromHref(el.href);
if (!id) { return; } if (!id) { return; }
spliceFileData(id); spliceFileData(id);
removePadAttribute(el.href);
}); });
return; return;
} }
@ -259,7 +243,6 @@ define([
if (!id) { return; } if (!id) { return; }
if (!loggedIn && !config.testMode) { if (!loggedIn && !config.testMode) {
// delete permanently // delete permanently
exp.removePadAttribute(href);
spliceFileData(id); spliceFileData(id);
return; return;
} }
@ -268,14 +251,7 @@ define([
}; };
// REPLACE // REPLACE
exp.replace = function (o, n) { // If all the occurences of an href are in the trash, remove them and add the file in root.
var idO = exp.getIdFromHref(o);
if (!idO || !exp.isFile(idO)) { return; }
var data = exp.getFileData(idO);
if (!data) { return; }
data.href = n;
};
// If all the occurences of an href are in the trash, remvoe them and add the file in root.
// This is use with setPadTitle when we open a stronger version of a deleted pad // This is use with setPadTitle when we open a stronger version of a deleted pad
exp.restoreHref = function (href) { exp.restoreHref = function (href) {
var idO = exp.getIdFromHref(href); var idO = exp.getIdFromHref(href);
@ -563,13 +539,15 @@ define([
continue; continue;
} }
// Clean missing href // Clean missing href
if (!el.href) { if (!el.href && !el.roHref) {
debug("Removing an element in filesData with a missing href.", el); debug("Removing an element in filesData with a missing href.", el);
toClean.push(id); toClean.push(id);
continue; continue;
} }
var parsed = Hash.parsePadUrl(el.href); var parsed = Hash.parsePadUrl(el.href || el.roHref);
var secret;
// Clean invalid hash // Clean invalid hash
if (!parsed.hash) { if (!parsed.hash) {
debug("Removing an element in filesData with a invalid href.", el); debug("Removing an element in filesData with a invalid href.", el);
@ -583,6 +561,23 @@ define([
continue; continue;
} }
// If we have an edit link, check the view link
if (el.href && parsed.hashData.type === "pad") {
if (parsed.hashData.mode === "view") {
el.roHref = el.href;
delete el.href;
} else if (!el.roHref) {
secret = Hash.getSecrets(parsed.type, parsed.hash, el.password);
el.roHref = '/' + parsed.type + '/#' + Hash.getViewHashFromKeys(secret);
} else {
var parsed2 = Hash.parsePadUrl(el.roHref);
if (!parsed2.hash || !parsed2.type) {
secret = Hash.getSecrets(parsed.type, parsed.hash, el.password);
el.roHref = '/' + parsed.type + '/#' + Hash.getViewHashFromKeys(secret);
}
}
}
// Fix href // Fix href
if (/^https*:\/\//.test(el.href)) { el.href = Hash.getRelativeHref(el.href); } if (/^https*:\/\//.test(el.href)) { el.href = Hash.getRelativeHref(el.href); }
// Fix creation time // Fix creation time
@ -592,7 +587,9 @@ define([
// Fix channel // Fix channel
if (!el.channel) { if (!el.channel) {
try { try {
var secret = Hash.getSecrets(parsed.type, parsed.hash, el.password); if (!secret) {
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) {

@ -79,11 +79,7 @@ define([
exp.isReadOnlyFile = function (element) { exp.isReadOnlyFile = function (element) {
if (!isFile(element)) { return false; } if (!isFile(element)) { return false; }
var data = exp.getFileData(element); var data = exp.getFileData(element);
var parsed = Hash.parsePadUrl(data.href); return Boolean(data.roHref && !data.href);
if (!parsed) { return false; }
var pHash = parsed.hashData;
if (!pHash || pHash.type !== "pad") { return; }
return pHash && pHash.mode === 'view';
}; };
var isFolder = exp.isFolder = function (element) { var isFolder = exp.isFolder = function (element) {
@ -140,7 +136,7 @@ define([
var getTitle = exp.getTitle = function (file, type) { var getTitle = exp.getTitle = function (file, type) {
if (workgroup) { debug("No titles in workgroups"); return; } if (workgroup) { debug("No titles in workgroups"); return; }
var data = getFileData(file); var data = getFileData(file);
if (!file || !data || !data.href) { if (!file || !data || !(data.href || data.roHref)) {
error("getTitle called with a non-existing file id: ", file, data); error("getTitle called with a non-existing file id: ", file, data);
return; return;
} }
@ -289,7 +285,8 @@ define([
var getIdFromHref = exp.getIdFromHref = function (href) { var getIdFromHref = exp.getIdFromHref = function (href) {
var result; var result;
getFiles([FILES_DATA]).some(function (id) { getFiles([FILES_DATA]).some(function (id) {
if (files[FILES_DATA][id].href === href) { if (files[FILES_DATA][id].href === href ||
files[FILES_DATA][id].roHref === href) {
result = id; result = id;
return true; return true;
} }

@ -666,10 +666,10 @@ define([
var openFile = function (el, href) { var openFile = function (el, href) {
if (!href) { if (!href) {
var data = filesOp.getFileData(el); var data = filesOp.getFileData(el);
if (!data || !data.href) { if (!data || (!data.href && !data.roHref)) {
return void logError("Missing data for the file", el, data); return void logError("Missing data for the file", el, data);
} }
href = data.href; href = data.href || data.roHref;
} }
window.open(APP.origin + href); window.open(APP.origin + href);
}; };
@ -1271,9 +1271,10 @@ define([
if (!filesOp.isFile(element)) { return; } if (!filesOp.isFile(element)) { return; }
var data = filesOp.getFileData(element); var data = filesOp.getFileData(element);
var href = data.href || data.roHref;
if (!data) { return void logError("No data for the file", element); } if (!data) { return void logError("No data for the file", element); }
var hrefData = Hash.parsePadUrl(data.href); var hrefData = Hash.parsePadUrl(href);
if (hrefData.type) { if (hrefData.type) {
$span.addClass('cp-border-color-'+hrefData.type); $span.addClass('cp-border-color-'+hrefData.type);
} }
@ -1305,7 +1306,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, data.password, $span, function ($thumb) { common.displayThumbnail(href || data.roHref, 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
@ -1847,7 +1848,7 @@ define([
var data = filesOp.getFileData(id); var data = filesOp.getFileData(id);
if (!data) { return ''; } if (!data) { return ''; }
if (prop === 'type') { if (prop === 'type') {
var hrefData = Hash.parsePadUrl(data.href); var hrefData = Hash.parsePadUrl(data.href || data.roHref);
return hrefData.type; return hrefData.type;
} }
if (prop === 'atime' || prop === 'ctime') { if (prop === 'atime' || prop === 'ctime') {
@ -1882,7 +1883,7 @@ define([
}; };
} }
if (prop === 'type') { if (prop === 'type') {
var hrefData = Hash.parsePadUrl(e.href); var hrefData = Hash.parsePadUrl(e.href || e.roHref);
return hrefData.type; return hrefData.type;
} }
if (prop === 'atime' || prop === 'ctime') { if (prop === 'atime' || prop === 'ctime') {
@ -2690,20 +2691,6 @@ define([
return $div.html(); return $div.html();
}; };
var getReadOnlyUrl = APP.getRO = function (id) {
if (!filesOp.isFile(id)) { return; }
var data = filesOp.getFileData(id);
if (!data) { return; }
var parsed = Hash.parsePadUrl(data.href);
if (parsed.hashData.type !== "pad") { return; }
var i = data.href.indexOf('#') + 1;
var base = data.href.slice(0, i);
var hrefsecret = Hash.getSecrets(parsed.type, parsed.hash, data.password);
if (!hrefsecret.keys) { return; }
var viewHash = Hash.getViewHashFromKeys(hrefsecret);
return base + viewHash;
};
// Disable middle click in the context menu to avoid opening /drive/inner.html# in new tabs // Disable middle click in the context menu to avoid opening /drive/inner.html# in new tabs
$(window).click(function (e) { $(window).click(function (e) {
if (!e.target || !$(e.target).parents('.cp-dropdown-content').length) { return; } if (!e.target || !$(e.target).parents('.cp-dropdown-content').length) { return; }
@ -2717,19 +2704,16 @@ define([
if (!filesOp.isFile(el)) { if (!filesOp.isFile(el)) {
return void cb('NOT_FILE'); return void cb('NOT_FILE');
} }
var ro = filesOp.isReadOnlyFile(el); //var ro = filesOp.isReadOnlyFile(el);
var base = APP.origin; var base = APP.origin;
var data = JSON.parse(JSON.stringify(filesOp.getFileData(el))); var data = JSON.parse(JSON.stringify(filesOp.getFileData(el)));
if (!data || !data.href) { return void cb('INVALID_FILE'); } if (!data || !(data.href || data.roHref)) { return void cb('INVALID_FILE'); }
data.href = base + data.href;
var roUrl; if (data.href) {
if (ro) { data.href = base + data.href;
data.roHref = data.href; }
delete data.href; if (data.roHref) {
} else { data.roHref = base + data.roHref;
roUrl = getReadOnlyUrl(el);
if (roUrl) { data.roHref = base + roUrl; }
} }
UIElements.getProperties(common, data, cb); UIElements.getProperties(common, data, cb);
@ -2804,10 +2788,15 @@ define([
else if ($(this).hasClass('cp-app-drive-context-openro')) { else if ($(this).hasClass('cp-app-drive-context-openro')) {
paths.forEach(function (p) { paths.forEach(function (p) {
var el = filesOp.find(p.path); var el = filesOp.find(p.path);
if (filesOp.isPathIn(p.path, [FILES_DATA])) { el = el.href; } var href;
if (!el || filesOp.isFolder(el)) { return; } if (filesOp.isPathIn(p.path, [FILES_DATA])) {
var roUrl = getReadOnlyUrl(el); href = el.roHref;
openFile(null, roUrl); } else {
if (!el || filesOp.isFolder(el)) { return; }
var data = filesOp.getFileData(el);
href = data.roHref;
}
openFile(null, href);
}); });
} }
else if ($(this).hasClass('cp-app-drive-context-newfolder')) { else if ($(this).hasClass('cp-app-drive-context-newfolder')) {
@ -2847,7 +2836,7 @@ define([
el = filesOp.find(paths[0].path); el = filesOp.find(paths[0].path);
var data = filesOp.getFileData(el); var data = filesOp.getFileData(el);
if (!data) { return void console.error("Expected to find a file"); } if (!data) { return void console.error("Expected to find a file"); }
var href = data.href; var href = data.href || data.roHref;
common.updateTags(href); common.updateTags(href);
} }
else if ($(this).hasClass("cp-app-drive-context-empty")) { else if ($(this).hasClass("cp-app-drive-context-empty")) {

@ -237,7 +237,8 @@ define([
&& typeof files.template[0] === "number" && typeof files.template[0] === "number"
&& typeof files.filesData[files.template[0]] === "object" && typeof files.filesData[files.template[0]] === "object"
&& !files.filesData[files.template[0]].filename && !files.filesData[files.template[0]].filename
&& files.filesData[files.template[0]].href === href3 && !files.filesData[files.template[0]].href
&& files.filesData[files.template[0]].roHref === href3
&& typeof fileId2 === "number" && typeof fileId2 === "number"
&& typeof files.filesData[fileId2] === "object" && typeof files.filesData[fileId2] === "object"
&& files.filesData[fileId2].filename === "Trash" && files.filesData[fileId2].filename === "Trash"
@ -392,11 +393,6 @@ define([
console.log("DRIVE operations: rename"); console.log("DRIVE operations: rename");
return cb(); return cb();
} }
fo.replace(href1, href2);
if (fo.getFileData(id1).href !== href2) {
console.log("DRIVE operations: replace");
return cb();
}
cb(true); cb(true);
}, "DRIVE operations"); }, "DRIVE operations");

Loading…
Cancel
Save