@@ -114,6 +114,7 @@ Nextcloud.prototype = {
114114 _userName : "" ,
115115 _password : "" ,
116116 _protectUploads : "" ,
117+ _useRandomPassword : false ,
117118 _prefBranch : null ,
118119 _loggedIn : false ,
119120 _authToken : "" ,
@@ -133,6 +134,7 @@ Nextcloud.prototype = {
133134 _uploads : [ ] ,
134135 _urlsForFiles : { } ,
135136 _uploadInfo : { } , // upload info keyed on aFiles.
137+ _messageWindow : null ,
136138
137139 /**
138140 * Initialize this instance of Nextcloud, setting the accountKey.
@@ -168,6 +170,14 @@ Nextcloud.prototype = {
168170 if ( this . _prefBranch . prefHasUserValue ( "protectUploads" ) ) {
169171 this . _protectUploads = this . _prefBranch . getCharPref ( "protectUploads" ) ;
170172 }
173+
174+ if ( this . _prefBranch . prefHasUserValue ( "useRandomPassword" ) ) {
175+ this . _useRandomPassword = this . _prefBranch . getBoolPref ( "useRandomPassword" ) ;
176+ }
177+
178+ let windowMediator = Components . classes [ "@mozilla.org/appshell/window-mediator;1" ]
179+ . getService ( Components . interfaces . nsIWindowMediator ) ;
180+ this . _messageWindow = windowMediator . getMostRecentWindow ( "msgcompose" ) ;
171181 } ,
172182
173183 /**
@@ -237,6 +247,32 @@ Nextcloud.prototype = {
237247 * @param aFile the nsILocalFile to retrieve the URL for
238248 */
239249 urlForFile : function nsNc_urlForFile ( aFile ) {
250+ if ( this . _uploadInfo [ "downloadPassword" ] == null ) {
251+ return this . _urlsForFiles [ aFile . path ] ;
252+ }
253+
254+ // Output download password
255+ let document = this . _messageWindow . document ;
256+ let contentFrame = document . getElementById ( "content-frame" ) ;
257+ let contentDocument = contentFrame . contentDocument ;
258+ this . log . debug ( "Document body: " + contentDocument . body . innerHTML ) ;
259+
260+ let cloudAttachmentPasswordList = contentDocument . getElementById ( "cloudAttachmentPasswordList" ) ;
261+ if ( cloudAttachmentPasswordList == null ) {
262+ // First cloud attachment
263+ contentDocument . body . insertAdjacentHTML ( "afterbegin" ,
264+ '<div id="cloudAttachmentPasswordList"></div>' ) ;
265+ cloudAttachmentPasswordList = contentDocument . getElementById ( "cloudAttachmentPasswordList" ) ;
266+ }
267+ cloudAttachmentPasswordList . insertAdjacentHTML ( "beforeend" ,
268+ '<div class="cloudAttachmentPassword">* Download password for '
269+ + aFile . leafName + ":<br>"
270+ + this . _uploadInfo [ "downloadPassword" ] + '</div>' ) ;
271+
272+ if ( contentFrame . editortype == "textmail" ) {
273+ // Start a new line before url
274+ return "\n" + this . _urlsForFiles [ aFile . path ] ;
275+ }
240276 return this . _urlsForFiles [ aFile . path ] ;
241277 } ,
242278
@@ -866,8 +902,15 @@ NextcloudFileUploader.prototype = {
866902
867903 let formData = "shareType=" + shareType + "&path=" + path ;
868904 // Request a password for the link if it has been defined during setup time
869- if ( this . nextcloud . _protectUploads . length ) {
870- formData += "&password=" + wwwFormUrlEncode ( this . nextcloud . _protectUploads ) ;
905+ let downloadPassword = this . nextcloud . _protectUploads ;
906+ // Use random password for each upload
907+ if ( this . nextcloud . _useRandomPassword ) {
908+ downloadPassword = this . _generatePassword ( 16 ) ;
909+ }
910+ if ( downloadPassword . length ) {
911+ this . log . debug ( "FormData password: " + downloadPassword ) ;
912+ this . nextcloud . _uploadInfo [ "downloadPassword" ] = downloadPassword ;
913+ formData += "&password=" + wwwFormUrlEncode ( downloadPassword ) ;
871914 }
872915
873916 req . open ( "POST" ,
@@ -918,6 +961,48 @@ NextcloudFileUploader.prototype = {
918961 } . bind ( this ) ;
919962 this . log . debug ( "Raw formData: " + formData ) ;
920963 req . send ( formData ) ;
964+ } ,
965+
966+ /**
967+ * A private function which generates a password.
968+ *
969+ * On Nextcloud, most strict password policy require:
970+ * - Enforce upper and lower case characters
971+ * - Enforce numeric characters
972+ * - Enforce special characters
973+ *
974+ * @param length password length
975+ * @private
976+ */
977+ _generatePassword : function generatePassword ( length ) {
978+ const lower = "abcdefghijklmnopqrstuvwxyz" ;
979+ const upper = "ABCDEFGHIJKLMNOPQRSTUVWXYZ" ;
980+ const numeric = "0123456789"
981+ // Excludes characters that fail to output if continuous: <>
982+ const special = "!\"#$%&'()*+,-./:;=?@[\\]^_`{|}~" ;
983+ const seed = lower + upper + numeric + special ;
984+
985+ const lowerRegex = new RegExp ( "[" + lower + "]" ) ;
986+ const upperRegex = new RegExp ( "[" + upper + "]" ) ;
987+ const numericRegex = new RegExp ( "[" + numeric + "]" ) ;
988+ const specialRegex = new RegExp ( "[" + special + "]" ) ;
989+
990+ let limit = 100000 ;
991+ let i = 0 ;
992+ let password = "" ;
993+ while ( i < limit ) {
994+ i ++ ;
995+ password = Array . from ( Array ( length ) ) . map ( ( ) => seed [ Math . floor ( Math . random ( ) * seed . length ) ] ) . join ( "" ) ;
996+
997+ if ( ! lowerRegex . test ( password ) ) continue ;
998+ if ( ! upperRegex . test ( password ) ) continue ;
999+ if ( ! numericRegex . test ( password ) ) continue ;
1000+ if ( ! specialRegex . test ( password ) ) continue ;
1001+
1002+ break ;
1003+ }
1004+ this . log . debug ( "Generated password: " + i + ": " + password ) ;
1005+ return password ;
9211006 }
9221007} ;
9231008
0 commit comments