├── .gitignore
├── README.md
├── chrome.manifest
├── chrome
├── content
│ ├── icon.png
│ ├── management.js
│ ├── management.xhtml
│ ├── nextcloud.png
│ ├── settings.js
│ └── settings.xhtml
└── locale
│ ├── de
│ ├── management.dtd
│ └── settings.dtd
│ ├── en
│ ├── management.dtd
│ └── settings.dtd
│ ├── es
│ ├── management.dtd
│ └── settings.dtd
│ ├── fr
│ ├── management.dtd
│ └── settings.dtd
│ ├── nl
│ ├── management.dtd
│ └── settings.dtd
│ └── pl
│ ├── management.dtd
│ └── settings.dtd
├── components
└── nsOwncloud.js
└── install.rdf
/.gitignore:
--------------------------------------------------------------------------------
1 | /nbproject/
2 | /build.xml
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | ## Deprecated
2 | This plugin is now deprecated in favor of https://github.com/nextcloud/nextcloud-filelink
3 |
4 | # ownCloud/nextCloud for Filelink
5 | Development: GVJ Web Sites & Consulting - http://www.viguierjust.com
6 |
7 | ## Description
8 | ownCloud/nextCloud for Filelink makes it easy to send large attachments by uploading those attachments to any ownCloud or nextCloud server and inserting a link to the file into the body of your email.
9 |
10 | ownCloud/nextCloud are popular storage services, and this add-on allows Filelink to make use of them.
11 |
12 | ## Installation
13 | 1a) Download the provided .xpi release
14 |
15 | OR
16 |
17 | 1b) Download the provided .xpi.zip release and unzip it
18 |
19 | OR
20 |
21 | 1c) Zip all files from this repository such that the install.rdf and chrome.manifest are located in the root folder of the zip file. Change the file extension from .zip to .xpi.
22 |
23 | 2) Open your Thunderbird, navigate to Tools->Add-Ons, choose "Install Add-On From File..." and select the .xpi file. After installation restart your thunderbird.
24 |
25 | 3) Make sure that you have checked "Allow users to share via link" in **"Sharing"** section in your ownCloud/nextCloud admin page. If you also have **"Enforce password protection"** checked, make sure to fill **"Password for uploaded files"** field in next step
26 |
27 | 4) Navigate to Edit->Preferences->Attachments and add an online storage on the outgoing tab. Select ownCloud from the list and type in your ownCloud url/credentials. If you want to save the attachments not in the root folder of your ownCloud account, then you have to modify the storage path, e.g. use "/mail_attachments/" if you want to save all attachments in the folder named mail_attachments.
28 |
29 | 5) After setting up the account Thunderbird will ask you if you want to upload big mail attachments to ownCloud.
30 |
31 | ## Requirements
32 | * ownCloud/nextCloud: 5.0.13 and newer
33 | * Thunderbird: 13.0 and newer
34 |
--------------------------------------------------------------------------------
/chrome.manifest:
--------------------------------------------------------------------------------
1 | content owncloud chrome/content/
2 | locale owncloud en chrome/locale/en/
3 | locale owncloud fr chrome/locale/fr/
4 | locale owncloud pl chrome/locale/pl/
5 | locale owncloud de chrome/locale/de/
6 | locale owncloud nl chrome/locale/nl/
7 | locale owncloud es chrome/locale/es/
8 | component {ad8c3b77-7dc8-41d1-8985-5be88b254ff3} components/nsOwncloud.js
9 | contract @mozilla.org/mail/owncloud;1 {ad8c3b77-7dc8-41d1-8985-5be88b254ff3}
10 | category cloud-files Owncloud @mozilla.org/mail/owncloud;1
--------------------------------------------------------------------------------
/chrome/content/icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/guillaumev/owncloud_for_filelink/d8c292af7ca328377aa51c9559a73936697b7413/chrome/content/icon.png
--------------------------------------------------------------------------------
/chrome/content/management.js:
--------------------------------------------------------------------------------
1 | /* This Source Code Form is subject to the terms of the Mozilla Public
2 | * License, v. 2.0. If a copy of the MPL was not distributed with this
3 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
4 |
5 | function onLoadProvider(provider) {
6 | let messenger = Components.classes["@mozilla.org/messenger;1"]
7 | .createInstance(Components.interfaces.nsIMessenger);
8 |
9 | let fileSpaceUsed = document.getElementById("file-space-used");
10 | fileSpaceUsed.textContent = messenger.formatFileSize(provider.fileSpaceUsed);
11 | let fileSpaceUsedSwatch = document.getElementById("file-space-used-swatch");
12 | fileSpaceUsedSwatch.style.backgroundColor = pv.Colors.category20.values[0];
13 |
14 | let remainingFileSpace = document.getElementById("remaining-file-space");
15 | remainingFileSpace.textContent = messenger.formatFileSize(
16 | provider.remainingFileSpace);
17 | let remainingFileSpaceSwatch = document.getElementById("remaining-file-space-swatch");
18 | remainingFileSpaceSwatch.style.backgroundColor = pv.Colors.category20.values[1];
19 |
20 | let totalSpace = provider.fileSpaceUsed + provider.remainingFileSpace;
21 | let pieScale = 2 * Math.PI / totalSpace;
22 |
23 | let spaceDiv = document.getElementById("provider-space-visuals");
24 | let vis = new pv.Panel().canvas(spaceDiv)
25 | .width(150)
26 | .height(150);
27 | vis.add(pv.Wedge)
28 | .data([provider.fileSpaceUsed, provider.remainingFileSpace])
29 | .left(75)
30 | .top(75)
31 | .innerRadius(30)
32 | .outerRadius(65)
33 | .angle(function(d) d * pieScale);
34 |
35 | vis.add(pv.Label)
36 | .left(75)
37 | .top(75)
38 | .font("14px Sans-Serif")
39 | .textAlign("center")
40 | .textBaseline("middle")
41 | .text(messenger.formatFileSize(totalSpace));
42 |
43 | vis.render();
44 | }
45 |
--------------------------------------------------------------------------------
/chrome/content/management.xhtml:
--------------------------------------------------------------------------------
1 |
2 |
5 |
6 | %htmlDTD;
8 | %managementDTD;
9 | %owncloudDTD;
10 | ]>
11 |
12 |
13 |
15 |
17 |
20 |
21 |
22 |
27 |
28 |
29 |
30 |
31 | &cloudfileMgmt.usedSpace;
32 |
33 |
34 |
35 |
36 | &cloudfileMgmt.unusedSpace;
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
--------------------------------------------------------------------------------
/chrome/content/nextcloud.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/guillaumev/owncloud_for_filelink/d8c292af7ca328377aa51c9559a73936697b7413/chrome/content/nextcloud.png
--------------------------------------------------------------------------------
/chrome/content/settings.js:
--------------------------------------------------------------------------------
1 | function extraArgs() {
2 | var serverValue = document.getElementById("server").value;
3 | var storageFolderValue = document.getElementById("storageFolder").value;
4 | var userValue = document.getElementById("username").value;
5 | var protectUploadsValue = document.getElementById("protectUploads").value;
6 | return {
7 | "server": {type: "char", value: serverValue},
8 | "storageFolder": {type: "char", value: storageFolderValue},
9 | "username": {type: "char", value: userValue},
10 | "protectUploads": {type: "char", value: protectUploadsValue},
11 | };
12 | }
13 |
--------------------------------------------------------------------------------
/chrome/content/settings.xhtml:
--------------------------------------------------------------------------------
1 |
2 |
5 |
6 | %htmlDTD;
8 | %ocDTD;
9 | ]>
10 |
11 |
12 |
14 |
17 |
18 |
19 |
29 |
32 |
33 |
34 |
--------------------------------------------------------------------------------
/chrome/locale/de/management.dtd:
--------------------------------------------------------------------------------
1 |
5 |
6 |
--------------------------------------------------------------------------------
/chrome/locale/de/settings.dtd:
--------------------------------------------------------------------------------
1 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
--------------------------------------------------------------------------------
/chrome/locale/en/management.dtd:
--------------------------------------------------------------------------------
1 |
5 |
6 |
--------------------------------------------------------------------------------
/chrome/locale/en/settings.dtd:
--------------------------------------------------------------------------------
1 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
--------------------------------------------------------------------------------
/chrome/locale/es/management.dtd:
--------------------------------------------------------------------------------
1 |
5 |
--------------------------------------------------------------------------------
/chrome/locale/es/settings.dtd:
--------------------------------------------------------------------------------
1 |
5 |
6 |
7 |
8 |
9 |
10 |
--------------------------------------------------------------------------------
/chrome/locale/fr/management.dtd:
--------------------------------------------------------------------------------
1 |
5 |
6 |
--------------------------------------------------------------------------------
/chrome/locale/fr/settings.dtd:
--------------------------------------------------------------------------------
1 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
--------------------------------------------------------------------------------
/chrome/locale/nl/management.dtd:
--------------------------------------------------------------------------------
1 |
5 |
6 |
--------------------------------------------------------------------------------
/chrome/locale/nl/settings.dtd:
--------------------------------------------------------------------------------
1 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
--------------------------------------------------------------------------------
/chrome/locale/pl/management.dtd:
--------------------------------------------------------------------------------
1 |
5 |
6 |
--------------------------------------------------------------------------------
/chrome/locale/pl/settings.dtd:
--------------------------------------------------------------------------------
1 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
--------------------------------------------------------------------------------
/components/nsOwncloud.js:
--------------------------------------------------------------------------------
1 | /* This Source Code Form is subject to the terms of the Mozilla Public
2 | * License, v. 2.0. If a copy of the MPL was not distributed with this file,
3 | * You can obtain one at http://mozilla.org/MPL/2.0/. */
4 |
5 | /* This file implements the nsIMsgCloudFileProvider interface.
6 | *
7 | * This component handles the ownCloud implementation of the
8 | * nsIMsgCloudFileProvider interface.
9 | */
10 |
11 | const Cc = Components.classes;
12 | const Ci = Components.interfaces;
13 | const Cu = Components.utils;
14 | const Cr = Components.results;
15 |
16 | Cu.import("resource://gre/modules/XPCOMUtils.jsm");
17 | Cu.import("resource:///modules/oauth.jsm");
18 | Cu.import("resource://gre/modules/Services.jsm");
19 | Cu.import("resource:///modules/gloda/log4moz.js");
20 | Cu.import("resource:///modules/cloudFileAccounts.js");
21 |
22 | const kRestBase = "/ocs/v1.php"
23 | const kAuthPath = kRestBase + "/person/check";
24 | const kShareApp = kRestBase + "/apps/files_sharing/api/v1/shares";
25 | const kWebDavPath = "/remote.php/webdav";
26 |
27 | function wwwFormUrlEncode(aStr) {
28 | return encodeURIComponent(aStr).replace(/!/g, '%21')
29 | .replace(/'/g, '%27')
30 | .replace(/\(/g, '%28')
31 | .replace(/\)/g, '%29')
32 | .replace(/\*/g, '%2A')
33 | .replace(/\@/g, '%40');
34 | }
35 |
36 |
37 | function nsOwncloud() {
38 | this.log = Log4Moz.getConfiguredLogger("Owncloud");
39 | }
40 |
41 | nsOwncloud.prototype = {
42 | /* nsISupports */
43 | QueryInterface: XPCOMUtils.generateQI([Ci.nsIMsgCloudFileProvider]),
44 |
45 | classID: Components.ID("{ad8c3b77-7dc8-41d1-8985-5be88b254ff3}"),
46 |
47 | get type() "Nextcloud/Owncloud",
48 | get displayName() "Nextcloud/Owncloud",
49 | get serviceURL() this._serverUrl,
50 | get iconClass() "chrome://owncloud/content/nextcloud.png",
51 | get accountKey() this._accountKey,
52 | get lastError() this._lastErrorText,
53 | get settingsURL() "chrome://owncloud/content/settings.xhtml",
54 | get managementURL() "chrome://owncloud/content/management.xhtml",
55 |
56 | _accountKey: false,
57 | _serverUrl: "",
58 | _storageFolder: "",
59 | _userName: "",
60 | _password: "",
61 | _protectUploads: "",
62 | _prefBranch: null,
63 | _loggedIn: false,
64 | _authToken: "",
65 | _userInfo: null,
66 | _file : null,
67 | _requestDate: null,
68 | _successCallback: null,
69 | _connection: null,
70 | _request: null,
71 | _uploadingFile : null,
72 | _uploader : null,
73 | _lastErrorStatus : 0,
74 | _lastErrorText : "",
75 | _maxFileSize : -1,
76 | _totalStorage: -1,
77 | _fileSpaceUsed : -1,
78 | _uploads: [],
79 | _urlsForFiles : {},
80 | _uploadInfo : {}, // upload info keyed on aFiles.
81 |
82 | /**
83 | * Initialize this instance of nsOwncloud, setting the accountKey.
84 | *
85 | * @param aAccountKey the account key to initialize this provider with
86 | */
87 | init: function nsOwncloud_init(aAccountKey) {
88 | this._accountKey = aAccountKey;
89 | this._prefBranch = Services.prefs.getBranch("mail.cloud_files.accounts." +
90 | aAccountKey + ".");
91 | this._serverUrl = this._prefBranch.getCharPref("server");
92 | this._userName = this._prefBranch.getCharPref("username");
93 |
94 | if(this._prefBranch.prefHasUserValue("storageFolder")) {
95 | this._storageFolder = this._prefBranch.getCharPref("storageFolder");
96 | } else {
97 | this._storageFolder = "/";
98 | }
99 |
100 | if(this._prefBranch.prefHasUserValue("protectUploads")) {
101 | this._protectUploads = this._prefBranch.getCharPref("protectUploads");
102 | }
103 | },
104 |
105 | /**
106 | * The callback passed to an nsOwncloudFileUploader, which is fired when
107 | * nsOwncloudFileUploader exits.
108 | *
109 | * @param aRequestObserver the request observer originally passed to
110 | * uploadFile for the file associated with the
111 | * nsOwncloudFileUploader
112 | * @param aStatus the result of the upload
113 | */
114 | _uploaderCallback : function nsOwncloud__uploaderCallback(aRequestObserver,
115 | aStatus) {
116 | aRequestObserver.onStopRequest(null, null, aStatus);
117 | this._uploadingFile = null;
118 | this._uploads.shift();
119 | if (this._uploads.length > 0) {
120 | let nextUpload = this._uploads[0];
121 | this.log.info("chaining upload, file = " + nextUpload.file.leafName);
122 | this._uploadingFile = nextUpload.file;
123 | this._uploader = nextUpload;
124 | try {
125 | this.uploadFile(nextUpload.file, nextUpload.callback);
126 | }
127 | catch (ex) {
128 | nextUpload.callback(nextUpload.requestObserver, Cr.NS_ERROR_FAILURE);
129 | }
130 | }
131 | else
132 | this._uploader = null;
133 | },
134 |
135 | /**
136 | * Attempts to upload a file to Owncloud.
137 | *
138 | * @param aFile the nsILocalFile to be uploaded
139 | * @param aCallback an nsIRequestObserver for listening for the starting
140 | * and ending states of the upload.
141 | */
142 | uploadFile: function nsOwncloud_uploadFile(aFile, aCallback) {
143 | if (Services.io.offline)
144 | throw Ci.nsIMsgCloudFileProvider.offlineErr;
145 |
146 | this.log.info("uploading " + aFile.leafName);
147 |
148 | // Some ugliness here - we stash requestObserver here, because we might
149 | // use it again in _getUserInfo.
150 | this.requestObserver = aCallback;
151 |
152 | // if we're uploading a file, queue this request.
153 | if (this._uploadingFile && this._uploadingFile != aFile) {
154 | let uploader = new nsOwncloudFileUploader(this, aFile,
155 | this._uploaderCallback
156 | .bind(this),
157 | aCallback);
158 | this._uploads.push(uploader);
159 | return;
160 | }
161 | this._file = aFile;
162 | this._uploadingFile = aFile;
163 |
164 | let successCallback = this._finishUpload.bind(this, aFile, aCallback);
165 | if (!this._loggedIn)
166 | return this._logonAndGetUserInfo(successCallback, null, true);
167 | this.log.info("getting user info");
168 | if (!this._userInfo)
169 | return this._getUserInfo(successCallback);
170 | successCallback();
171 | },
172 |
173 | /**
174 | * A private function used to ensure that we can actually upload the file
175 | * (we haven't exceeded file size or quota limitations), and then attempts
176 | * to kick-off the upload.
177 | *
178 | * @param aFile the nsILocalFile to upload
179 | * @param aCallback an nsIRequestObserver for monitoring the starting and
180 | * ending states of the upload.
181 | */
182 | _finishUpload: function nsOwncloud__finishUpload(aFile, aCallback) {
183 | let exceedsQuota = Ci.nsIMsgCloudFileProvider.uploadWouldExceedQuota;
184 | if ((this._totalStorage > 0) && (aFile.fileSize > this.remainingFileSpace))
185 | return aCallback.onStopRequest(null, null, exceedsQuota);
186 |
187 | delete this._userInfo; // force us to update userInfo on every upload.
188 |
189 | if (!this._uploader) {
190 | this._uploader = new nsOwncloudFileUploader(this, aFile,
191 | this._uploaderCallback
192 | .bind(this),
193 | aCallback);
194 | this._uploads.unshift(this._uploader);
195 | }
196 |
197 | this._uploadingFile = aFile;
198 | this._uploader.uploadFile();
199 | },
200 |
201 | /**
202 | * Attempts to cancel a file upload.
203 | *
204 | * @param aFile the nsILocalFile to cancel the upload for.
205 | */
206 | cancelFileUpload: function nsOwncloud_cancelFileUpload(aFile) {
207 | if (this._uploadingFile.equals(aFile)) {
208 | this._uploader.cancel();
209 | }
210 | else {
211 | for (let i = 0; i < this._uploads.length; i++)
212 | if (this._uploads[i].file.equals(aFile)) {
213 | this._uploads[i].requestObserver.onStopRequest(
214 | null, null, Ci.nsIMsgCloudFileProvider.uploadCanceled);
215 | this._uploads.splice(i, 1);
216 | return;
217 | }
218 | }
219 | },
220 |
221 | /**
222 | * A private function used to retrieve the profile information for the
223 | * user account associated with the accountKey.
224 | *
225 | * @param successCallback the function called if information retrieval
226 | * is successful
227 | * @param failureCallback the function called if information retrieval fails
228 | */
229 | _getUserInfo: function nsOwncloud__getUserInfo(successCallback,
230 | failureCallback) {
231 | if (!successCallback)
232 | successCallback = function() {
233 | this.requestObserver
234 | .onStopRequest(null, null,
235 | this._loggedIn ? Cr.NS_OK : Ci.nsIMsgCloudFileProvider.authErr);
236 | }.bind(this);
237 |
238 | if (!failureCallback)
239 | failureCallback = function () {
240 | this.requestObserver
241 | .onStopRequest(null, null, Ci.nsIMsgCloudFileProvider.authErr);
242 | }.bind(this);
243 |
244 | let body = '' +
245 | '' +
246 | ' ' +
247 | ' ' +
248 | ' ' +
249 | ' ';
250 |
251 | let req = Cc["@mozilla.org/xmlextras/xmlhttprequest;1"]
252 | .createInstance(Ci.nsIXMLHttpRequest);
253 |
254 | req.open("PROPFIND", this._serverUrl + kWebDavPath, true, this._userName, this._password);
255 | req.onerror = function() {
256 | this.log.info("logon failure");
257 | failureCallback();
258 | }.bind(this);
259 |
260 | req.onload = function() {
261 | if (req.status >= 200 && req.status < 400) {
262 | let qub = req.responseXML.documentElement.getElementsByTagNameNS("DAV:", "quota-used-bytes");
263 | this._fileSpaceUsed = qub && qub.length && Number(qub[0].textContent) || -1;
264 | if (this._fileSpaceUsed < 0) this._fileSpaceUsed = -1;
265 | let qab = req.responseXML.documentElement.getElementsByTagNameNS("DAV:", "quota-available-bytes");
266 | let fsa = qab && qab.length && Number(qab[0].textContent) || -1;
267 |
268 | if (fsa && fsa > -1) { // positive numbers
269 | this._totalStorage = fsa+this._fileSpaceUsed;
270 | } else if (!fsa && fsa !== 0) { // 0 and unequal 0
271 | this._totalStorage = -1;
272 | } else if (!fsa || fsa < 0) { // 0 or negative
273 | this._totalStorage = 0;
274 | }
275 | successCallback();
276 | }
277 | else {
278 | failureCallback();
279 | }
280 | }.bind(this);
281 |
282 | req.send(body);
283 | },
284 |
285 | /**
286 | * A private function that checks that the folder entered in the
287 | * settings screen exists on the server.
288 | *
289 | * @param callback callback function called with true or false as an argument
290 | */
291 | _checkFolderExists: function nsOwncloud_checkFolderExists(callback) {
292 | dump('Checking folder exists');
293 | if (this._storageFolder !== '/') {
294 | let body = '' +
295 | '' +
296 | ' ' +
297 | ' ' +
298 | ' ';
299 |
300 | let req = Cc["@mozilla.org/xmlextras/xmlhttprequest;1"]
301 | .createInstance(Ci.nsIXMLHttpRequest);
302 |
303 | req.open("PROPFIND", this._serverUrl + kWebDavPath + ("/" + this._storageFolder + "/").replace(/\/+/g,'/'), true, this._userName, this._password);
304 | req.onerror = function() {
305 | this.log.info("failure checking folder");
306 | callback(false);
307 | }.bind(this);
308 |
309 | req.onload = function() {
310 | if (req.status === 207) {
311 | dump('Folder does exist');
312 | return callback(true);
313 | }
314 | else {
315 | dump('Folder does not exist');
316 | return callback(false);
317 | }
318 | }.bind(this);
319 |
320 | req.send(body);
321 | }
322 | else {
323 | return callback(true);
324 | }
325 | },
326 |
327 | /**
328 | * A private function that first ensures that the user is logged in, and then
329 | * retrieves the user's profile information.
330 | *
331 | * @param aSuccessCallback the function called on successful information
332 | * retrieval
333 | * @param aFailureCallback the function called on failed information retrieval
334 | * @param aWithUI a boolean for whether or not we should display authorization
335 | * UI if we don't have a valid token anymore, or just fail out.
336 | */
337 | _logonAndGetUserInfo: function nsOwncloud_logonAndGetUserInfo(aSuccessCallback,
338 | aFailureCallback,
339 | aWithUI) {
340 | if (!aFailureCallback)
341 | aFailureCallback = function () {
342 | this.requestObserver
343 | .onStopRequest(null, null, Ci.nsIMsgCloudFileProvider.authErr);
344 | }.bind(this);
345 |
346 | return this.logon(function() {
347 | this._getUserInfo(aSuccessCallback, aFailureCallback);
348 | }.bind(this), aFailureCallback, aWithUI);
349 | },
350 |
351 | /**
352 | * For some nsILocalFile, return the associated sharing URL.
353 | *
354 | * @param aFile the nsILocalFile to retrieve the URL for
355 | */
356 | urlForFile: function nsOwncloud_urlForFile(aFile) {
357 | return this._urlsForFiles[aFile.path];
358 | },
359 |
360 | /**
361 | * Updates the profile information for the account associated with the
362 | * account key.
363 | *
364 | * @param aWithUI a boolean for whether or not we should display authorization
365 | * UI if we don't have a valid token anymore, or just fail out.
366 | * @param aCallback an nsIRequestObserver for observing the starting and
367 | * ending states of the request.
368 | */
369 | refreshUserInfo: function nsOwncloud_refreshUserInfo(aWithUI, aCallback) {
370 | if (Services.io.offline)
371 | throw Ci.nsIMsgCloudFileProvider.offlineErr;
372 | this.requestObserver = aCallback;
373 | aCallback.onStartRequest(null, null);
374 | if (!this._loggedIn)
375 | return this._logonAndGetUserInfo(null, null, aWithUI);
376 | if (!this._userInfo)
377 | return this._getUserInfo();
378 | return this._userInfo;
379 | },
380 |
381 |
382 | /**
383 | * Our ownCloud implementation does not implement the createNewAccount
384 | * function defined in nsIMsgCloudFileProvider.idl.
385 | */
386 | createNewAccount: function nsOwncloud_createNewAccount(aEmailAddress,
387 | aPassword, aFirstName,
388 | aLastName) {
389 | return Cr.NS_ERROR_NOT_IMPLEMENTED;
390 | },
391 |
392 | /**
393 | * If the user already has an account, we can get the user to just login
394 | * to it via OAuth.
395 | *
396 | * This function does not appear to be called from the BigFiles UI, and
397 | * might be excisable.
398 | */
399 | createExistingAccount: function nsOwncloud_createExistingAccount(aRequestObserver) {
400 | // XXX: replace this with a better function
401 | let successCb = function(aResponseText, aRequest) {
402 | let folderExists = function (exists) {
403 | if (exists) {
404 | aRequestObserver.onStopRequest(null, this, Cr.NS_OK);
405 | }
406 | else {
407 | aRequestObserver.onStopRequest(null, this, Ci.nsIMsgCloudFileProvider.authErr);
408 | }
409 | }.bind(this);
410 | this._checkFolderExists(folderExists);
411 | }.bind(this);
412 |
413 | let failureCb = function(aResponseText, aRequest) {
414 | aRequestObserver.onStopRequest(null, this,
415 | Ci.nsIMsgCloudFileProvider.authErr);
416 | }.bind(this);
417 |
418 | this.logon(successCb, failureCb, true);
419 | },
420 |
421 | /**
422 | * If the provider doesn't have an API for creating an account, perhaps
423 | * there's a url we can load in a content tab that will allow the user
424 | * to create an account.
425 | */
426 | get createNewAccountUrl() "",
427 |
428 | /**
429 | * For a particular error, return a URL if ownCloud has a page for handling
430 | * that particular error.
431 | *
432 | * @param aError the error to get the URL for
433 | */
434 | providerUrlForError: function nsOwncloud_providerUrlForError(aError) {
435 | return "";
436 | },
437 |
438 | /**
439 | * If we don't know the limit, this will return -1.
440 | */
441 | get fileUploadSizeLimit() this._maxFileSize,
442 | get remainingFileSpace() this._totalStorage > 0 ? this._totalStorage - this._fileSpaceUsed : -1,
443 | get fileSpaceUsed() this._fileSpaceUsed,
444 |
445 | /**
446 | * Attempt to delete an upload file if we've uploaded it.
447 | *
448 | * @param aFile the file that was originall uploaded
449 | * @param aCallback an nsIRequestObserver for monitoring the starting and
450 | * ending states of the deletion request.
451 | */
452 | deleteFile: function nsOwncloud_deleteFile(aFile, aCallback) {
453 | return Cr.NS_ERROR_NOT_IMPLEMENTED;
454 | },
455 |
456 | /**
457 | * Returns the saved password for this account if one exists, or prompts
458 | * the user for a password. Returns the empty string on failure.
459 | *
460 | * @param aUsername the username associated with the account / password.
461 | * @param aNoPrompt a boolean for whether or not we should suppress
462 | * the password prompt if no password exists. If so,
463 | * returns the empty string if no password exists.
464 | */
465 | getPassword: function(aUsername, aNoPrompt) {
466 | this.log.info("Getting password for user: " + aUsername);
467 |
468 | if (aNoPrompt)
469 | this.log.info("Suppressing password prompt");
470 |
471 | let passwordURI = this._serverUrl;
472 | let logins = Services.logins.findLogins({}, passwordURI, null, passwordURI);
473 | for each (let loginInfo in logins) {
474 | if (loginInfo.username == aUsername)
475 | return loginInfo.password;
476 | }
477 | if (aNoPrompt)
478 | return "";
479 |
480 | // OK, let's prompt for it.
481 | let win = Services.wm.getMostRecentWindow(null);
482 |
483 | let authPrompter = Services.ww.getNewAuthPrompter(win);
484 | let password = { value: "" };
485 | // Use the service name in the prompt text
486 | let serverUrl = this._serverUrl;
487 | let userPos = this._serverUrl.indexOf("//") + 2;
488 | let userNamePart = encodeURIComponent(this._userName) + '@';
489 | serverUrl = this._serverUrl.substr(0, userPos) + userNamePart + this._serverUrl.substr(userPos);
490 | let messengerBundle = Services.strings.createBundle(
491 | "chrome://messenger/locale/messenger.properties");
492 | let promptString = messengerBundle.formatStringFromName("passwordPrompt",
493 | [this._userName,
494 | this.displayName],
495 | 2);
496 |
497 | if (authPrompter.promptPassword(this.displayName, promptString, serverUrl,
498 | authPrompter.SAVE_PASSWORD_PERMANENTLY,
499 | password))
500 | return password.value;
501 |
502 | return "";
503 | },
504 |
505 | /**
506 | * This function is used by our testing framework to override the default
507 | * URL's that nsOwncloud connects to.
508 | */
509 | overrideUrls : function nsOwncloud_overrideUrls(aNumUrls, aUrls) {
510 | this._serverUrl = aUrls[0];
511 | },
512 |
513 | /**
514 | * logon to the owncloud account.
515 | *
516 | * @param successCallback - called if logon is successful
517 | * @param failureCallback - called back on error.
518 | * @param aWithUI if false, logon fails if it would have needed to put up UI.
519 | * This is used for things like displaying account settings,
520 | * where we don't want to pop up the oauth ui.
521 | */
522 | logon: function nsOwncloud_logon(successCallback, failureCallback, aWithUI) {
523 | this.log.info("Logging in, aWithUI = " + aWithUI);
524 | if (this._password == undefined || !this._password)
525 | this._password = this.getPassword(this._userName, !aWithUI);
526 | this.log.info("Sending login information...");
527 |
528 | let loginData = "login=" + wwwFormUrlEncode(this._userName) + "&password=" +
529 | wwwFormUrlEncode(this._password);
530 | let args = "?format=json";
531 | let req = Cc["@mozilla.org/xmlextras/xmlhttprequest;1"]
532 | .createInstance(Ci.nsIXMLHttpRequest);
533 |
534 | req.open("POST", this._serverUrl + kAuthPath + args, true);
535 | req.setRequestHeader('Content-Type', "application/x-www-form-urlencoded");
536 | req.setRequestHeader('OCS-APIRequest', 'true');
537 |
538 | req.onerror = function() {
539 | this.log.info("logon failure");
540 | failureCallback();
541 | }.bind(this);
542 |
543 | req.onload = function() {
544 | if (req.status >= 200 && req.status < 400) {
545 | try {
546 | this.log.info("auth token response = " + req.responseText);
547 | let docResponse = JSON.parse(req.responseText);
548 | //this.log.info("login response parsed = " + docResponse);
549 | let statuscode = docResponse.ocs.meta.statuscode;
550 | this.log.info("statuscode = " + statuscode);
551 | if (statuscode == 100) {
552 | this._loggedIn = true;
553 | successCallback();
554 | }
555 | else {
556 | this._loggedIn = false;
557 | this._lastErrorText = docResponse.ocs.meta.message;
558 | this._lastErrorStatus = docResponse.ocs.meta.statuscode;
559 | failureCallback();
560 | }
561 | } catch(e) {
562 | this.log.error(e);
563 | this._loggedIn = false;
564 | failureCallback();
565 | }
566 | }
567 | else {
568 | failureCallback();
569 | }
570 | }.bind(this);
571 |
572 | req.send(loginData);
573 | this.log.info("Login information sent!");
574 | },
575 | };
576 |
577 | function nsOwncloudFileUploader(aOwncloud, aFile, aCallback, aRequestObserver) {
578 | this.owncloud = aOwncloud;
579 | this.log = this.owncloud.log;
580 | this.log.info("new nsOwncloudFileUploader file = " + aFile.leafName);
581 | this.file = aFile;
582 | this.callback = aCallback;
583 | this.requestObserver = aRequestObserver;
584 | }
585 |
586 | nsOwncloudFileUploader.prototype = {
587 | owncloud : null,
588 | file : null,
589 | callback : null,
590 | request : null,
591 | _fileUploadTS: { }, // timestamps to prepend, avoiding filename conflict
592 |
593 | /**
594 | * Kicks off the upload request for the file associated with this Uploader.
595 | */
596 | uploadFile: function nsOFU_uploadFile() {
597 | this.requestObserver.onStartRequest(null, null);
598 | this._fileUploadTS[this.file.path] = new Date().getTime();
599 | this.log.info("ready to upload file " + wwwFormUrlEncode(this.file.leafName) + " to folder " + this.owncloud._storageFolder);
600 | let url = this.owncloud._serverUrl + kWebDavPath + ("/" + this.owncloud._storageFolder + "/").replace(/\/+/g,'/')
601 | + this._fileUploadTS[this.file.path] + "_" + this.file.leafName;
602 | let fileContents = "";
603 | let fstream = Cc["@mozilla.org/network/file-input-stream;1"]
604 | .createInstance(Ci.nsIFileInputStream);
605 | fstream.init(this.file, -1, 0, 0);
606 |
607 | let bufStream = Cc["@mozilla.org/network/buffered-input-stream;1"].
608 | createInstance(Ci.nsIBufferedInputStream);
609 | bufStream.init(fstream, this.file.fileSize);
610 | bufStream = bufStream.QueryInterface(Ci.nsIInputStream);
611 | let contentLength = fstream.available();
612 |
613 | let req = Cc["@mozilla.org/xmlextras/xmlhttprequest;1"]
614 | .createInstance(Ci.nsIXMLHttpRequest);
615 |
616 | req.open("PUT", url, true, this._userName, this._password);
617 | req.onerror = function() {
618 | this.log.info("Could not upload file");
619 | if (this.callback) {
620 | this.callback(this.requestObserver,
621 | Ci.nsIMsgCloudFileProvider.uploadErr);
622 | }
623 | }.bind(this);
624 |
625 | req.onload = function() {
626 | if (req.status >= 200 && req.status < 400) {
627 | this._getShareUrl(this.file, this.callback);
628 | }
629 | else {
630 | if (this.callback)
631 | this.callback(this.requestObserver,
632 | Ci.nsIMsgCloudFileProvider.uploadErr);
633 | }
634 | }.bind(this);
635 | req.setRequestHeader("Content-Length", contentLength);
636 | req.setRequestHeader('OCS-APIREQUEST', 'true');
637 | req.send(bufStream);
638 | },
639 |
640 | /**
641 | * Cancels the upload request for the file associated with this Uploader.
642 | */
643 | cancel: function nsOFU_cancel() {
644 | this.callback(this.requestObserver, Ci.nsIMsgCloudFileProvider.uploadCanceled);
645 | if (this.request) {
646 | let req = this.request;
647 | if (req.channel) {
648 | this.log.info("canceling channel upload");
649 | delete this.callback;
650 | req.channel.cancel(Cr.NS_BINDING_ABORTED);
651 | }
652 | this.request = null;
653 | }
654 | },
655 |
656 | /**
657 | * Private function that attempts to retrieve the sharing URL for the file
658 | * uploaded with this Uploader.
659 | *
660 | * @param aFile ...
661 | * @param aCallback an nsIRequestObserver for monitoring the starting and
662 | * ending states of the URL retrieval request.
663 | */
664 | _getShareUrl: function nsOFU__getShareUrl(aFile, aCallback) {
665 | //let url = this.owncloud._serverUrl + kWebDavPath;
666 | this.file = aFile;
667 |
668 | let formData = "shareType=3&path=" + wwwFormUrlEncode( ("/" + this.owncloud._storageFolder + "/").replace(/\/+/g,'/')
669 | + this._fileUploadTS[this.file.path] + "_" + this.file.leafName);
670 | if(this.owncloud._protectUploads.length) {
671 | formData += "&password=" + wwwFormUrlEncode(this.owncloud._protectUploads);
672 | }
673 | let args = "?format=json";
674 | let req = Cc["@mozilla.org/xmlextras/xmlhttprequest;1"]
675 | .createInstance(Ci.nsIXMLHttpRequest);
676 |
677 | req.open("POST", this.owncloud._serverUrl + kShareApp + args, true,
678 | this.owncloud._userName, this.owncloud._password);
679 | req.withCredentials = true;
680 | req.setRequestHeader('Content-Type', "application/x-www-form-urlencoded");
681 | req.setRequestHeader("Content-Length", formData.length);
682 | req.setRequestHeader('OCS-APIREQUEST', 'true');
683 |
684 | req.onload = function() {
685 | this.log.debug("Raw response: " + req.responseText);
686 | if (req.status >= 200 && req.status < 400) {
687 | try {
688 | var response = JSON.parse(req.responseText);
689 | if (typeof response.ocs.data.url !== 'undefined') {
690 | this.owncloud._urlsForFiles[this.file.path] = response.ocs.data.url;
691 | if(!this.owncloud._protectUploads.length) {
692 | var arg_separator = this.owncloud._urlsForFiles[this.file.path].lastIndexOf("?") > 0 ? "&" : "/";
693 | this.owncloud._urlsForFiles[this.file.path] += arg_separator +'download';
694 | }
695 | aCallback(this.requestObserver, Cr.NS_OK);
696 | } else {
697 | aCallback(this.requestObserver, Ci.nsIMsgCloudFileProvider.uploadErr);
698 | }
699 | } catch(e) {
700 | this.log.error(e);
701 | aCallback(this.requestObserver, Ci.nsIMsgCloudFileProvider.uploadErr);
702 | }
703 | } else {
704 | this.log.info("Could not retrive share URL");
705 | aCallback(this.requestObserver, Cr.NS_ERROR_FAILURE);
706 | }
707 | }.bind(this);
708 |
709 | req.onerror = function(e) {
710 | this.log.debug("Other error: " + e);
711 | aCallback(this.requestObserver, Cr.NS_ERROR_FAILURE);
712 | }.bind(this);
713 | this.log.debug("Raw formData: " + formData);
714 | req.send(formData);
715 | },
716 | };
717 |
718 | const NSGetFactory = XPCOMUtils.generateNSGetFactory([nsOwncloud]);
719 |
--------------------------------------------------------------------------------
/install.rdf:
--------------------------------------------------------------------------------
1 |
2 |
3 |
5 |
6 |
7 | owncloud@viguierjust.com
8 | ownCloud for Filelink
9 | 1.4
10 | 2
11 | guillaumev
12 |
13 | ownCloud provider for Filelink
14 | https://github.com/guillaumev/owncloud_for_filelink
15 | chrome://owncloud/content/icon.png
16 |
17 |
18 |
19 | {3550f703-e582-4d05-9a08-453d09bdfdc6}
20 | 17.0
21 | 45.*
22 |
23 |
24 |
25 |
26 |
27 | es
28 | Soporte de ownCloud para Filelink
29 | Añade soporte de ownCloud para la característica Filelink en Thunderbird.
30 |
31 |
32 |
33 |
34 |
35 |
--------------------------------------------------------------------------------