diff --git a/src/chrome/content/settings.js b/src/chrome/content/settings.js index d354f6b..00e2061 100644 --- a/src/chrome/content/settings.js +++ b/src/chrome/content/settings.js @@ -28,7 +28,7 @@ * string, value: *}, port: {type: string, value: (string|string|string|Number)}, * storageFolder: {type: string, value: (string|string|string|Number)}, username: {type: * string, value: (string|string|string|Number)}, protectUploads: {type: string, value: - * (string|string|string|Number)}}} + * (string|string|string|Number), useRandomPassword: {type: bool, value: bool}}} */ function extraArgs () { let displayName = document.getElementById("displayName").value; @@ -37,6 +37,7 @@ function extraArgs () { let storageFolderValue = document.getElementById("storageFolder").value; let userValue = document.getElementById("username").value; let protectUploadsValue = document.getElementById("protectUploads").value; + let useRandomPasswordValue = document.getElementById("useRandomPassword").checked; return { "displayName": { @@ -62,6 +63,19 @@ function extraArgs () { "protectUploads": { type: "char", value: protectUploadsValue + }, + "useRandomPassword": { + type: "bool", + value: useRandomPasswordValue } }; } + +function onUseRandomPasswordClick () { + let useRandomPassword = document.getElementById("useRandomPassword").checked; + if (useRandomPassword) { + document.getElementById("protectUploads").disabled = "disabled"; + } else { + document.getElementById("protectUploads").disabled = ""; + } +} diff --git a/src/chrome/content/settings.xhtml b/src/chrome/content/settings.xhtml index 9cf0926..f3e2b90 100644 --- a/src/chrome/content/settings.xhtml +++ b/src/chrome/content/settings.xhtml @@ -50,6 +50,9 @@ +
&nextcloudSettings.learnMore; diff --git a/src/chrome/locale/de/settings.dtd b/src/chrome/locale/de/settings.dtd index 155f3f4..0716e2c 100644 --- a/src/chrome/locale/de/settings.dtd +++ b/src/chrome/locale/de/settings.dtd @@ -30,3 +30,4 @@ + diff --git a/src/chrome/locale/en/settings.dtd b/src/chrome/locale/en/settings.dtd index 97f1a83..db641c5 100644 --- a/src/chrome/locale/en/settings.dtd +++ b/src/chrome/locale/en/settings.dtd @@ -30,3 +30,4 @@ + diff --git a/src/chrome/locale/es/settings.dtd b/src/chrome/locale/es/settings.dtd index 37b1fb0..af19c8e 100644 --- a/src/chrome/locale/es/settings.dtd +++ b/src/chrome/locale/es/settings.dtd @@ -30,3 +30,4 @@ + diff --git a/src/chrome/locale/fr/settings.dtd b/src/chrome/locale/fr/settings.dtd index c476944..0557d43 100644 --- a/src/chrome/locale/fr/settings.dtd +++ b/src/chrome/locale/fr/settings.dtd @@ -30,3 +30,4 @@ + diff --git a/src/chrome/locale/nl/settings.dtd b/src/chrome/locale/nl/settings.dtd index c7deb62..dcfae27 100644 --- a/src/chrome/locale/nl/settings.dtd +++ b/src/chrome/locale/nl/settings.dtd @@ -30,3 +30,4 @@ + diff --git a/src/chrome/locale/pl/settings.dtd b/src/chrome/locale/pl/settings.dtd index 2f4ad48..a492e0f 100644 --- a/src/chrome/locale/pl/settings.dtd +++ b/src/chrome/locale/pl/settings.dtd @@ -30,3 +30,4 @@ + diff --git a/src/components/nsNextcloud.js b/src/components/nsNextcloud.js index f20ab9a..9e09d9e 100644 --- a/src/components/nsNextcloud.js +++ b/src/components/nsNextcloud.js @@ -114,6 +114,7 @@ Nextcloud.prototype = { _userName: "", _password: "", _protectUploads: "", + _useRandomPassword: false, _prefBranch: null, _loggedIn: false, _authToken: "", @@ -133,6 +134,7 @@ Nextcloud.prototype = { _uploads: [], _urlsForFiles: {}, _uploadInfo: {}, // upload info keyed on aFiles. + _messageWindow: null, /** * Initialize this instance of Nextcloud, setting the accountKey. @@ -168,6 +170,14 @@ Nextcloud.prototype = { if (this._prefBranch.prefHasUserValue("protectUploads")) { this._protectUploads = this._prefBranch.getCharPref("protectUploads"); } + + if (this._prefBranch.prefHasUserValue("useRandomPassword")) { + this._useRandomPassword = this._prefBranch.getBoolPref("useRandomPassword"); + } + + let windowMediator = Components.classes["@mozilla.org/appshell/window-mediator;1"] + .getService(Components.interfaces.nsIWindowMediator); + this._messageWindow = windowMediator.getMostRecentWindow("msgcompose"); }, /** @@ -237,6 +247,32 @@ Nextcloud.prototype = { * @param aFile the nsILocalFile to retrieve the URL for */ urlForFile: function nsNc_urlForFile (aFile) { + if (this._uploadInfo["downloadPassword"] == null) { + return this._urlsForFiles[aFile.path]; + } + + // Output download password + let document = this._messageWindow.document; + let contentFrame = document.getElementById("content-frame"); + let contentDocument = contentFrame.contentDocument; + this.log.debug("Document body: " + contentDocument.body.innerHTML); + + let cloudAttachmentPasswordList = contentDocument.getElementById("cloudAttachmentPasswordList"); + if (cloudAttachmentPasswordList == null) { + // First cloud attachment + contentDocument.body.insertAdjacentHTML("afterbegin", + '
'); + cloudAttachmentPasswordList = contentDocument.getElementById("cloudAttachmentPasswordList"); + } + cloudAttachmentPasswordList.insertAdjacentHTML("beforeend", + '
* Download password for ' + + aFile.leafName + ":
" + + this._uploadInfo["downloadPassword"] + '
'); + + if (contentFrame.editortype == "textmail") { + // Start a new line before url + return "\n" + this._urlsForFiles[aFile.path]; + } return this._urlsForFiles[aFile.path]; }, @@ -866,8 +902,15 @@ NextcloudFileUploader.prototype = { let formData = "shareType=" + shareType + "&path=" + path; // Request a password for the link if it has been defined during setup time - if (this.nextcloud._protectUploads.length) { - formData += "&password=" + wwwFormUrlEncode(this.nextcloud._protectUploads); + let downloadPassword = this.nextcloud._protectUploads; + // Use random password for each upload + if (this.nextcloud._useRandomPassword) { + downloadPassword = this._generatePassword(16); + } + if (downloadPassword.length) { + this.log.debug("FormData password: " + downloadPassword); + this.nextcloud._uploadInfo["downloadPassword"] = downloadPassword; + formData += "&password=" + wwwFormUrlEncode(downloadPassword); } req.open("POST", @@ -918,6 +961,48 @@ NextcloudFileUploader.prototype = { }.bind(this); this.log.debug("Raw formData: " + formData); req.send(formData); + }, + + /** + * A private function which generates a password. + * + * On Nextcloud, most strict password policy require: + * - Enforce upper and lower case characters + * - Enforce numeric characters + * - Enforce special characters + * + * @param length password length + * @private + */ + _generatePassword: function generatePassword(length) { + const lower = "abcdefghijklmnopqrstuvwxyz"; + const upper = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"; + const numeric = "0123456789" + // Excludes characters that fail to output if continuous: <> + const special = "!\"#$%&'()*+,-./:;=?@[\\]^_`{|}~"; + const seed = lower + upper + numeric + special; + + const lowerRegex = new RegExp("[" + lower + "]"); + const upperRegex = new RegExp("[" + upper + "]"); + const numericRegex = new RegExp("[" + numeric + "]"); + const specialRegex = new RegExp("[" + special + "]"); + + let limit = 100000; + let i = 0; + let password = ""; + while (i < limit) { + i++; + password = Array.from(Array(length)).map(() => seed[Math.floor(Math.random() * seed.length)]).join(""); + + if (!lowerRegex.test(password)) continue; + if (!upperRegex.test(password)) continue; + if (!numericRegex.test(password)) continue; + if (!specialRegex.test(password)) continue; + + break; + } + this.log.debug("Generated password: " + i + ": " + password); + return password; } };