(feat) initial S/MIME support for EAS (#3327)
parent
ddf97ebb13
commit
020fa78848
|
@ -1 +1,4 @@
|
||||||
# compilation settings
|
ifeq ($(HAS_LIBRARY_ssl),yes)
|
||||||
|
ADDITIONAL_CPPFLAGS += -DHAVE_OPENSSL=1
|
||||||
|
BUNDLE_LIBS += -lcrypto
|
||||||
|
endif
|
||||||
|
|
|
@ -1407,6 +1407,11 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
// By default, send MIME mails. See #3146 for details.
|
// By default, send MIME mails. See #3146 for details.
|
||||||
if (!bodyPreferenceType)
|
if (!bodyPreferenceType)
|
||||||
bodyPreferenceType = @"4";
|
bodyPreferenceType = @"4";
|
||||||
|
|
||||||
|
mimeSupport = [[folderMetadata objectForKey: @"FolderOptions"] objectForKey: @"MIMESupport"];
|
||||||
|
|
||||||
|
if (!mimeSupport)
|
||||||
|
mimeSupport = @"1";
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
@ -1437,6 +1442,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
}
|
}
|
||||||
|
|
||||||
[context setObject: bodyPreferenceType forKey: @"BodyPreferenceType"];
|
[context setObject: bodyPreferenceType forKey: @"BodyPreferenceType"];
|
||||||
|
[context setObject: mimeSupport forKey: @"MIMESupport"];
|
||||||
|
|
||||||
//
|
//
|
||||||
// We process the commands from the request
|
// We process the commands from the request
|
||||||
|
|
|
@ -30,6 +30,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
#include "SOGoActiveSyncDispatcher.h"
|
#include "SOGoActiveSyncDispatcher.h"
|
||||||
|
|
||||||
#import <Foundation/NSArray.h>
|
#import <Foundation/NSArray.h>
|
||||||
|
#import <Foundation/NSData.h>
|
||||||
#import <Foundation/NSAutoreleasePool.h>
|
#import <Foundation/NSAutoreleasePool.h>
|
||||||
#import <Foundation/NSCalendarDate.h>
|
#import <Foundation/NSCalendarDate.h>
|
||||||
#import <Foundation/NSLocale.h>
|
#import <Foundation/NSLocale.h>
|
||||||
|
@ -135,6 +136,12 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
|
|
||||||
|
#ifdef HAVE_OPENSSL
|
||||||
|
#include <openssl/bio.h>
|
||||||
|
#include <openssl/err.h>
|
||||||
|
#include <openssl/x509.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
@interface SOGoActiveSyncDispatcher (Sync)
|
@interface SOGoActiveSyncDispatcher (Sync)
|
||||||
|
|
||||||
- (NSMutableDictionary *) _folderMetadataForKey: (NSString *) theFolderKey;
|
- (NSMutableDictionary *) _folderMetadataForKey: (NSString *) theFolderKey;
|
||||||
|
@ -1262,7 +1269,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
- (void) processItemOperations: (id <DOMElement>) theDocumentElement
|
- (void) processItemOperations: (id <DOMElement>) theDocumentElement
|
||||||
inResponse: (WOResponse *) theResponse
|
inResponse: (WOResponse *) theResponse
|
||||||
{
|
{
|
||||||
NSString *fileReference, *realCollectionId, *serverId, *bodyPreferenceType, *collectionId;
|
NSString *fileReference, *realCollectionId, *serverId, *bodyPreferenceType, *mimeSupport, *collectionId;
|
||||||
NSMutableString *s;
|
NSMutableString *s;
|
||||||
NSArray *fetchRequests;
|
NSArray *fetchRequests;
|
||||||
id aFetch;
|
id aFetch;
|
||||||
|
@ -1375,6 +1382,8 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
serverId = [[(id)[theDocumentElement getElementsByTagName: @"ServerId"] lastObject] textValue];
|
serverId = [[(id)[theDocumentElement getElementsByTagName: @"ServerId"] lastObject] textValue];
|
||||||
bodyPreferenceType = [[(id)[[(id)[theDocumentElement getElementsByTagName: @"BodyPreference"] lastObject] getElementsByTagName: @"Type"] lastObject] textValue];
|
bodyPreferenceType = [[(id)[[(id)[theDocumentElement getElementsByTagName: @"BodyPreference"] lastObject] getElementsByTagName: @"Type"] lastObject] textValue];
|
||||||
[context setObject: bodyPreferenceType forKey: @"BodyPreferenceType"];
|
[context setObject: bodyPreferenceType forKey: @"BodyPreferenceType"];
|
||||||
|
mimeSupport = [[(id)[theDocumentElement getElementsByTagName: @"MIMESupport"] lastObject] textValue];
|
||||||
|
[context setObject: mimeSupport forKey: @"MIMESupport"];
|
||||||
|
|
||||||
currentCollection = [self collectionFromId: realCollectionId type: folderType];
|
currentCollection = [self collectionFromId: realCollectionId type: folderType];
|
||||||
|
|
||||||
|
@ -2102,6 +2111,123 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
[theResponse setContent: d];
|
[theResponse setContent: d];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
//
|
||||||
|
//
|
||||||
|
#ifdef HAVE_OPENSSL
|
||||||
|
- (unsigned int) validateCert: (NSString *) theCert
|
||||||
|
{
|
||||||
|
NSData *d;
|
||||||
|
|
||||||
|
const unsigned char *data;
|
||||||
|
X509_STORE_CTX *ctx;
|
||||||
|
X509_LOOKUP *lookup;
|
||||||
|
X509_STORE *store;
|
||||||
|
X509 *cert;
|
||||||
|
|
||||||
|
BOOL success;
|
||||||
|
size_t len;
|
||||||
|
int rc;
|
||||||
|
|
||||||
|
success = NO;
|
||||||
|
|
||||||
|
d = [theCert dataByDecodingBase64];
|
||||||
|
data = (unsigned char *)[d bytes];
|
||||||
|
len = [d length];
|
||||||
|
|
||||||
|
cert = d2i_X509(NULL, &data, len);
|
||||||
|
if (!cert)
|
||||||
|
{
|
||||||
|
[self logWithFormat: @"EAS - validateCert failed for device %@: d2i_X509 failed", [context objectForKey: @"DeviceId"]];
|
||||||
|
return 17;
|
||||||
|
}
|
||||||
|
|
||||||
|
store = X509_STORE_new();
|
||||||
|
OpenSSL_add_all_algorithms();
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ctx = X509_STORE_CTX_new();
|
||||||
|
if (!ctx)
|
||||||
|
{
|
||||||
|
[self logWithFormat: @"EAS - validateCert failed for device %@: X509_STORE_CTX_new failed", [context objectForKey: @"DeviceId"]];
|
||||||
|
return 17;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (X509_STORE_CTX_init(ctx, store, cert, NULL) != 1)
|
||||||
|
{
|
||||||
|
[self logWithFormat: @"EAS - validateCert failed for device %@: X509_STORE_CTX_init failed", [context objectForKey: @"DeviceId"]];
|
||||||
|
X509_STORE_CTX_free(ctx);
|
||||||
|
return 17;
|
||||||
|
}
|
||||||
|
|
||||||
|
rc = X509_verify_cert(ctx);
|
||||||
|
X509_STORE_CTX_free(ctx);
|
||||||
|
X509_free(cert);
|
||||||
|
|
||||||
|
if (rc)
|
||||||
|
{
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
[self logWithFormat: @"EAS - validateCert failed for device %@: err=%d", [context objectForKey: @"DeviceId"], X509_STORE_CTX_get_error(ctx)];
|
||||||
|
return 17;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
- (unsigned int) validateCert: (NSString *) theCert
|
||||||
|
{
|
||||||
|
return 17;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
- (void) processValidateCert: (id <DOMElement>) theDocumentElement
|
||||||
|
inResponse: (WOResponse *) theResponse
|
||||||
|
{
|
||||||
|
NSMutableString *s;
|
||||||
|
NSString *cert;
|
||||||
|
NSData *d;
|
||||||
|
|
||||||
|
cert = [[(id)[theDocumentElement getElementsByTagName: @"Certificate"] lastObject] textValue];
|
||||||
|
|
||||||
|
s = [NSMutableString string];
|
||||||
|
[s appendString: @"<?xml version=\"1.0\" encoding=\"utf-8\"?>"];
|
||||||
|
[s appendString: @"<!DOCTYPE ActiveSync PUBLIC \"-//MICROSOFT//DTD ActiveSync//EN\" \"http://www.microsoft.com/\">"];
|
||||||
|
[s appendString: @"<ValidateCert xmlns=\"ValidateCert:\">"];
|
||||||
|
[s appendString: @"<Status>1</Status><Certificate>"];
|
||||||
|
[s appendFormat: @"<Status>%d</Status>", [self validateCert: cert]];
|
||||||
|
[s appendString: @"</Certificate></ValidateCert>"];
|
||||||
|
|
||||||
|
d = [[s dataUsingEncoding: NSUTF8StringEncoding] xml2wbxml];
|
||||||
|
|
||||||
|
[theResponse setContent: d];
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
//
|
//
|
||||||
// <?xml version="1.0"?>
|
// <?xml version="1.0"?>
|
||||||
// <!DOCTYPE ActiveSync PUBLIC "-//MICROSOFT//DTD ActiveSync//EN" "http://www.microsoft.com/">
|
// <!DOCTYPE ActiveSync PUBLIC "-//MICROSOFT//DTD ActiveSync//EN" "http://www.microsoft.com/">
|
||||||
|
@ -2418,39 +2544,85 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
NGMimeMessageParser *parser;
|
NGMimeMessageParser *parser;
|
||||||
NGMimeMessage *message;
|
NGMimeMessage *message;
|
||||||
NSException *error;
|
NSException *error;
|
||||||
NSData *data;
|
NSMutableData *data;
|
||||||
NGMutableHashMap *map;
|
NSData *new_from_header;
|
||||||
NGMimeMessage *messageToSend;
|
|
||||||
NGMimeMessageGenerator *generator;
|
|
||||||
NSDictionary *identity;
|
NSDictionary *identity;
|
||||||
NSString *fullName, *email;
|
NSString *fullName, *email;
|
||||||
|
|
||||||
|
const char *bytes;
|
||||||
|
int i, len;
|
||||||
|
BOOL found_header;
|
||||||
|
|
||||||
// We get the mail's data
|
// We get the mail's data
|
||||||
data = [[[[(id)[theDocumentElement getElementsByTagName: @"MIME"] lastObject] textValue] stringByDecodingBase64] dataUsingEncoding: NSUTF8StringEncoding];
|
data = [NSMutableData dataWithData: [[[[(id)[theDocumentElement getElementsByTagName: @"MIME"] lastObject] textValue] stringByDecodingBase64] dataUsingEncoding: NSUTF8StringEncoding]];
|
||||||
|
|
||||||
// We extract the recipients
|
// We extract the recipients
|
||||||
parser = [[NGMimeMessageParser alloc] init];
|
parser = [[NGMimeMessageParser alloc] init];
|
||||||
message = [parser parsePartFromData: data];
|
message = [parser parsePartFromData: data];
|
||||||
RELEASE(parser);
|
RELEASE(parser);
|
||||||
|
|
||||||
map = [NGHashMap hashMapWithDictionary: [message headers]];
|
|
||||||
|
|
||||||
identity = [[context activeUser] primaryIdentity];
|
identity = [[context activeUser] primaryIdentity];
|
||||||
|
|
||||||
fullName = [identity objectForKey: @"fullName"];
|
fullName = [identity objectForKey: @"fullName"];
|
||||||
email = [identity objectForKey: @"email"];
|
email = [identity objectForKey: @"email"];
|
||||||
|
|
||||||
if ([fullName length])
|
if ([fullName length])
|
||||||
[map setObject: [NSString stringWithFormat: @"%@ <%@>", fullName, email] forKey: @"from"];
|
new_from_header = [[NSString stringWithFormat: @"From: %@ <%@>\r\n", fullName, email] dataUsingEncoding:NSUTF8StringEncoding];
|
||||||
else
|
else
|
||||||
[map setObject: email forKey: @"from"];
|
new_from_header = [[NSString stringWithFormat: @"From: %@\r\n", email] dataUsingEncoding:NSUTF8StringEncoding];
|
||||||
|
|
||||||
messageToSend = [[[NGMimeMessage alloc] initWithHeader: map] autorelease];
|
bytes = [data bytes];
|
||||||
|
len = [data length];
|
||||||
|
i = 0;
|
||||||
|
found_header = NO;
|
||||||
|
|
||||||
[messageToSend setBody: [message body]];
|
// Search for the from-header
|
||||||
|
while (i < len)
|
||||||
|
{
|
||||||
|
if (i == 0 &&
|
||||||
|
(*bytes == 'f' || *bytes == 'F') &&
|
||||||
|
(*(bytes+1) == 'r' || *(bytes+1) == 'R') &&
|
||||||
|
(*(bytes+2) == 'o' || *(bytes+2) == 'O') &&
|
||||||
|
(*(bytes+3) == 'm' || *(bytes+3) == 'M') &&
|
||||||
|
(*(bytes+4) == ':'))
|
||||||
|
{
|
||||||
|
found_header = YES;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (((*bytes == '\r') && (*(bytes+1) == '\n')) &&
|
||||||
|
(*(bytes+2) == 'f' || *(bytes+2) == 'F') &&
|
||||||
|
(*(bytes+3) == 'r' || *(bytes+3) == 'R') &&
|
||||||
|
(*(bytes+4) == 'o' || *(bytes+4) == 'O') &&
|
||||||
|
(*(bytes+5) == 'm' || *(bytes+5) == 'M') &&
|
||||||
|
(*(bytes+6) == ':'))
|
||||||
|
{
|
||||||
|
found_header = YES;
|
||||||
|
i = i + 2; // \r\n
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
bytes++;
|
||||||
|
i++;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update/Add the From header in the MIMEBody of the SendMail request.
|
||||||
|
// Any other way to modify the mail body would break s/mime emails.
|
||||||
|
if (found_header)
|
||||||
|
{
|
||||||
|
// Change the From header
|
||||||
|
[data replaceBytesInRange: NSMakeRange(i, [[message headerForKey: @"from"] length]+8) // start of the From header found - length of the parsed from-header-value + 8 (From:+\r\n+1)
|
||||||
|
withBytes: [new_from_header bytes]
|
||||||
|
length: [new_from_header length]];
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// Add a From header
|
||||||
|
[data replaceBytesInRange: NSMakeRange(0, 0)
|
||||||
|
withBytes: [new_from_header bytes]
|
||||||
|
length: [new_from_header length]];
|
||||||
|
}
|
||||||
|
|
||||||
generator = [[[NGMimeMessageGenerator alloc] init] autorelease];
|
|
||||||
data = [generator generateMimeFromPart: messageToSend];
|
|
||||||
|
|
||||||
error = [self _sendMail: data
|
error = [self _sendMail: data
|
||||||
recipients: [message allRecipients]
|
recipients: [message allRecipients]
|
||||||
saveInSentItems: ([(id)[theDocumentElement getElementsByTagName: @"SaveInSentItems"] count] ? YES : NO)];
|
saveInSentItems: ([(id)[theDocumentElement getElementsByTagName: @"SaveInSentItems"] count] ? YES : NO)];
|
||||||
|
|
|
@ -476,14 +476,16 @@ struct GlobalObjectId {
|
||||||
//
|
//
|
||||||
//
|
//
|
||||||
- (NSData *) _preferredBodyDataUsingType: (int) theType
|
- (NSData *) _preferredBodyDataUsingType: (int) theType
|
||||||
|
mimeSupport: (int) theMimeSupport
|
||||||
nativeType: (int *) theNativeType
|
nativeType: (int *) theNativeType
|
||||||
{
|
{
|
||||||
NSString *type, *subtype, *encoding;
|
NSString *type, *subtype, *encoding;
|
||||||
NSData *d;
|
NSData *d;
|
||||||
|
BOOL isSMIME;
|
||||||
|
|
||||||
type = [[[self bodyStructure] valueForKey: @"type"] lowercaseString];
|
type = [[[self bodyStructure] valueForKey: @"type"] lowercaseString];
|
||||||
subtype = [[[self bodyStructure] valueForKey: @"subtype"] lowercaseString];
|
subtype = [[[self bodyStructure] valueForKey: @"subtype"] lowercaseString];
|
||||||
|
isSMIME = NO;
|
||||||
d = nil;
|
d = nil;
|
||||||
|
|
||||||
// We determine the native type
|
// We determine the native type
|
||||||
|
@ -494,8 +496,14 @@ struct GlobalObjectId {
|
||||||
else if ([type isEqualToString: @"multipart"])
|
else if ([type isEqualToString: @"multipart"])
|
||||||
*theNativeType = 4;
|
*theNativeType = 4;
|
||||||
|
|
||||||
|
if (([subtype isEqualToString: @"signed"] || [subtype isEqualToString: @"pkcs7-mime"] ) && theMimeSupport > 0)
|
||||||
|
{
|
||||||
|
*theNativeType = 4;
|
||||||
|
isSMIME = YES;
|
||||||
|
}
|
||||||
|
|
||||||
// We get the right part based on the preference
|
// We get the right part based on the preference
|
||||||
if (theType == 1 || theType == 2)
|
if ((theType == 1 || theType == 2) && !isSMIME)
|
||||||
{
|
{
|
||||||
if ([type isEqualToString: @"text"] && ![subtype isEqualToString: @"calendar"])
|
if ([type isEqualToString: @"text"] && ![subtype isEqualToString: @"calendar"])
|
||||||
{
|
{
|
||||||
|
@ -536,12 +544,12 @@ struct GlobalObjectId {
|
||||||
d = [self _preferredBodyDataInMultipartUsingType: theType nativeTypeFound: theNativeType];
|
d = [self _preferredBodyDataInMultipartUsingType: theType nativeTypeFound: theNativeType];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if (theType == 4)
|
else if (theType == 4 || isSMIME)
|
||||||
{
|
{
|
||||||
// We sanitize the content *ONLY* for Outlook clients and if the content-transfer-encoding is 8bit. Outlook has strange issues
|
// We sanitize the content *ONLY* for Outlook clients and if the content-transfer-encoding is 8bit. Outlook has strange issues
|
||||||
// with quoted-printable/base64 encoded text parts. It just doesn't decode them.
|
// with quoted-printable/base64 encoded text parts. It just doesn't decode them.
|
||||||
encoding = [[self lookupInfoForBodyPart: @""] objectForKey: @"encoding"];
|
encoding = [[self lookupInfoForBodyPart: @""] objectForKey: @"encoding"];
|
||||||
if (encoding && ([encoding caseInsensitiveCompare: @"8bit"] == NSOrderedSame))
|
if ((encoding && ([encoding caseInsensitiveCompare: @"8bit"] == NSOrderedSame)) && !isSMIME)
|
||||||
d = [self _sanitizedMIMEMessage];
|
d = [self _sanitizedMIMEMessage];
|
||||||
else
|
else
|
||||||
d = [self content];
|
d = [self content];
|
||||||
|
@ -656,16 +664,18 @@ struct GlobalObjectId {
|
||||||
{
|
{
|
||||||
NSData *d, *globalObjId;
|
NSData *d, *globalObjId;
|
||||||
NSArray *attachmentKeys;
|
NSArray *attachmentKeys;
|
||||||
NSMutableString *s;
|
|
||||||
|
|
||||||
uint32_t v;
|
|
||||||
NSString *p;
|
|
||||||
|
|
||||||
id value;
|
|
||||||
|
|
||||||
iCalCalendar *calendar;
|
iCalCalendar *calendar;
|
||||||
|
NSString *p, *subtype;
|
||||||
|
NSMutableString *s;
|
||||||
|
id value;
|
||||||
|
|
||||||
int preferredBodyType, nativeBodyType;
|
int preferredBodyType, mimeSupport, nativeBodyType;
|
||||||
|
uint32_t v;
|
||||||
|
|
||||||
|
subtype = [[[self bodyStructure] valueForKey: @"subtype"] lowercaseString];
|
||||||
|
|
||||||
|
preferredBodyType = [[context objectForKey: @"BodyPreferenceType"] intValue];
|
||||||
|
mimeSupport = [[context objectForKey: @"MIMESupport"] intValue];
|
||||||
|
|
||||||
s = [NSMutableString string];
|
s = [NSMutableString string];
|
||||||
|
|
||||||
|
@ -862,7 +872,12 @@ struct GlobalObjectId {
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
// MesssageClass and ContentClass
|
// MesssageClass and ContentClass
|
||||||
[s appendFormat: @"<MessageClass xmlns=\"Email:\">%@</MessageClass>", @"IPM.Note"];
|
if ([subtype isEqualToString: @"signed"])
|
||||||
|
[s appendFormat: @"<MessageClass xmlns=\"Email:\">%@</MessageClass>", @"IPM.Note.SMIME.MultipartSigned"];
|
||||||
|
else if ([subtype isEqualToString: @"pkcs7-mime"])
|
||||||
|
[s appendFormat: @"<MessageClass xmlns=\"Email:\">%@</MessageClass>", @"IPM.Note.SMIME"];
|
||||||
|
else
|
||||||
|
[s appendFormat: @"<MessageClass xmlns=\"Email:\">%@</MessageClass>", @"IPM.Note"];
|
||||||
[s appendFormat: @"<ContentClass xmlns=\"Email:\">%@</ContentClass>", @"urn:content-classes:message"];
|
[s appendFormat: @"<ContentClass xmlns=\"Email:\">%@</ContentClass>", @"urn:content-classes:message"];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -876,10 +891,8 @@ struct GlobalObjectId {
|
||||||
[s appendFormat: @"<InternetCPID xmlns=\"Email:\">%@</InternetCPID>", @"65001"];
|
[s appendFormat: @"<InternetCPID xmlns=\"Email:\">%@</InternetCPID>", @"65001"];
|
||||||
|
|
||||||
// Body - namespace 17
|
// Body - namespace 17
|
||||||
preferredBodyType = [[context objectForKey: @"BodyPreferenceType"] intValue];
|
|
||||||
|
|
||||||
nativeBodyType = 1;
|
nativeBodyType = 1;
|
||||||
d = [self _preferredBodyDataUsingType: preferredBodyType nativeType: &nativeBodyType];
|
d = [self _preferredBodyDataUsingType: preferredBodyType mimeSupport: mimeSupport nativeType: &nativeBodyType];
|
||||||
|
|
||||||
if (calendar && !d)
|
if (calendar && !d)
|
||||||
{
|
{
|
||||||
|
@ -981,9 +994,12 @@ struct GlobalObjectId {
|
||||||
{
|
{
|
||||||
[s appendString: @"<Body xmlns=\"AirSyncBase:\">"];
|
[s appendString: @"<Body xmlns=\"AirSyncBase:\">"];
|
||||||
|
|
||||||
// Set the correct type if client requested text/html but we got text/plain
|
// Set the correct type if client requested text/html but we got text/plain.
|
||||||
|
// For s/mime mails type is always 4 if mimeSupport is 1 or 2.
|
||||||
if (preferredBodyType == 2 && nativeBodyType == 1)
|
if (preferredBodyType == 2 && nativeBodyType == 1)
|
||||||
[s appendString: @"<Type>1</Type>"];
|
[s appendString: @"<Type>1</Type>"];
|
||||||
|
else if (([subtype isEqualToString: @"signed"] || [subtype isEqualToString: @"pkcs7-mime"] ) && mimeSupport > 0)
|
||||||
|
[s appendString: @"<Type>4</Type>"];
|
||||||
else
|
else
|
||||||
[s appendFormat: @"<Type>%d</Type>", preferredBodyType];
|
[s appendFormat: @"<Type>%d</Type>", preferredBodyType];
|
||||||
|
|
||||||
|
@ -1001,7 +1017,8 @@ struct GlobalObjectId {
|
||||||
|
|
||||||
// Attachments -namespace 16
|
// Attachments -namespace 16
|
||||||
attachmentKeys = [self fetchFileAttachmentKeys];
|
attachmentKeys = [self fetchFileAttachmentKeys];
|
||||||
if ([attachmentKeys count])
|
|
||||||
|
if ([attachmentKeys count] && !([subtype isEqualToString: @"signed"]))
|
||||||
{
|
{
|
||||||
int i;
|
int i;
|
||||||
|
|
||||||
|
|
1
NEWS
1
NEWS
|
@ -2,6 +2,7 @@
|
||||||
------------------
|
------------------
|
||||||
|
|
||||||
New features
|
New features
|
||||||
|
- initial S/MIME support for EAS (#3327)
|
||||||
|
|
||||||
Enhancements
|
Enhancements
|
||||||
- we no longer always entirely rewrite messages for Outlook 2013 when using EAS
|
- we no longer always entirely rewrite messages for Outlook 2013 when using EAS
|
||||||
|
|
|
@ -389,6 +389,10 @@ static BOOL debugSoParts = NO;
|
||||||
[[[info valueForKey: @"subtype"] lowercaseString] isEqualToString: @"calendar"])
|
[[[info valueForKey: @"subtype"] lowercaseString] isEqualToString: @"calendar"])
|
||||||
return info;
|
return info;
|
||||||
|
|
||||||
|
if ([[[info valueForKey: @"type"] lowercaseString] isEqualToString: @"application"] &&
|
||||||
|
[[[info valueForKey: @"subtype"] lowercaseString] isEqualToString: @"pkcs7-mime"])
|
||||||
|
return info;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
For each path component, eg 1,1,3
|
For each path component, eg 1,1,3
|
||||||
|
|
||||||
|
@ -810,7 +814,7 @@ static BOOL debugSoParts = NO;
|
||||||
NSMutableDictionary *currentPart;
|
NSMutableDictionary *currentPart;
|
||||||
NSString *newPath;
|
NSString *newPath;
|
||||||
NSArray *subparts;
|
NSArray *subparts;
|
||||||
NSString *type;
|
NSString *type, *subtype;
|
||||||
NSUInteger i;
|
NSUInteger i;
|
||||||
|
|
||||||
type = [[part objectForKey: @"type"] lowercaseString];
|
type = [[part objectForKey: @"type"] lowercaseString];
|
||||||
|
@ -833,7 +837,15 @@ static BOOL debugSoParts = NO;
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
if (!path)
|
if (!path)
|
||||||
path = @"1";
|
{
|
||||||
|
path = @"1";
|
||||||
|
|
||||||
|
// We set the path to 0 in case of a smime mail if not provided.
|
||||||
|
subtype = [[part objectForKey: @"subtype"] lowercaseString];
|
||||||
|
if ([subtype isEqualToString: @"pkcs7-mime"])
|
||||||
|
path = @"0";
|
||||||
|
}
|
||||||
|
|
||||||
[self _fetchFileAttachmentKey: part
|
[self _fetchFileAttachmentKey: part
|
||||||
intoArray: keys
|
intoArray: keys
|
||||||
withPath: path
|
withPath: path
|
||||||
|
|
Loading…
Reference in New Issue