From 6f9f415014744b6cc3f1c363d97be1e38f74befc Mon Sep 17 00:00:00 2001 From: Wolfgang Sourdeau Date: Mon, 14 Sep 2009 17:44:39 +0000 Subject: [PATCH] Monotone-Parent: 2b043d9497bc939d7078d10d38953e92948da5aa Monotone-Revision: ff9ea5325e680d04c8672e78e3bd74f60d7316c1 Monotone-Author: wsourdeau@inverse.ca Monotone-Date: 2009-09-14T17:44:39 Monotone-Branch: ca.inverse.sogo --- ChangeLog | 10 + NEWS | 1 + UI/MailPartViewers/GNUmakefile | 1 + UI/MailPartViewers/UIxMailPartSignedViewer.h | 47 ++++ UI/MailPartViewers/UIxMailPartSignedViewer.m | 267 +++++++++++++++++++ UI/MailPartViewers/UIxMailPartViewer.m | 2 +- UI/MailPartViewers/UIxMailRenderingContext.m | 2 +- UI/WebServerResources/MailerUI.css | 13 + UI/WebServerResources/MailerUI.js | 26 ++ UI/WebServerResources/UIxMailPopupView.js | 69 ++--- UI/WebServerResources/signature-not-ok.png | Bin 0 -> 1920 bytes UI/WebServerResources/signature-ok.png | Bin 0 -> 1243 bytes 12 files changed, 402 insertions(+), 36 deletions(-) create mode 100644 UI/MailPartViewers/UIxMailPartSignedViewer.h create mode 100644 UI/MailPartViewers/UIxMailPartSignedViewer.m create mode 100644 UI/WebServerResources/signature-not-ok.png create mode 100644 UI/WebServerResources/signature-ok.png diff --git a/ChangeLog b/ChangeLog index 875c18e80..bb289b14c 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,13 @@ +2009-09-14 Wolfgang Sourdeau + + * UI/MailPartViewers/UIxMailPartSignedViewer.[hm]: new module + class that implements the viewer for multipart/signed messages. + + * UI/WebServerResources/MailerUI.js (configureSignatureFlagImage): + new method that check whether the message being displayed is + signed and if so, displays the proper image depending on the + validation status. + 2009-09-14 Cyril Robert * UI/Common/UIxPageFrame.m (commonLocalizableStrings): clabels are no longer diff --git a/NEWS b/NEWS index b123e3837..447509613 100644 --- a/NEWS +++ b/NEWS @@ -14,6 +14,7 @@ - added support for remote ICS subscriptions - added support for ICS and vCard/LDIF import - added support for event delegation (resend an invitation to someone else) +- added alpha support for checking and displaying signed messages 1.0-20090812 (1.0.4) -------------------- diff --git a/UI/MailPartViewers/GNUmakefile b/UI/MailPartViewers/GNUmakefile index a8cc1428e..25b5dce99 100644 --- a/UI/MailPartViewers/GNUmakefile +++ b/UI/MailPartViewers/GNUmakefile @@ -20,6 +20,7 @@ MailPartViewers_OBJC_FILES += \ UIxMailPartImageViewer.m \ UIxMailPartLinkViewer.m \ UIxMailPartMixedViewer.m \ + UIxMailPartSignedViewer.m \ UIxMailPartAlternativeViewer.m \ UIxMailPartMessageViewer.m \ UIxMailPartICalViewer.m \ diff --git a/UI/MailPartViewers/UIxMailPartSignedViewer.h b/UI/MailPartViewers/UIxMailPartSignedViewer.h new file mode 100644 index 000000000..08d390760 --- /dev/null +++ b/UI/MailPartViewers/UIxMailPartSignedViewer.h @@ -0,0 +1,47 @@ +/* UIxMailPartSignedViewer.h - this file is part of SOGo + * + * Copyright (C) 2009 Inverse inc. + * + * Author: Wolfgang Sourdeau + * + * This file is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This file 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#ifndef UIXMAILPARTSIGNEDVIEWER_H +#define UIXMAILPARTSIGNEDVIEWER_H + +#import "UIxMailPartViewer.h" + +@class NSString; +@class NGPart; + +@interface UIxMailPartSignedViewer : UIxMailPartViewer +{ + BOOL processed; + NGPart *messagePart; + + BOOL validSignature; + NSString *validationError; +} + +- (NSString *) flatContentAsString; + +- (BOOL) validSignature; +- (NSString *) validationError; + +@end + +#endif /* UIXMAILPARTSIGNEDVIEWER_H */ diff --git a/UI/MailPartViewers/UIxMailPartSignedViewer.m b/UI/MailPartViewers/UIxMailPartSignedViewer.m new file mode 100644 index 000000000..7514a14c5 --- /dev/null +++ b/UI/MailPartViewers/UIxMailPartSignedViewer.m @@ -0,0 +1,267 @@ +/* UIxMailPartSignedViewer.m - this file is part of SOGo + * + * Copyright (C) 2009 Inverse inc. + * + * Author: Wolfgang Sourdeau + * + * This file is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This file 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#include +#include +#include +#include +#include + +#import + +#import +#import +#import +#import +#import +#import +#import + +#import +#import + +#import +#import + +#import "UIxMailPartSignedViewer.h" + +@interface NGMimeMessage (SOGoExtension) + +- (id ) mimePartFromPath: (NSArray *) path; + +@end + +@implementation NGMimeMessage (SOGoExtension) + +- (id ) mimePartFromPath: (NSArray *) path +{ + int count, max, partNumber; + BOOL overflow; + id currentPart, foundPart; + NSArray *parts; + + foundPart = nil; + overflow = NO; + + currentPart = [self body]; + + max = [path count]; + for (count = 0; (!overflow && !foundPart) && count < max; count++) + { + partNumber = [[path objectAtIndex: count] intValue]; + if (partNumber == 0) + foundPart = currentPart; + else + { + if ([currentPart isKindOfClass: [NGMimeMultipartBody class]]) + { + parts = [(NGMimeMultipartBody *) currentPart parts]; + if (partNumber <= [parts count]) + currentPart = [parts objectAtIndex: partNumber - 1]; + else + overflow = YES; + } + else + overflow = YES; + } + } + if (!overflow) + foundPart = currentPart; + + return foundPart; +} + +@end + +@interface NGMimeType (SOGoExtension) + +- (BOOL) isEqualToString: (NSString *) otherType; + +@end + +@implementation NGMimeType (SOGoExtension) + +- (BOOL) isEqualToString: (NSString *) otherType +{ + NSString *thisString; + + thisString = [NSString stringWithFormat: @"%@/%@", + [self type], [self subType]]; + + return [thisString isEqualToString: otherType]; +} + +@end + +@implementation UIxMailPartSignedViewer : UIxMailPartViewer + +- (void) _setupParts +{ + NSData *content; + NGMimeMessageParser *parser; + NGMimeMessage *message; + NSArray *path; + id co; + NSArray *parts; + + co = [self clientObject]; + + content = [co content]; + parser = [NGMimeMessageParser new]; + message = [parser parsePartFromData: content]; + if ([co respondsToSelector: @selector (bodyPartPath)]) + path = [(SOGoMailBodyPart *) co bodyPartPath]; + else + path = [NSArray arrayWithObject: @"0"]; + parts = [(NGMimeMultipartBody *) [message mimePartFromPath: path] parts]; + if ([parts count] == 2) + messagePart = [parts objectAtIndex: 0]; + + [parser release]; +} + +- (NSString *) flatContentAsString +{ + NSString *body, *content; + NGMimeType *mimeType; + + [self _setupParts]; + + mimeType = [messagePart contentType]; + if ([mimeType isEqualToString: @"text/plain"]) + { + body = [[messagePart body] stringByEscapingHTMLString]; + content = [[body stringByDetectingURLs] + stringByConvertingCRLNToHTML]; + } + else + { + NSLog (@"unhandled mime type in multipart/signed: '%@'", mimeType); + content = nil; + } + + return content; +} + +- (X509_STORE *) _setupVerify +{ + X509_STORE *store; + X509_LOOKUP *lookup; + BOOL success; + + success = NO; + + store = X509_STORE_new(); + + if (store) + { + lookup = X509_STORE_add_lookup(store,X509_LOOKUP_file()); + if (lookup) + { + X509_LOOKUP_load_file (lookup, NULL, X509_FILETYPE_DEFAULT); + lookup = X509_STORE_add_lookup (store, X509_LOOKUP_hash_dir()); + if (lookup) + { + X509_LOOKUP_add_dir (lookup, NULL, X509_FILETYPE_DEFAULT); + ERR_clear_error(); + success = YES; + } + } + } + + if (!success) + { + if (store) + { + X509_STORE_free(store); + store = NULL; + } + } + + return store; +} + +- (void) _processMessage +{ + BIO *msgBio, *inData; + X509_STORE *x509Store; + PKCS7 *p7; + int err; + NSData *signedData; + char sslError[1024]; + + *sslError = 0; + + ERR_clear_error(); + + signedData = [[self clientObject] content]; + msgBio = BIO_new_mem_buf ((void *) [signedData bytes], [signedData length]); + + inData = NULL; + p7 = SMIME_read_PKCS7 (msgBio, &inData); + err = ERR_get_error(); + if (err) + { + ERR_error_string_n (err, sslError, 1023); + validSignature = NO; + } + else + { + x509Store = [self _setupVerify]; + validSignature = (PKCS7_verify(p7, NULL, x509Store, inData, + NULL, PKCS7_DETACHED) == 1); + + err = ERR_get_error(); + if (err) + ERR_error_string_n (err, sslError, 1023); + + if (x509Store) + X509_STORE_free (x509Store); + } + + BIO_free (msgBio); + if (inData) + BIO_free (inData); + + if (!validSignature) + validationError = [NSString stringWithFormat: @"%s", sslError]; + + processed = YES; +} + +- (BOOL) validSignature +{ + if (!processed) + [self _processMessage]; + + return validSignature; +} + +- (NSString *) validationError +{ + if (!processed) + [self _processMessage]; + + return validationError; +} + +@end diff --git a/UI/MailPartViewers/UIxMailPartViewer.m b/UI/MailPartViewers/UIxMailPartViewer.m index ee388b018..99c8c4ea9 100644 --- a/UI/MailPartViewers/UIxMailPartViewer.m +++ b/UI/MailPartViewers/UIxMailPartViewer.m @@ -156,7 +156,7 @@ - (NSData *) content { - return [[self clientPart] fetchBLOB]; + return [[self clientObject] fetchBLOB]; } - (NSString *) flatContentAsString diff --git a/UI/MailPartViewers/UIxMailRenderingContext.m b/UI/MailPartViewers/UIxMailRenderingContext.m index b24856f5b..867dd00cc 100644 --- a/UI/MailPartViewers/UIxMailRenderingContext.m +++ b/UI/MailPartViewers/UIxMailRenderingContext.m @@ -132,7 +132,7 @@ static BOOL showNamedTextAttachmentsInline = NO; /* Note: we cannot cache the multipart viewers, because it can be nested */ // TODO: temporary workaround (treat it like a plain mixed part) - return [self mixedViewer]; + return [viewer pageWithName: @"UIxMailPartSignedViewer"]; } - (WOComponent *) alternativeViewer diff --git a/UI/WebServerResources/MailerUI.css b/UI/WebServerResources/MailerUI.css index b06985c6d..7817d93a8 100644 --- a/UI/WebServerResources/MailerUI.css +++ b/UI/WebServerResources/MailerUI.css @@ -331,6 +331,19 @@ INPUT#loadImagesButton right: 1em; } +#signedImage +{ + position: absolute; + top: 5px; + right: 5px; +} + +.popup #signedImage +{ + top: 54px; + right: 5px; +} + TABLE.mailer_fieldtable { top: 0px; left: 0px; diff --git a/UI/WebServerResources/MailerUI.js b/UI/WebServerResources/MailerUI.js index f511a947d..2132e1da5 100644 --- a/UI/WebServerResources/MailerUI.js +++ b/UI/WebServerResources/MailerUI.js @@ -947,6 +947,7 @@ function loadMessage(idx) { } configureLoadImagesButton(); + configureSignatureFlagImage(); } function configureLoadImagesButton() { @@ -965,6 +966,30 @@ function configureLoadImagesButton() { } } +function configureSignatureFlagImage() { + var signedPart = $("signedMessage"); + if (signedPart) { + var loadImagesButton = $("loadImagesButton"); + var parentNode = loadImagesButton.parentNode; + var valid = parseInt(signedPart.getAttribute("valid")); + var flagImage; + var error = null; + if (valid) { + flagImage = "signature-ok.png"; + } else { + flagImage = "signature-not-ok.png"; + error = signedPart.getAttribute("error"); + } + var attrs = { src: ResourcesURL + "/" + flagImage }; + if (error) { + attrs["title"] = error; + } + var newImg = createElement("img", "signedImage", null, + null, attrs); + loadImagesButton.parentNode.insertBefore(newImg, loadImagesButton.nextSibling); + } +} + function configureLinksInMessage() { var messageDiv = $('messageContent'); var mailContentDiv = document.getElementsByClassName('mailer_mailcontent', @@ -1190,6 +1215,7 @@ function messageCallback(http) { configureLinksInMessage(); resizeMailContent(); configureLoadImagesButton(); + configureSignatureFlagImage(); if (http.callbackData) { var cachedMessage = new Array(); diff --git a/UI/WebServerResources/UIxMailPopupView.js b/UI/WebServerResources/UIxMailPopupView.js index 3d53b91aa..d021baa3a 100644 --- a/UI/WebServerResources/UIxMailPopupView.js +++ b/UI/WebServerResources/UIxMailPopupView.js @@ -1,57 +1,58 @@ /* -*- Mode: java; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ function onPrintCurrentMessage(event) { - window.print(); + window.print(); - preventDefault(event); + preventDefault(event); } function initPopupMailer(event) { - configureLinksInMessage(); - resizeMailContent(); + configureLinksInMessage(); + resizeMailContent(); - var loadImagesButton = $("loadImagesButton"); - if (loadImagesButton) - loadImagesButton.observe("click", - onMessageLoadImages.bindAsEventListener(loadImagesButton)); + var loadImagesButton = $("loadImagesButton"); + if (loadImagesButton) + loadImagesButton.observe("click", + onMessageLoadImages.bindAsEventListener(loadImagesButton)); - configureLoadImagesButton(); + configureLoadImagesButton(); + configureSignatureFlagImage(); } function onMessageLoadImages(event) { - var msguid = window.opener.Mailer.currentMessages[window.opener.Mailer.currentMailbox]; - var url = (window.opener.ApplicationBaseURL + window.opener.encodeURI(window.opener.Mailer.currentMailbox) + "/" - + msguid + "/view?noframe=1&unsafe=1"); - document.messageAjaxRequest - = triggerAjaxRequest(url, messageCallback, msguid); + var msguid = window.opener.Mailer.currentMessages[window.opener.Mailer.currentMailbox]; + var url = (window.opener.ApplicationBaseURL + window.opener.encodeURI(window.opener.Mailer.currentMailbox) + "/" + + msguid + "/view?noframe=1&unsafe=1"); + document.messageAjaxRequest + = triggerAjaxRequest(url, messageCallback, msguid); } function onICalendarButtonClick(event) { - var link = $("iCalendarAttachment").value; - if (link) { - var urlstr = link + "/" + this.action; - var currentMsg; - if (window.opener && window.opener.open && !window.opener.closed && window.messageUID) { - var c = window.opener; - window.opener.triggerAjaxRequest(urlstr, - window.opener.ICalendarButtonCallback, - window.messageUID); + var link = $("iCalendarAttachment").value; + if (link) { + var urlstr = link + "/" + this.action; + var currentMsg; + if (window.opener && window.opener.open && !window.opener.closed && window.messageUID) { + var c = window.opener; + window.opener.triggerAjaxRequest(urlstr, + window.opener.ICalendarButtonCallback, + window.messageUID); + } } - } - else - log("no link"); + else + log("no link"); } function onMenuDeleteMessage(event) { - if (window.opener && window.opener.open && !window.opener.closed) { - var url = ApplicationBaseURL + encodeURI(mailboxName) + "/deleteMessages"; - var path = mailboxName + "/" + messageName; - - window.opener.deleteMessageWithDelay(url, messageName, mailboxName, path); - } + if (window.opener && window.opener.open && !window.opener.closed) { + var url = ApplicationBaseURL + encodeURI(mailboxName) + "/deleteMessages"; + var path = mailboxName + "/" + messageName; + + window.opener.deleteMessageWithDelay(url, messageName, mailboxName, path); + } - window.close(); - return false; + window.close(); + return false; } document.observe("dom:loaded", initPopupMailer); diff --git a/UI/WebServerResources/signature-not-ok.png b/UI/WebServerResources/signature-not-ok.png new file mode 100644 index 0000000000000000000000000000000000000000..34df3bffd12e9d230f547520449b2f5d8a1fb922 GIT binary patch literal 1920 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