diff --git a/UI/Common/English.lproj/Localizable.strings b/UI/Common/English.lproj/Localizable.strings index f7497fb38..6eaf6b4ef 100644 --- a/UI/Common/English.lproj/Localizable.strings +++ b/UI/Common/English.lproj/Localizable.strings @@ -76,6 +76,10 @@ "Yes" = "Yes"; "No" = "No"; +/* generic messages */ +"Error" = "Error"; +"Success" = "Success"; + /* alarms */ "Reminder" = "Reminder"; "Start" = "Start"; diff --git a/UI/MailerUI/UIxMailAccountActions.m b/UI/MailerUI/UIxMailAccountActions.m index d66b0f2b5..2b057c1af 100644 --- a/UI/MailerUI/UIxMailAccountActions.m +++ b/UI/MailerUI/UIxMailAccountActions.m @@ -20,8 +20,20 @@ #import +#import + +#import #import +#define COMPILING_NGOBJWEB 1 /* httpRequest is needed in importCertificateAction */ #import +#undef COMPILING_NGOBJWEB +#import + +#import +#import +#import +#import + #import #import @@ -91,7 +103,7 @@ drafts = [[self clientObject] draftsFolderInContext: context]; newDraftMessage = [drafts newDraft]; headers = [NSMutableDictionary dictionary]; - + save = NO; value = [[self request] formValueForKey: @"mailto"]; @@ -177,4 +189,64 @@ return [self _performDelegationAction: @selector (removeDelegates:)]; } +- (WOResponse *) importCertificateAction +{ + NSArray *parts; + NGMimeBodyPart *part; + NGMimeContentDispositionHeaderField *header; + NSData *pkcs12; + NSString *mimeType, *name, *password; + WOResponse *response; + id data; + unsigned int count, max; + + password = nil; + pkcs12 = nil; + response = [self responseWithStatus: 507]; + + data = [[[context request] httpRequest] body]; + + if (![data isKindOfClass: [NSException class]]) + { + parts = [data parts]; + max = [parts count]; + for (count = 0; count < max; count++) + { + part = [parts objectAtIndex: count]; + header = (NGMimeContentDispositionHeaderField *)[part headerForKey: @"content-disposition"]; + name = [header name]; + if ([name isEqualToString: @"password"]) + { + password = name; + } + else if ([name isEqualToString: @"file"]) + { + mimeType = [(NGMimeType *)[part headerForKey: @"content-type"] stringValue]; + if ([mimeType hasSuffix: @"pkcs12"]) + { + pkcs12 = [part body]; + } + else + { + response = [self responseWithStatus: 507]; + } + } + } + } + + if (password && pkcs12) + { + // TODO: append certificate to user defaults + response = [self responseWith204]; + } + + return response; +} + +- (WOResponse *) removeCertificateAction +{ + // TODO: remove certificate from user defaults + return [self responseWith204]; +} + @end diff --git a/UI/MailerUI/product.plist b/UI/MailerUI/product.plist index 91fc3f656..7c3b02487 100644 --- a/UI/MailerUI/product.plist +++ b/UI/MailerUI/product.plist @@ -433,6 +433,16 @@ actionClass = "UIxMailAccountActions"; actionName = "removeDelegate"; }; + importCertificate = { + protectedBy = "Change Images And Files"; + actionClass = "UIxMailAccountActions"; + actionName = "importCertificate"; + }; + removeCertificate = { + protectedBy = "Change Images And Files"; + actionClass = "UIxMailAccountActions"; + actionName = "removeCertificate"; + }; }; }; diff --git a/UI/PreferencesUI/English.lproj/Localizable.strings b/UI/PreferencesUI/English.lproj/Localizable.strings index 2d4adead1..ddac7f81e 100644 --- a/UI/PreferencesUI/English.lproj/Localizable.strings +++ b/UI/PreferencesUI/English.lproj/Localizable.strings @@ -187,6 +187,8 @@ "Collected Address Book" = "Collected Address Book"; /* IMAP Accounts */ +"Settings" = "Settings"; +"Security" = "Security"; "Mail Account" = "Mail Account"; "New Mail Account" = "New Mail Account"; "Server Name" = "Server Name"; @@ -204,6 +206,17 @@ "Please specify a valid reply-to address." = "Please specify a valid reply-to address."; "Specify a hostname other than the local host" = "Specify a hostname other than the local host"; +"S/MIME Certificate" = "S/MIME Certificate"; +"No certificate installed" = "No certificate installed"; +"The SSL certificate must use the PKCS#12 (PFX) format." = "The SSL certificate must use the PKCS#12 (PFX) format."; +"Uninstall" = "Uninstall"; +"Choose PKCS12 Certificate .." = "Choose PKCS12 Certificate .."; +"Certificate Password" = "Certificate Password"; +"Upload" = "Upload"; +"When composing a message" = "When composing a message"; +"Digitally sign the message by default" = "Digitally sign the message by default"; +"Always try to encrypt the message" = "Always try to encrypt the message"; + /* Additional Parameters */ "Additional Parameters" = "Additional Parameters"; diff --git a/UI/Templates/PreferencesUI/UIxAccountEditor.wox b/UI/Templates/PreferencesUI/UIxAccountEditor.wox index a29cdbf70..1e78dffdd 100644 --- a/UI/Templates/PreferencesUI/UIxAccountEditor.wox +++ b/UI/Templates/PreferencesUI/UIxAccountEditor.wox @@ -20,159 +20,243 @@ - - +
-
- - - -
-
-
-
+ - - - - -
+ + + - - - -
-
- +
+ + + +
+
+
+
+ + + + +
-
- + + + + +
+
+ +
+
+ +
+
+ +
+
+
+
+ +
+ + + + + + + + +
-
- + + + + + + +
+ + + + + + + +
-
- - -
- - - - + + + + + +
+ + +
- - - - -
+ + + + + + + - - - - +
+ + + + + + + + -
- - - - - - - - -
+ + + + + + + + - - - - - -
- - -
+ + + + + + + + +
- - - - - - - + + -
- - - - - - - - + + + - - - - - - - - +
+ + + +
+
+
+
+ + + +
+ + +
+
+ + + + - - - - - - - - -
+
+ + + +
+ + + +
+ +
+ + +
+
+ + + + + - diff --git a/UI/Templates/PreferencesUI/UIxPreferences.wox b/UI/Templates/PreferencesUI/UIxPreferences.wox index 911279b8f..c8565b827 100644 --- a/UI/Templates/PreferencesUI/UIxPreferences.wox +++ b/UI/Templates/PreferencesUI/UIxPreferences.wox @@ -8,7 +8,7 @@ xmlns:label="OGo:label" className="UIxPageFrame" title="title" - const:jsFiles="vendor/ckeditor/ckeditor.js, vendor/ckeditor/ck.js, vendor/ng-sortable.min.js, Common.js, Preferences.js, Preferences.services.js, Mailer.services.js, Contacts.services.js"> + const:jsFiles="vendor/ckeditor/ckeditor.js, vendor/ckeditor/ck.js, vendor/angular-file-upload.min.js, vendor/ng-sortable.min.js, Common.js, Preferences.js, Preferences.services.js, Mailer.services.js, Contacts.services.js">
diff --git a/UI/WebServerResources/js/Common/Dialog.service.js b/UI/WebServerResources/js/Common/Dialog.service.js index 4ce2473af..a5cc33138 100644 --- a/UI/WebServerResources/js/Common/Dialog.service.js +++ b/UI/WebServerResources/js/Common/Dialog.service.js @@ -20,7 +20,8 @@ var alert = this.$modal.alert() .title(title) .htmlContent(content) - .ok(l('OK')); + .ok(l('OK')) + .multiple(true); this.$modal.show(alert); }; diff --git a/UI/WebServerResources/js/Mailer/Account.service.js b/UI/WebServerResources/js/Mailer/Account.service.js index e2105ef04..6ec15dda2 100644 --- a/UI/WebServerResources/js/Mailer/Account.service.js +++ b/UI/WebServerResources/js/Mailer/Account.service.js @@ -287,6 +287,18 @@ }); }; + /** + * @function $removeCertificate + * @memberof Account.prototype + * @desc Remove any S/MIME certificate associated with the account. + * @returns a promise of the HTTP operation + */ + Account.prototype.$removeCertificate = function() { + var _this = this; + + return Account.$$resource.fetch(this.id.toString(), 'removeCertificate'); + }; + /** * @function updateQuota * @memberof Account.prototype diff --git a/UI/WebServerResources/js/Preferences/AccountDialogController.js b/UI/WebServerResources/js/Preferences/AccountDialogController.js index feb1fa8a3..c9ef518ec 100644 --- a/UI/WebServerResources/js/Preferences/AccountDialogController.js +++ b/UI/WebServerResources/js/Preferences/AccountDialogController.js @@ -7,8 +7,8 @@ /** * @ngInject */ - AccountDialogController.$inject = ['$mdDialog', 'defaults', 'account', 'accountId', 'mailCustomFromEnabled']; - function AccountDialogController($mdDialog, defaults, account, accountId, mailCustomFromEnabled) { + AccountDialogController.$inject = ['$mdDialog', '$mdToast', 'FileUploader', 'Dialog', 'sgSettings', 'Account', 'defaults', 'account', 'accountId', 'mailCustomFromEnabled']; + function AccountDialogController($mdDialog, $mdToast, FileUploader, Dialog, Settings, Account, defaults, account, accountId, mailCustomFromEnabled) { var vm = this; vm.defaultPort = 143; @@ -16,6 +16,10 @@ vm.account = account; vm.accountId = accountId; vm.customFromIsReadonly = customFromIsReadonly; + vm.onBeforeUploadCertificate = onBeforeUploadCertificate; + vm.certificateIsInstalled = certificateIsInstalled; + vm.removeCertificate = removeCertificate; + vm.importCertificate = importCertificate; vm.cancel = cancel; vm.save = save; vm.hostnameRE = accountId > 0 ? /^(?!(127\.0\.0\.1|localhost(?:\.localdomain)?)$)/ : /./; @@ -25,13 +29,65 @@ else if (vm.account.encryption == "ssl") vm.defaultPort = 993; + if (vm.account.certificateFilename) + vm.certificateFilename = vm.account.certificateFilename; + + vm.uploader = new FileUploader({ + url: [Settings.activeUser('folderURL') + 'Mail', accountId, 'importCertificate'].join('/'), + autoUpload: false, + queueLimit: 1, + filters: [{ name: filterByExtension, fn: filterByExtension }], + onAfterAddingFile: function(item) { + vm.certificateFilename = item.file.name; + }, + onSuccessItem: function(item, response, status, headers) { + var el = angular.element(document.getElementById('accountSecurityContent')); + $mdToast.show( + $mdToast.simple() + .content(l('Success')) + .parent(el) + .position('top right') + .hideDelay(3000)); + this.clearQueue(); + }, + onErrorItem: function(item, response, status, headers) { + Dialog.alert(l('Error'), l('An error occurred while importing the certificate. Verify your password.')); + } + }); + + function filterByExtension(item) { + var isP12File = item.type.indexOf('pkcs12') > 0 || /\.(p12|pfx)$/.test(item.name); + vm.form.certificateFilename.$setValidity('fileformat', isP12File); + return isP12File; + } + function customFromIsReadonly() { if (accountId > 0) return false; - return !mailCustomFromEnabled; } + function importCertificate() { + vm.uploader.queue[0].formData = [{ password: vm.certificatePassword }]; + vm.uploader.uploadAll(); + } + + function onBeforeUploadCertificate(form) { + vm.form = form; + vm.uploader.clearQueue(); + } + + function certificateIsInstalled() { + return vm.certificateFilename && vm.uploader.queue.length === 0; + } + + function removeCertificate() { + var accountObject = new Account({ id: accountId }); + accountObject.$removeCertificate().then(function() { + delete vm.certificateFilename; + }); + } + function cancel() { $mdDialog.cancel(); } diff --git a/UI/WebServerResources/js/Preferences/Preferences.app.js b/UI/WebServerResources/js/Preferences/Preferences.app.js index f38b224d9..2126e8322 100644 --- a/UI/WebServerResources/js/Preferences/Preferences.app.js +++ b/UI/WebServerResources/js/Preferences/Preferences.app.js @@ -4,7 +4,7 @@ (function() { 'use strict'; - angular.module('SOGo.PreferencesUI', ['ui.router', 'ck', 'SOGo.Common', 'SOGo.MailerUI', 'SOGo.ContactsUI', 'SOGo.Authentication', 'as.sortable']) + angular.module('SOGo.PreferencesUI', ['ui.router', 'ck', 'angularFileUpload', 'SOGo.Common', 'SOGo.MailerUI', 'SOGo.ContactsUI', 'SOGo.Authentication', 'as.sortable']) .config(configure) .run(runBlock);