550 lines
18 KiB
JavaScript
550 lines
18 KiB
JavaScript
|
/*
|
||
|
|
||
|
Copyright 2008-2015 Clipperz Srl
|
||
|
|
||
|
This file is part of Clipperz, the online password manager.
|
||
|
For further information about its features and functionalities please
|
||
|
refer to http://www.clipperz.com.
|
||
|
|
||
|
* Clipperz is free software: you can redistribute it and/or modify it
|
||
|
under the terms of the GNU Affero General Public License as published
|
||
|
by the Free Software Foundation, either version 3 of the License, or
|
||
|
(at your option) any later version.
|
||
|
|
||
|
* Clipperz is distributed in the hope that it will be useful, but
|
||
|
WITHOUT ANY WARRANTY; without even the implied warranty of
|
||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
||
|
See the GNU Affero General Public License for more details.
|
||
|
|
||
|
* You should have received a copy of the GNU Affero General Public
|
||
|
License along with Clipperz. If not, see http://www.gnu.org/licenses/.
|
||
|
|
||
|
*/
|
||
|
|
||
|
"use strict";
|
||
|
Clipperz.Base.module('Clipperz.PM.UI');
|
||
|
|
||
|
Clipperz.PM.UI.AttachmentController = function(someParameters) {
|
||
|
this.MAX_SIMULTANEOUS_READ = 1;
|
||
|
this.MAX_SIMULTANEOUS_UPLOAD = 1;
|
||
|
this.MAX_SIMULTANEOUS_DOWNLOAD = 1;
|
||
|
this.MAX_SIMULTANEOUS_ENCRYPT = 1;
|
||
|
this.MAX_SIMULTANEOUS_DECRYPT = 1;
|
||
|
|
||
|
this.LATEST_ENCRYPTION_VERSION = '1.0'; // Versions aren't handled completely yet!
|
||
|
|
||
|
this.fileQueue = [];
|
||
|
this.notifications = [];
|
||
|
this.operationsCount = null;
|
||
|
|
||
|
this.encryptedDocument = null;
|
||
|
|
||
|
this.uploadMessageCallback = someParameters['uploadMessageCallback'];
|
||
|
this.downloadMessageCallback = someParameters['downloadMessageCallback'];
|
||
|
this.reloadServerStatusCallback = someParameters['reloadServerStatusCallback'];
|
||
|
|
||
|
return this;
|
||
|
}
|
||
|
|
||
|
MochiKit.Base.update(Clipperz.PM.UI.AttachmentController.prototype, {
|
||
|
|
||
|
toString: function () {
|
||
|
return "Clipperz.PM.UI.AttachmentController";
|
||
|
},
|
||
|
|
||
|
//-------------------------------------------------------------------------
|
||
|
|
||
|
notifyUpdate: function() {
|
||
|
MochiKit.Signal.signal(Clipperz.Signal.NotificationCenter, 'updateAttachmentQueueInfo', this.getQueueInfo(), this.getNotificationsInfo());
|
||
|
},
|
||
|
|
||
|
getQueueInfo: function() {
|
||
|
return this.fileQueue;
|
||
|
},
|
||
|
|
||
|
getNotificationsInfo: function() {
|
||
|
return this.notifications;
|
||
|
},
|
||
|
|
||
|
//=========================================================================
|
||
|
// Entry points
|
||
|
//=========================================================================
|
||
|
|
||
|
addAttachment: function (anAttachment) {
|
||
|
var deferredResult;
|
||
|
|
||
|
deferredResult = new Clipperz.Async.Deferred("Clipperz.PM.UI.AttachmentController.uploadAttachment", {trace:false});
|
||
|
deferredResult.collectResults({
|
||
|
'_attachment': MochiKit.Base.partial(MochiKit.Async.succeed, anAttachment),
|
||
|
'_record': MochiKit.Base.method(anAttachment, 'record'),
|
||
|
'reference': MochiKit.Base.method(anAttachment, 'reference'),
|
||
|
'meta': MochiKit.Base.method(anAttachment, 'metadata'),
|
||
|
'key': MochiKit.Base.method(anAttachment, 'key'),
|
||
|
'nonce': MochiKit.Base.method(anAttachment, 'nonce'),
|
||
|
'status': MochiKit.Base.partial(MochiKit.Async.succeed, 'WAITING_READ'),
|
||
|
'file': MochiKit.Base.method(anAttachment, 'file'),
|
||
|
'recordReference': MochiKit.Base.method(anAttachment.record(), 'reference'),
|
||
|
'process': MochiKit.Base.partial(MochiKit.Async.succeed, 'UPLOAD'), // Used only to differentiate notifications
|
||
|
}, {trace: false});
|
||
|
deferredResult.addMethod(this, 'addFileToQueue');
|
||
|
deferredResult.callback();
|
||
|
|
||
|
return deferredResult;
|
||
|
},
|
||
|
|
||
|
getAttachment: function(anAttachment, aMessageCallback) {
|
||
|
if (this.getQueuePosition(anAttachment.reference()) >= 0) {
|
||
|
this.removeFileFromQueue(anAttachment.reference());
|
||
|
}
|
||
|
var deferredResult;
|
||
|
|
||
|
deferredResult = new Clipperz.Async.Deferred("Clipperz.PM.UI.AttachmentController.downloadAttachment", {trace:false});
|
||
|
deferredResult.collectResults({
|
||
|
'_attachment': MochiKit.Base.partial(MochiKit.Async.succeed, anAttachment),
|
||
|
'_record': MochiKit.Base.method(anAttachment, 'record'),
|
||
|
'reference': MochiKit.Base.method(anAttachment, 'reference'),
|
||
|
'meta': MochiKit.Base.method(anAttachment, 'metadata'),
|
||
|
'key': MochiKit.Base.method(anAttachment, 'key'),
|
||
|
'nonce': MochiKit.Base.method(anAttachment, 'nonce'),
|
||
|
'status': MochiKit.Base.partial(MochiKit.Async.succeed, 'WAITING_DOWNLOAD'),
|
||
|
'messageCallback': MochiKit.Async.succeed(aMessageCallback),
|
||
|
'process': MochiKit.Base.partial(MochiKit.Async.succeed, 'DOWNLOAD'), // Used only to differentiate notifications
|
||
|
}, {trace: false});
|
||
|
deferredResult.addCallback(function(aResult){
|
||
|
MochiKit.Base.update(aResult, {'messageCallback': aMessageCallback});
|
||
|
return aResult;
|
||
|
});
|
||
|
deferredResult.addMethod(this, 'addFileToQueue');
|
||
|
deferredResult.callback();
|
||
|
|
||
|
return deferredResult;
|
||
|
},
|
||
|
|
||
|
cancelAttachment: function(anAttachment) {
|
||
|
var deferredResult;
|
||
|
|
||
|
var reference = anAttachment.reference()
|
||
|
var queueElement = this.getQueueElement(reference);
|
||
|
|
||
|
if (queueElement) {
|
||
|
deferredResult = new Clipperz.Async.Deferred("Clipperz.PM.UI.AttachmentController.cancelAttachment", {trace:false});
|
||
|
deferredResult.addMethod(this, 'updateFileInQueue', reference, {'status': 'CANCELED'});
|
||
|
if (queueElement['deferredRequest']) {
|
||
|
deferredResult.addMethod(queueElement['deferredRequest'], 'cancel');
|
||
|
}
|
||
|
|
||
|
|
||
|
deferredResult.callback();
|
||
|
// TODO: We may also want do delete stuff in the queue element
|
||
|
} else {
|
||
|
deferredResult = MochiKit.Async.succeed();
|
||
|
}
|
||
|
|
||
|
return deferredResult;
|
||
|
},
|
||
|
|
||
|
//=========================================================================
|
||
|
// Queue management
|
||
|
//=========================================================================
|
||
|
|
||
|
dispatchQueueOperations: function() {
|
||
|
var currentElement;
|
||
|
var processNextElements;
|
||
|
|
||
|
var count = this.updateOperationsCount();
|
||
|
|
||
|
this.notifyUpdate();
|
||
|
|
||
|
processNextElements = true;
|
||
|
for (i in this.fileQueue) {
|
||
|
if(processNextElements) {
|
||
|
currentElement = this.fileQueue[i];
|
||
|
switch (currentElement['status']) {
|
||
|
case 'WAITING_READ':
|
||
|
if ((count['READING']) < this.MAX_SIMULTANEOUS_READ) {
|
||
|
this.readFile(currentElement['reference'], currentElement['file']);
|
||
|
processNextElements = false;
|
||
|
}
|
||
|
break;
|
||
|
|
||
|
case 'WAITING_ENCRYPT':
|
||
|
if (count['ENCRYPTING'] < this.MAX_SIMULTANEOUS_ENCRYPT) {
|
||
|
this.encryptFile(currentElement['reference'], currentElement['originalArray'], currentElement['key'], currentElement['nonce']);
|
||
|
processNextElements = false;
|
||
|
}
|
||
|
break;
|
||
|
|
||
|
case 'WAITING_UPLOAD':
|
||
|
if (count['UPLOADING'] < this.MAX_SIMULTANEOUS_UPLOAD) {
|
||
|
this.uploadFile(currentElement['reference'], currentElement['encryptedArray']);
|
||
|
processNextElements = false;
|
||
|
}
|
||
|
break;
|
||
|
|
||
|
case 'WAITING_DOWNLOAD':
|
||
|
if (count['DOWNLOADING'] < this.MAX_SIMULTANEOUS_DOWNLOAD) {
|
||
|
this.downloadFile(currentElement['reference'], currentElement['messageCallback']);
|
||
|
processNextElements = false;
|
||
|
}
|
||
|
break;
|
||
|
|
||
|
case 'WAITING_DECRYPT':
|
||
|
if (count['DECRYPTING'] < this.MAX_SIMULTANEOUS_DECRYPT) {
|
||
|
this.decryptFile(currentElement['reference'], currentElement['encryptedArray'], currentElement['key'], currentElement['nonce']);
|
||
|
processNextElements = false;
|
||
|
}
|
||
|
break;
|
||
|
|
||
|
case 'WAITING_SAVE':
|
||
|
this.saveFile(currentElement['reference'], currentElement['decryptedArray'], currentElement['meta']['name'], currentElement['meta']['type']);
|
||
|
processNextElements = false;
|
||
|
Clipperz.Sound.beep();
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
},
|
||
|
|
||
|
updateOperationsCount: function() {
|
||
|
var count;
|
||
|
|
||
|
count = {
|
||
|
'WAITING_READ': 0,
|
||
|
'READING': 0,
|
||
|
'WAITING_ENCRYPT': 0,
|
||
|
'ENCRYPTING': 0,
|
||
|
'WAITING_UPLOAD': 0,
|
||
|
'UPLOADING': 0,
|
||
|
'WAITING_DOWNLOAD': 0,
|
||
|
'DOWNLOADING': 0,
|
||
|
'WAITING_DECRYPT': 0,
|
||
|
'DECRYPTING': 0,
|
||
|
'WAITING_SAVE': 0,
|
||
|
'DONE': 0,
|
||
|
'CANCELED': 0,
|
||
|
'FAILED': 0,
|
||
|
};
|
||
|
|
||
|
for (var i in this.fileQueue) {
|
||
|
count[this.fileQueue[i]['status']]++;
|
||
|
}
|
||
|
|
||
|
this.operationsCount = count;
|
||
|
|
||
|
return this.operationsCount;
|
||
|
},
|
||
|
|
||
|
addFileToQueue: function(someParameters) {
|
||
|
this.fileQueue.push(someParameters);
|
||
|
this.addNotification(someParameters);
|
||
|
|
||
|
this.dispatchQueueOperations();
|
||
|
},
|
||
|
|
||
|
removeFileFromQueue: function(aFileReference) {
|
||
|
this.fileQueue.splice(this.getQueuePosition(aFileReference), 1);
|
||
|
|
||
|
this.dispatchQueueOperations();
|
||
|
},
|
||
|
|
||
|
getQueueElement: function(aFileReference) {
|
||
|
var i = this.getQueuePosition(aFileReference);
|
||
|
return this.fileQueue[i];
|
||
|
},
|
||
|
|
||
|
updateFileInQueue: function(aFileReference, someParameters) {
|
||
|
var queuePosition = this.getQueuePosition(aFileReference);
|
||
|
|
||
|
MochiKit.Base.update(this.fileQueue[queuePosition], someParameters);
|
||
|
|
||
|
this.dispatchQueueOperations();
|
||
|
},
|
||
|
|
||
|
appendResult: function(aFileReference, anArray) {
|
||
|
var queueElement = this.getQueueElement(aFileReference);
|
||
|
queueElement['result'].set(anArray, queueElement['currentByte']);
|
||
|
},
|
||
|
|
||
|
getQueuePosition: function(aFileReference) {
|
||
|
var result;
|
||
|
|
||
|
result = -1;
|
||
|
for (var i in this.fileQueue) {
|
||
|
if (this.fileQueue[i].reference == aFileReference) {
|
||
|
result = i;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return result;
|
||
|
},
|
||
|
|
||
|
//=========================================================================
|
||
|
// Queue Processing
|
||
|
//=========================================================================
|
||
|
|
||
|
addNotification: function(aQueueElement) {
|
||
|
this.notifications.push({
|
||
|
'id': this.randomId(),
|
||
|
'queueElement': aQueueElement
|
||
|
})
|
||
|
},
|
||
|
|
||
|
removeNotification: function(aNotificationId) {
|
||
|
var i, position;
|
||
|
|
||
|
position = -1;
|
||
|
for (i in this.notifications) {
|
||
|
if (this.notifications[i]['id'] == aNotificationId) {
|
||
|
position = i;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if (position >= 0) {
|
||
|
this.notifications.splice(position, 1);
|
||
|
}
|
||
|
|
||
|
this.notifyUpdate();
|
||
|
},
|
||
|
|
||
|
randomId: function() {
|
||
|
return Clipperz.Crypto.PRNG.defaultRandomGenerator().getRandomBytes(32).toHexString().substring(2);
|
||
|
},
|
||
|
|
||
|
|
||
|
//=========================================================================
|
||
|
// Queue Processing: READ
|
||
|
//=========================================================================
|
||
|
|
||
|
readFile: function(aFileReference, aFile) {
|
||
|
var reader = new FileReader();
|
||
|
|
||
|
this.updateFileInQueue(aFileReference, {
|
||
|
'status': 'READING',
|
||
|
});
|
||
|
|
||
|
reader.onload = MochiKit.Base.method(this, 'readFileOnload', aFileReference);
|
||
|
reader.readAsArrayBuffer(aFile);
|
||
|
},
|
||
|
|
||
|
readFileOnload: function(aFileReference, anEvent) {
|
||
|
this.updateFileInQueue(aFileReference, {
|
||
|
'status': 'WAITING_ENCRYPT',
|
||
|
'originalArray': new Uint8Array(anEvent.target.result),
|
||
|
})
|
||
|
},
|
||
|
|
||
|
|
||
|
//=========================================================================
|
||
|
// Queue Processing: ENCRYPT
|
||
|
//=========================================================================
|
||
|
|
||
|
encryptFile: function(aFileReference, anArrayBuffer, aKey, aNonce) {
|
||
|
this.updateFileInQueue(aFileReference, {
|
||
|
'status': 'ENCRYPTING',
|
||
|
});
|
||
|
|
||
|
window.crypto.subtle.importKey(
|
||
|
"raw",
|
||
|
aKey, //this is an example jwk key, "raw" would be an ArrayBuffer
|
||
|
{ name: "AES-CBC" }, //this is the algorithm options
|
||
|
false, //whether the key is extractable (i.e. can be used in exportKey)
|
||
|
["encrypt"] //can be "encrypt", "decrypt", "wrapKey", or "unwrapKey"
|
||
|
)
|
||
|
// .then(MochiKit.Base.method(this, 'doEncrypt', aFileReference, anArrayBuffer, aNonce))
|
||
|
.then(this.doEncrypt.bind(this,aFileReference, anArrayBuffer, aNonce))
|
||
|
.catch(MochiKit.Base.method(this, 'handleException', aFileReference, 'encryptFile(): encryption failed'));
|
||
|
},
|
||
|
|
||
|
doEncrypt: function(aFileReference, anArrayBuffer, anIV, aWebcryptoKey) {
|
||
|
window.crypto.subtle.encrypt(
|
||
|
{
|
||
|
name: "AES-CBC",
|
||
|
iv: anIV,
|
||
|
},
|
||
|
aWebcryptoKey,
|
||
|
anArrayBuffer
|
||
|
)
|
||
|
.then(MochiKit.Base.method(this, 'doneEncrypt', aFileReference))
|
||
|
.catch(MochiKit.Base.method(this, 'handleException', aFileReference, 'doEncrypt(): encryption failed'));
|
||
|
},
|
||
|
|
||
|
doneEncrypt: function(aFileReference, anArrayBuffer) {
|
||
|
this.updateFileInQueue(aFileReference, {
|
||
|
'status': 'WAITING_UPLOAD',
|
||
|
'encryptedArray': new Uint8Array(anArrayBuffer),
|
||
|
});
|
||
|
},
|
||
|
|
||
|
|
||
|
//=========================================================================
|
||
|
// Queue Processing: UPLOAD
|
||
|
//=========================================================================
|
||
|
|
||
|
uploadFile: function(aFileReference, anEncryptedArray) {
|
||
|
this.updateFileInQueue(aFileReference, {
|
||
|
'status': 'UPLOADING',
|
||
|
'deferredRequest': this.uploadFileRequest(aFileReference, anEncryptedArray),
|
||
|
'requestProgress': 0,
|
||
|
});
|
||
|
},
|
||
|
|
||
|
uploadFileRequest: function(aFileReference, anEncryptedArray) {
|
||
|
var deferredResult;
|
||
|
var queueElement = this.getQueueElement(aFileReference);
|
||
|
|
||
|
deferredResult = new Clipperz.Async.Deferred("Clipperz.PM.UI.AttachmentController.uploadFileRequest", {trace:false});
|
||
|
deferredResult.addCallback(this.uploadMessageCallback, {
|
||
|
'attachmentReference': queueElement['_attachment'].reference(),
|
||
|
'recordReference': queueElement['_attachment'].record().reference(),
|
||
|
'arrayBufferData': anEncryptedArray,
|
||
|
'version': this.LATEST_ENCRYPTION_VERSION,
|
||
|
}, MochiKit.Base.method(this, 'uploadFileProgress', aFileReference));
|
||
|
deferredResult.addMethod(this, 'uploadFileDone', aFileReference);
|
||
|
deferredResult.addErrback(MochiKit.Base.method(this, 'handleException', aFileReference, 'uploadFileRequest(): request failed or canceled'));
|
||
|
deferredResult.callback();
|
||
|
|
||
|
return deferredResult;
|
||
|
},
|
||
|
|
||
|
uploadFileDone: function(aFileReference, aResult){
|
||
|
var record = this.getQueueElement(aFileReference)['_record'];
|
||
|
|
||
|
return Clipperz.Async.callbacks("AttachmentController.uploadFileDone", [
|
||
|
MochiKit.Base.partial(this.reloadServerStatusCallback, record),
|
||
|
MochiKit.Base.method(this, 'updateFileInQueue', aFileReference, {
|
||
|
'status': 'DONE',
|
||
|
'requestProgress': 1,
|
||
|
}),
|
||
|
], {trace:false});
|
||
|
},
|
||
|
|
||
|
uploadFileProgress: function(aFileReference, anEvent) {
|
||
|
var newProgress = (anEvent.lengthComputable) ? (anEvent.loaded / anEvent.total) : -1;
|
||
|
this.updateFileInQueue(aFileReference, {
|
||
|
'requestProgress': newProgress,
|
||
|
});
|
||
|
},
|
||
|
|
||
|
|
||
|
//=========================================================================
|
||
|
// Queue Processing: DOWNLOAD
|
||
|
//=========================================================================
|
||
|
|
||
|
downloadFile: function(aFileReference) {
|
||
|
var deferredRequest;
|
||
|
var queueElement = this.getQueueElement(aFileReference);
|
||
|
|
||
|
deferredRequest = new Clipperz.Async.Deferred("Clipperz.PM.UI.AttachmentController.downloadFile", {trace:false});
|
||
|
deferredRequest.addCallback(this.downloadMessageCallback, queueElement['_attachment'], MochiKit.Base.method(this, 'downloadFileProgress', aFileReference));
|
||
|
deferredRequest.addMethod(this, 'downloadFileDone', aFileReference);
|
||
|
deferredRequest.addErrback(MochiKit.Base.method(this, 'handleException', aFileReference, 'downloadFile(): download filed or canceled'));
|
||
|
deferredRequest.callback();
|
||
|
|
||
|
this.updateFileInQueue(aFileReference, {
|
||
|
'status': 'DOWNLOADING',
|
||
|
'deferredRequest': deferredRequest,
|
||
|
'requestProgress': 0,
|
||
|
});
|
||
|
},
|
||
|
|
||
|
downloadFileDone: function(aFileReference, aResult){
|
||
|
var queueElement = this.getQueueElement(aFileReference);
|
||
|
var encryptedArray = new Uint8Array(aResult);
|
||
|
|
||
|
this.updateFileInQueue(aFileReference, {
|
||
|
'status': 'WAITING_DECRYPT',
|
||
|
'key': queueElement['key'],
|
||
|
'nonce': queueElement['nonce'],
|
||
|
'encryptedArray': encryptedArray,
|
||
|
'requestProgress': 1,
|
||
|
});
|
||
|
|
||
|
},
|
||
|
|
||
|
downloadFileProgress: function(aFileReference, anEvent) {
|
||
|
var newProgress = (anEvent.lengthComputable) ? (anEvent.loaded / anEvent.total) : -1;
|
||
|
|
||
|
this.updateFileInQueue(aFileReference, {
|
||
|
'requestProgress': newProgress,
|
||
|
});
|
||
|
},
|
||
|
|
||
|
|
||
|
//=========================================================================
|
||
|
// Queue Processing: DECRYPT
|
||
|
//=========================================================================
|
||
|
|
||
|
decryptFile: function(aFileReference, anArrayBuffer, aKey, aNonce) {
|
||
|
this.updateFileInQueue(aFileReference, {
|
||
|
'status': 'DECRYPTING',
|
||
|
});
|
||
|
|
||
|
window.crypto.subtle.importKey(
|
||
|
"raw",
|
||
|
aKey, //this is an example jwk key, "raw" would be an ArrayBuffer
|
||
|
{name: "AES-CBC"}, //this is the algorithm options
|
||
|
false, //whether the key is extractable (i.e. can be used in exportKey)
|
||
|
["decrypt"] //can be "encrypt", "decrypt", "wrapKey", or "unwrapKey"
|
||
|
)
|
||
|
.then(MochiKit.Base.method(this, 'doDecrypt', aFileReference, anArrayBuffer, aNonce))
|
||
|
.catch(MochiKit.Base.method(this, 'handleException', aFileReference, 'decryptFile(): decryption failed'));
|
||
|
},
|
||
|
|
||
|
doDecrypt: function(aFileReference, anArrayBuffer, anIV, aWebcryptoKey) {
|
||
|
window.crypto.subtle.decrypt(
|
||
|
{name: "AES-CBC", iv: anIV},
|
||
|
aWebcryptoKey,
|
||
|
anArrayBuffer
|
||
|
)
|
||
|
.then(MochiKit.Base.method(this, 'doneDecrypt', aFileReference))
|
||
|
.catch(MochiKit.Base.method(this, 'handleException', aFileReference, 'doDecrypt(): decryption failed'));
|
||
|
},
|
||
|
|
||
|
doneDecrypt: function(aFileReference, anArrayBuffer) {
|
||
|
this.updateFileInQueue(aFileReference, {
|
||
|
'status': 'WAITING_SAVE',
|
||
|
'decryptedArray': new Uint8Array(anArrayBuffer),
|
||
|
});
|
||
|
},
|
||
|
|
||
|
|
||
|
//=========================================================================
|
||
|
// Queue Processing: SAVE
|
||
|
//=========================================================================
|
||
|
|
||
|
saveFile: function(aFileReference, anArray, aFileName, aFileType) {
|
||
|
var blob = new Blob([anArray], {type: aFileType});
|
||
|
saveAs(blob, aFileName);
|
||
|
|
||
|
this.updateFileInQueue(aFileReference, {
|
||
|
'status': 'DONE',
|
||
|
});
|
||
|
},
|
||
|
|
||
|
|
||
|
//=========================================================================
|
||
|
// Exceptions
|
||
|
//=========================================================================
|
||
|
|
||
|
/** Handles exceptions for upload/download and encrypt/decrypt. Note that
|
||
|
* an exception is thrown also when the user manually cancels the file
|
||
|
* processing. In this case the status remains 'CANCELED'.
|
||
|
*/
|
||
|
handleException: function(aFileReference, aMessage) {
|
||
|
var queueElement = this.getQueueElement(aFileReference);
|
||
|
|
||
|
if (queueElement['status'] != 'CANCELED') {
|
||
|
this.updateFileInQueue(aFileReference, {
|
||
|
'status': 'FAILED',
|
||
|
});
|
||
|
}
|
||
|
|
||
|
if (aMessage) {
|
||
|
console.log("AttachmentController: caught exception (" + aMessage + ")");
|
||
|
}
|
||
|
},
|
||
|
|
||
|
//=========================================================================
|
||
|
|
||
|
__syntaxFix__: "syntax fix"
|
||
|
});
|