diff --git a/ActiveSync/NGMimeMessage+ActiveSync.m b/ActiveSync/NGMimeMessage+ActiveSync.m
index 85fec63cd..f3c11cd96 100644
--- a/ActiveSync/NGMimeMessage+ActiveSync.m
+++ b/ActiveSync/NGMimeMessage+ActiveSync.m
@@ -1,6 +1,6 @@
/*
-Copyright (c) 2014, Inverse inc.
+Copyright (c) 2015, Inverse inc.
All rights reserved.
Redistribution and use in source and binary forms, with or without
@@ -40,35 +40,38 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
@implementation NGMimeMessage (ActiveSync)
-- (NSArray *) allRecipients
+- (void) _addRecipients: (NSEnumerator *) enumerator
+ toArray: (NSMutableArray *) recipients
{
- NSEnumerator *enumerator, *addressList;
- NSMutableArray *recipients;
NGMailAddressParser *parser;
+ NSEnumerator *addressList;
NGMailAddress *address;
NSString *s;
+ while ((s = [enumerator nextObject]))
+ {
+ parser = [NGMailAddressParser mailAddressParserWithString: s];
+ addressList = [[parser parseAddressList] objectEnumerator];
+
+ while ((address = [addressList nextObject]))
+ [recipients addObject: [address address]];
+ }
+}
+
+- (NSArray *) allRecipients
+{
+ NSMutableArray *recipients;
+
recipients = [NSMutableArray array];
- enumerator = [[self headersForKey: @"to"] objectEnumerator];
- while ((s = [enumerator nextObject]))
- {
- parser = [NGMailAddressParser mailAddressParserWithString: s];
- addressList = [[parser parseAddressList] objectEnumerator];
-
- while ((address = [addressList nextObject]))
- [recipients addObject: [address address]];
- }
+ [self _addRecipients: [[self headersForKey: @"to"] objectEnumerator]
+ toArray: recipients];
- enumerator = [[self headersForKey: @"cc"] objectEnumerator];
- while ((s = [enumerator nextObject]))
- {
- parser = [NGMailAddressParser mailAddressParserWithString: s];
- addressList = [[parser parseAddressList] objectEnumerator];
-
- while ((address = [addressList nextObject]))
- [recipients addObject: [address address]];
- }
+ [self _addRecipients: [[self headersForKey: @"cc"] objectEnumerator]
+ toArray: recipients];
+
+ [self _addRecipients: [[self headersForKey: @"bcc"] objectEnumerator]
+ toArray: recipients];
return recipients;
}
diff --git a/ActiveSync/NSString+ActiveSync.h b/ActiveSync/NSString+ActiveSync.h
index 172d71eb1..4aad558a2 100644
--- a/ActiveSync/NSString+ActiveSync.h
+++ b/ActiveSync/NSString+ActiveSync.h
@@ -49,6 +49,8 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- (NSString *) deviceType;
- (NSString *) attachmentName;
- (NSString *) command;
+- (NSString *) collectionid;
+- (NSString *) itemid;
- (NSData *) convertHexStringToBytes;
@end
diff --git a/ActiveSync/NSString+ActiveSync.m b/ActiveSync/NSString+ActiveSync.m
index 01f593352..6aa054d10 100644
--- a/ActiveSync/NSString+ActiveSync.m
+++ b/ActiveSync/NSString+ActiveSync.m
@@ -298,6 +298,31 @@ static NSArray *easCommandParameters = nil;
return s;
}
+- (NSString *) itemid
+{
+ NSString *s;
+
+ s = [self _valueForParameter: @"ITEMID="];
+
+ if (!s)
+ s = @"Unknown";
+
+ return s;
+}
+
+- (NSString *) collectionid
+{
+ NSString *s;
+
+ s = [self _valueForParameter: @"COLLECTIONID="];
+
+ if (!s)
+ s = @"Unknown";
+
+ return s;
+}
+
+
//
// FIXME: combine with our OpenChange code.
//
diff --git a/ActiveSync/SOGoActiveSyncDispatcher+Sync.m b/ActiveSync/SOGoActiveSyncDispatcher+Sync.m
index 2c353f736..cdd4cd9d1 100644
--- a/ActiveSync/SOGoActiveSyncDispatcher+Sync.m
+++ b/ActiveSync/SOGoActiveSyncDispatcher+Sync.m
@@ -540,6 +540,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
{
NSMutableDictionary *folderMetadata, *dateCache, *syncCache;
+ NSString *davCollectionTagToStore;
NSAutoreleasePool *pool;
NSMutableString *s;
@@ -618,6 +619,8 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
more_available = NO;
+ davCollectionTagToStore = [theCollection davCollectionTag];
+
switch (theFolderType)
{
// Handle all the GCS components
@@ -788,7 +791,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
else
{
[folderMetadata removeObjectForKey: @"MoreAvailable"];
- [folderMetadata setObject: [theCollection davCollectionTag] forKey: @"SyncKey"];
+ [folderMetadata setObject: davCollectionTagToStore forKey: @"SyncKey"];
}
[self _setFolderMetadata: folderMetadata
@@ -963,7 +966,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
else
{
[folderMetadata removeObjectForKey: @"MoreAvailable"];
- [folderMetadata setObject: [theCollection davCollectionTag] forKey: @"SyncKey"];
+ [folderMetadata setObject: davCollectionTagToStore forKey: @"SyncKey"];
}
[self _setFolderMetadata: folderMetadata forKey: [self _getNameInCache: theCollection withType: theFolderType]];
@@ -1211,7 +1214,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
{
// Make sure that client is updated with the right syncKey. - This keeps vtodo's and vevent's syncKey in sync.
syncKeyInCache = [folderMetadata objectForKey: @"SyncKey"];
- if (syncKeyInCache && !([davCollectionTag isEqualToString:syncKeyInCache]))
+ if (syncKeyInCache && !([davCollectionTag isEqualToString:syncKeyInCache]) && ![davCollectionTag isEqualToString: @"-1"])
{
davCollectionTag = syncKeyInCache;
*changeDetected = YES;
@@ -1431,8 +1434,9 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
}
}
- // Only send a response if there are changes otherwise send an empty response.
- if (changeDetected)
+
+ // Only send a response if there are changes or MS-ASProtocolVersion is either 2.5 or 12.0 oterwise send an empty response.
+ if (changeDetected || [[[context request] headerForKey: @"MS-ASProtocolVersion"] isEqualToString: @"2.5"] || [[[context request] headerForKey: @"MS-ASProtocolVersion"] isEqualToString: @"12.0"])
{
// We always return the last generated response.
// If we only return ,
diff --git a/ActiveSync/SOGoActiveSyncDispatcher.m b/ActiveSync/SOGoActiveSyncDispatcher.m
index ff9321f92..fc73a7b5e 100644
--- a/ActiveSync/SOGoActiveSyncDispatcher.m
+++ b/ActiveSync/SOGoActiveSyncDispatcher.m
@@ -1127,6 +1127,8 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
id currentCollection;
NSMutableString *s;
NSData *d;
+ NSArray *allCollections;
+ int j;
SOGoMicrosoftActiveSyncFolderType folderType;
int status, count;
@@ -1135,66 +1137,72 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
status = 1;
count = 0;
- collectionId = [[(id)[theDocumentElement getElementsByTagName: @"CollectionId"] lastObject] textValue];
- realCollectionId = [collectionId realCollectionIdWithFolderType: &folderType];
-
- if (folderType == ActiveSyncMailFolder)
- nameInCache = [NSString stringWithFormat: @"folder%@", realCollectionId];
- else
- nameInCache = collectionId;
-
- realCollectionId = [self globallyUniqueIDToIMAPFolderName: realCollectionId type: folderType];
-
- currentCollection = [self collectionFromId: realCollectionId type: folderType];
-
- //
- // For IMAP, we simply build a request like this:
- //
- // . UID SORT (SUBJECT) UTF-8 SINCE 1-Jan-2014 NOT DELETED
- // * SORT 124576 124577 124579 124578
- // . OK Completed (4 msgs in 0.000 secs)
- //
- if (folderType == ActiveSyncMailFolder)
- {
- EOQualifier *notDeletedQualifier, *sinceDateQualifier;
- EOAndQualifier *qualifier;
- NSCalendarDate *filter;
- NSArray *uids;
-
- filter = [NSCalendarDate dateFromFilterType: [[(id)[theDocumentElement getElementsByTagName: @"FilterType"] lastObject] textValue]];
-
- notDeletedQualifier = [EOQualifier qualifierWithQualifierFormat:
- @"(not (flags = %@))",
- @"deleted"];
- sinceDateQualifier = [EOQualifier qualifierWithQualifierFormat:
- @"(DATE >= %@)", filter];
-
- qualifier = [[EOAndQualifier alloc] initWithQualifiers: notDeletedQualifier, sinceDateQualifier,
- nil];
- AUTORELEASE(qualifier);
-
- uids = [currentCollection fetchUIDsMatchingQualifier: qualifier
- sortOrdering: @"REVERSE ARRIVAL"
- threaded: NO];
- count = [uids count];
-
- // Add the number of UIDs expected to "soft delete"
- count += [self _softDeleteCountWithFilter: filter collectionId: nameInCache];
-
- }
- else
- {
- count = [[currentCollection toOneRelationshipKeys] count];
- }
-
[s appendString: @""];
[s appendString: @""];
- [s appendFormat: @"%d", status];
+ [s appendString: @""];
+
+ allCollections = (id)[theDocumentElement getElementsByTagName: @"Collection"];
+
+ for (j = 0; j < [allCollections count]; j++)
+ {
+ collectionId = [[(id)[[allCollections objectAtIndex: j] getElementsByTagName: @"CollectionId"] lastObject] textValue];
+ realCollectionId = [collectionId realCollectionIdWithFolderType: &folderType];
+
+ if (folderType == ActiveSyncMailFolder)
+ nameInCache = [NSString stringWithFormat: @"folder%@", realCollectionId];
+ else
+ nameInCache = collectionId;
+
+ realCollectionId = [self globallyUniqueIDToIMAPFolderName: realCollectionId type: folderType];
+
+ currentCollection = [self collectionFromId: realCollectionId type: folderType];
- [s appendFormat: @"%@", collectionId];
- [s appendFormat: @"%d", count];
+ //
+ // For IMAP, we simply build a request like this:
+ //
+ // . UID SORT (SUBJECT) UTF-8 SINCE 1-Jan-2014 NOT DELETED
+ // * SORT 124576 124577 124579 124578
+ // . OK Completed (4 msgs in 0.000 secs)
+ //
+ if (folderType == ActiveSyncMailFolder)
+ {
+ NSCalendarDate *filter;
+ NSString *syncKey;
+ NSArray *allMessages;
+
+ filter = [NSCalendarDate dateFromFilterType: [[(id)[[allCollections objectAtIndex: j] getElementsByTagName: @"FilterType"] lastObject] textValue]];
+ syncKey = [[(id)[[allCollections objectAtIndex: j] getElementsByTagName: @"SyncKey"] lastObject] textValue];
+
+ allMessages = [currentCollection syncTokenFieldsWithProperties: nil matchingSyncToken: syncKey fromDate: filter];
+
+ count = [allMessages count];
+
+ // Add the number of UIDs expected to "soft delete"
+ count += [self _softDeleteCountWithFilter: filter collectionId: nameInCache];
+ }
+ else
+ {
+ count = [[currentCollection toOneRelationshipKeys] count];
+ }
+
+
+ [s appendString: @""];
+ [s appendFormat: @"%d", status];
+
+ if (folderType == ActiveSyncMailFolder)
+ [s appendString: @"Email"];
+ else if (folderType == ActiveSyncContactFolder)
+ [s appendString: @"Contacts"];
+ else if (folderType == ActiveSyncEventFolder)
+ [s appendString: @"Calendar"];
+ else if (folderType == ActiveSyncTaskFolder)
+ [s appendString: @"Tasks"];
+
+ [s appendFormat: @"%@",collectionId];
+ [s appendFormat: @"%d", count];
+ }
- [s appendString: @""];
+ [s appendString: @""];
d = [[s dataUsingEncoding: NSUTF8StringEncoding] xml2wbxml];
@@ -1929,6 +1937,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
[s appendString: @""];
[s appendString: @""];
[s appendString: @""];
+ [s appendString: @"1"];
[s appendString: @""];
d = [[s dataUsingEncoding: NSUTF8StringEncoding] xml2wbxml];
@@ -2254,7 +2263,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
error = [self _sendMail: data
recipients: [message allRecipients]
- saveInSentItems: ([(id)[theDocumentElement getElementsByTagName: @"SaveInSentItems"] count] ? YES : NO)];
+ saveInSentItems: ([(id)[theDocumentElement getElementsByTagName: @"SaveInSentItems"] count] ? YES : NO)];
if (error)
{
@@ -2345,7 +2354,21 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
id value;
folderId = [[(id)[theDocumentElement getElementsByTagName: @"FolderId"] lastObject] textValue];
+
+ // if folderId is not there try to get it from URL
+ if (!folderId)
+ {
+ folderId = [[[context request] uri] collectionid];
+ }
+
itemId = [[(id)[theDocumentElement getElementsByTagName: @"ItemId"] lastObject] textValue];
+
+ // if itemId is not there try to get it from URL
+ if (!itemId)
+ {
+ itemId = [[[context request] uri] itemid];
+ }
+
realCollectionId = [folderId realCollectionIdWithFolderType: &folderType];
realCollectionId = [self globallyUniqueIDToIMAPFolderName: realCollectionId type: folderType];
@@ -2543,7 +2566,9 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
// If the MS-ASProtocolVersion header is set to "12.1", the body of the SendMail request is
// is a "message/rfc822" payload - otherwise, it's a WBXML blob.
//
- if ([cmdName caseInsensitiveCompare: @"SendMail"] == NSOrderedSame &&
+ if (([cmdName caseInsensitiveCompare: @"SendMail"] == NSOrderedSame ||
+ [cmdName caseInsensitiveCompare: @"SmartReply"] == NSOrderedSame ||
+ [cmdName caseInsensitiveCompare: @"SmartForward"] == NSOrderedSame) &&
[[theRequest headerForKey: @"content-type"] caseInsensitiveCompare: @"message/rfc822"] == NSOrderedSame)
{
NSString *s, *xml;
@@ -2561,7 +2586,9 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
s = [theRequest contentAsString];
}
- xml = [NSString stringWithFormat: @"%@", [s stringByEncodingBase64]];
+ xml = [NSString stringWithFormat: @"<%@ xmlns=\"ComposeMail:\">%@%@>", cmdName, [s stringByEncodingBase64], cmdName];
+
+
d = [xml dataUsingEncoding: NSASCIIStringEncoding];
}
diff --git a/ActiveSync/SOGoMailObject+ActiveSync.m b/ActiveSync/SOGoMailObject+ActiveSync.m
index 6cc06e3d0..45c865c84 100644
--- a/ActiveSync/SOGoMailObject+ActiveSync.m
+++ b/ActiveSync/SOGoMailObject+ActiveSync.m
@@ -47,6 +47,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#import
#import
#import
+#import
#import
#import
@@ -57,6 +58,10 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#import
#import
+#import
+
+#import
+
#include "iCalTimeZone+ActiveSync.h"
#include "NSData+ActiveSync.h"
#include "NSDate+ActiveSync.h"
@@ -204,6 +209,7 @@ struct GlobalObjectId {
//
//
- (NSData *) _preferredBodyDataInMultipartUsingType: (int) theType
+ nativeTypeFound: (int *) theNativeTypeFound
{
NSString *encoding, *key, *plainKey, *htmlKey, *type, *subtype;
NSDictionary *textParts, *part;
@@ -222,6 +228,10 @@ struct GlobalObjectId {
type = [part valueForKey: @"type"];
subtype = [part valueForKey: @"subtype"];
+ // Don't select an attachment as body
+ if ([[[part valueForKey: @"disposition"] valueForKey: @"type"] isEqualToString: @"attachment"])
+ continue;
+
if ([type isEqualToString: @"text"] && [subtype isEqualToString: @"html"])
htmlKey = key;
else if ([type isEqualToString: @"text"] && [subtype isEqualToString: @"plain"])
@@ -229,11 +239,22 @@ struct GlobalObjectId {
}
key = nil;
+ *theNativeTypeFound = 1;
- if (theType == 2)
- key = htmlKey;
- else if (theType == 1)
+ if (theType == 2 && htmlKey)
+ {
+ key = htmlKey;
+ *theNativeTypeFound = 2;
+ }
+ else if (theType == 1 && plainKey)
key = plainKey;
+ else if (theType == 2 && plainKey)
+ key = plainKey;
+ else if (theType == 1 && htmlKey)
+ {
+ key = htmlKey;
+ *theNativeTypeFound = 2;
+ }
if (key)
{
@@ -251,9 +272,17 @@ struct GlobalObjectId {
charset = [[[self lookupInfoForBodyPart: key] objectForKey: @"parameterList"] objectForKey: @"charset"];
if (![charset length])
- charset = @"us-ascii";
+ charset = @"utf-8";
s = [NSString stringWithData: d usingEncodingNamed: charset];
+
+ // We fallback to ISO-8859-1 string encoding
+ if (!s)
+ s = [[[NSString alloc] initWithData: d encoding: NSISOLatin1StringEncoding] autorelease];
+
+ if (theType == 1 && *theNativeTypeFound == 2)
+ s = [s htmlToText];
+
d = [s dataUsingEncoding: NSUTF8StringEncoding];
}
@@ -298,7 +327,7 @@ struct GlobalObjectId {
[[[thePart contentType] type] isEqualToString: @"text"] &&
([[[thePart contentType] subType] isEqualToString: @"plain"] || [[[thePart contentType] subType] isEqualToString: @"html"]))
{
- // We make sure everything is encoded in UTF-8
+ // We make sure everything is encoded in UTF-8.
NGMimeType *mimeType;
NSString *s;
@@ -309,9 +338,13 @@ struct GlobalObjectId {
charset = [[thePart contentType] valueOfParameter: @"charset"];
if (![charset length])
- charset = @"us-ascii";
+ charset = @"utf-8";
- s = [NSString stringWithData: body usingEncodingNamed: charset];
+ s = [NSString stringWithData: body usingEncodingNamed: charset];
+
+ // We fallback to ISO-8859-1 string encoding. We avoid #3103.
+ if (!s)
+ s = [[[NSString alloc] initWithData: body encoding: NSISOLatin1StringEncoding] autorelease];
}
else
{
@@ -414,7 +447,7 @@ struct GlobalObjectId {
charset = [[[self lookupInfoForBodyPart: @""] objectForKey: @"parameterList"] objectForKey: @"charset"];
if (![charset length])
- charset = @"us-ascii";
+ charset = @"utf-8";
d = [[self fetchPlainTextParts] objectForKey: @""];
@@ -428,25 +461,30 @@ struct GlobalObjectId {
d = [d dataByDecodingQuotedPrintableTransferEncoding];
s = [NSString stringWithData: d usingEncodingNamed: charset];
+
+ // We fallback to ISO-8859-1 string encoding. We avoid #3103.
+ if (!s)
+ s = [[[NSString alloc] initWithData: d encoding: NSISOLatin1StringEncoding] autorelease];
// Check if we must convert html->plain
if (theType == 1 && [subtype isEqualToString: @"html"])
{
s = [s htmlToText];
}
-
+
d = [s dataUsingEncoding: NSUTF8StringEncoding];
}
else if ([type isEqualToString: @"multipart"])
{
- d = [self _preferredBodyDataInMultipartUsingType: theType];
+ d = [self _preferredBodyDataInMultipartUsingType: theType nativeTypeFound: theNativeType];
}
}
else if (theType == 4)
{
- // We sanitize the content *ONLY* for Outlook clients. 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.
- if ([[context objectForKey: @"DeviceType"] isEqualToString: @"WindowsOutlook15"])
+ encoding = [[self lookupInfoForBodyPart: @""] objectForKey: @"encoding"];
+ if ([[context objectForKey: @"DeviceType"] isEqualToString: @"WindowsOutlook15"] || ([encoding caseInsensitiveCompare: @"8bit"] == NSOrderedSame))
d = [self _sanitizedMIMEMessage];
else
d = [self content];
@@ -512,6 +550,10 @@ struct GlobalObjectId {
NSData *d, *globalObjId;
NSArray *attachmentKeys;
NSMutableString *s;
+
+ uint32_t v;
+ NSString *p;
+
id value;
iCalCalendar *calendar;
@@ -552,8 +594,25 @@ struct GlobalObjectId {
if (value)
[s appendFormat: @"%@", [value activeSyncRepresentationInContext: context]];
- // Importance - FIXME
- [s appendFormat: @"%@", @"1"];
+ // Importance
+ v = 0x1;
+ p = [[self mailHeaders] objectForKey: @"x-priority"];
+
+ if (p)
+ {
+ if ([p hasPrefix: @"1"]) v = 0x2;
+ else if ([p hasPrefix: @"2"]) v = 0x2;
+ else if ([p hasPrefix: @"4"]) v = 0x0;
+ else if ([p hasPrefix: @"5"]) v = 0x0;
+ }
+ else
+ {
+ p = [[self mailHeaders] objectForKey: @"importance"];
+ if ([p hasPrefix: @"High"]) v = 0x2;
+ else if ([p hasPrefix: @"Low"]) v = 0x0;
+ }
+
+ [s appendFormat: @"%d", v];
// Read
[s appendFormat: @"%d", ([self read] ? 1 : 0)];
@@ -728,17 +787,31 @@ struct GlobalObjectId {
len = [content length];
- [s appendString: @""];
- [s appendFormat: @"%d", preferredBodyType];
- [s appendFormat: @"%d", truncated];
- [s appendFormat: @""];
-
- if (!truncated)
+ if ([[[context request] headerForKey: @"MS-ASProtocolVersion"] isEqualToString: @"2.5"])
{
- [s appendFormat: @"%@", content];
- [s appendFormat: @"%d", len];
+ [s appendFormat: @"%@", content];
+ [s appendFormat: @"%d", truncated];
}
- [s appendString: @""];
+ else
+ {
+ [s appendString: @""];
+
+ // Set the correct type if client requested text/html but we got text/plain
+ if (preferredBodyType == 2 && nativeBodyType == 1)
+ [s appendString: @"1"];
+ else
+ [s appendFormat: @"%d", preferredBodyType];
+
+ [s appendFormat: @"%d", truncated];
+ [s appendFormat: @""];
+
+ if (!truncated)
+ {
+ [s appendFormat: @"%@", content];
+ [s appendFormat: @"%d", len];
+ }
+ [s appendString: @""];
+ }
}
// Attachments -namespace 16
@@ -746,9 +819,12 @@ struct GlobalObjectId {
if ([attachmentKeys count])
{
int i;
-
- [s appendString: @""];
-
+
+ if ([[[context request] headerForKey: @"MS-ASProtocolVersion"] isEqualToString: @"2.5"])
+ [s appendString: @""];
+ else
+ [s appendString: @""];
+
for (i = 0; i < [attachmentKeys count]; i++)
{
value = [attachmentKeys objectAtIndex: i];
@@ -759,11 +835,22 @@ struct GlobalObjectId {
// FileReference must be a unique identifier across the whole store. We use the following structure:
// mail//
// mail/INBOX/2
- [s appendFormat: @"mail/%@/%@/%@", [[[self container] relativeImap4Name] stringByEscapingURL], [self nameInContainer], [value objectForKey: @"path"]];
+ if ([[[context request] headerForKey: @"MS-ASProtocolVersion"] isEqualToString: @"2.5"])
+ [s appendFormat: @"mail/%@/%@/%@", [[[self container] relativeImap4Name] stringByEscapingURL], [self nameInContainer], [value objectForKey: @"path"]];
+ else
+ [s appendFormat: @"mail/%@/%@/%@", [[[self container] relativeImap4Name] stringByEscapingURL], [self nameInContainer], [value objectForKey: @"path"]];
- [s appendFormat: @"%d", 1]; // See: http://msdn.microsoft.com/en-us/library/ee160322(v=exchg.80).aspx
- [s appendFormat: @"%d", [[value objectForKey: @"size"] intValue]];
- //[s appendFormat: @"%d", 1];
+ if ([[[context request] headerForKey: @"MS-ASProtocolVersion"] isEqualToString: @"2.5"])
+ {
+ [s appendFormat: @"%d", 1];
+ [s appendFormat: @"%d", [[value objectForKey: @"size"] intValue]];
+ }
+ else
+ {
+ [s appendFormat: @"%d", 1]; // See: http://msdn.microsoft.com/en-us/library/ee160322(v=exchg.80).aspx
+ [s appendFormat: @"%d", [[value objectForKey: @"size"] intValue]];
+ //[s appendFormat: @"%d", 1];
+ }
[s appendString: @""];
}
@@ -774,6 +861,27 @@ struct GlobalObjectId {
[s appendString: @""];
[s appendFormat: @"%d", ([self flagged] ? 2 : 0)];
[s appendString: @""];
+
+
+ // Categroies/Labels
+ NSEnumerator *categories;
+ categories = [[[self fetchCoreInfos] objectForKey: @"flags"] objectEnumerator];
+
+ if (categories)
+ {
+ NSString *currentFlag;
+ NSDictionary *v;
+
+ v = [[[context activeUser] userDefaults] mailLabelsColors];
+
+ [s appendFormat: @""];
+ while ((currentFlag = [categories nextObject]))
+ {
+ if ([[v objectForKey: currentFlag] objectAtIndex:0])
+ [s appendFormat: @"%@", [[[v objectForKey: currentFlag] objectAtIndex:0] activeSyncRepresentationInContext: context]];
+ }
+ [s appendFormat: @""];
+ }
// FIXME - support these in the future
//[s appendString: @"foobar"];
@@ -836,6 +944,65 @@ struct GlobalObjectId {
else
[self removeFlags: @"seen"];;
}
+
+ if ((o = [theValues objectForKey: @"Categories"]))
+ {
+ NSEnumerator *categories;
+ NSString *currentFlag;
+ NSDictionary *v;
+
+ v = [[[context activeUser] userDefaults] mailLabelsColors];
+
+ // add categories/labels sent from client
+ if ([o isKindOfClass: [NSArray class]])
+ {
+ NSEnumerator *enumerator;
+ NSMutableArray *labels;
+ NSEnumerator *flags;
+ id key;
+
+ labels = [NSMutableArray array];
+
+ enumerator = [v keyEnumerator];
+ flags = [o objectEnumerator];
+
+ while ((currentFlag = [flags nextObject]))
+ {
+ while ((key = [enumerator nextObject]))
+ {
+ if (([currentFlag isEqualToString:[[v objectForKey:key] objectAtIndex:0]]))
+ {
+ [labels addObject: key];
+ break;
+ }
+ }
+ }
+
+ [self addFlags: [labels componentsJoinedByString: @" "]];
+ }
+
+ categories = [[[self fetchCoreInfos] objectForKey: @"flags"] objectEnumerator];
+
+ // remove all categories/labels from server which were not it the list sent from client
+ if (categories)
+ {
+ while ((currentFlag = [categories nextObject]))
+ {
+ // only deal with lables and don't touch flags like seen and flagged
+ if (([v objectForKey: currentFlag]))
+ {
+ if (![o isKindOfClass: [NSArray class]])
+ {
+ [self removeFlags: currentFlag];
+ }
+ else if (([o indexOfObject: [[v objectForKey:currentFlag] objectAtIndex:0]] == NSNotFound))
+ {
+ [self removeFlags: currentFlag];
+ }
+ }
+ }
+ }
+ }
}
@end
diff --git a/ActiveSync/iCalEvent+ActiveSync.m b/ActiveSync/iCalEvent+ActiveSync.m
index d1a4b6f8a..586bf5798 100644
--- a/ActiveSync/iCalEvent+ActiveSync.m
+++ b/ActiveSync/iCalEvent+ActiveSync.m
@@ -40,6 +40,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#import
#import
#import
+#import
#import
#import
@@ -274,11 +275,20 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
// It is very important here to NOT set 0 in the response,
// otherwise it'll prevent WP8 phones from sync'ing. See #3028 for details.
o = [o activeSyncRepresentationInContext: context];
- [s appendString: @""];
- [s appendFormat: @"%d", 1];
- [s appendFormat: @"%d", [o length]];
- [s appendFormat: @"%@", o];
- [s appendString: @""];
+
+ if ([[[context request] headerForKey: @"MS-ASProtocolVersion"] isEqualToString: @"2.5"])
+ {
+ [s appendFormat: @"%@", o];
+ [s appendString: @"0"];
+ }
+ else
+ {
+ [s appendString: @""];
+ [s appendFormat: @"%d", 1];
+ [s appendFormat: @"%d", [o length]];
+ [s appendFormat: @"%@", o];
+ [s appendString: @""];
+ }
}
[s appendFormat: @"%d", 1];
@@ -394,9 +404,17 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
}
// FIXME: merge with iCalToDo
- if ((o = [[theValues objectForKey: @"Body"] objectForKey: @"Data"]))
- [self setComment: o];
-
+ if ([[[context request] headerForKey: @"MS-ASProtocolVersion"] isEqualToString: @"2.5"])
+ {
+ if ((o = [theValues objectForKey: @"Body"]))
+ [self setComment: o];
+ }
+ else
+ {
+ if ((o = [[theValues objectForKey: @"Body"] objectForKey: @"Data"]))
+ [self setComment: o];
+ }
+
if ((o = [theValues objectForKey: @"Location"]))
[self setLocation: o];
diff --git a/ActiveSync/iCalToDo+ActiveSync.m b/ActiveSync/iCalToDo+ActiveSync.m
index 5f237a31b..e9417752c 100644
--- a/ActiveSync/iCalToDo+ActiveSync.m
+++ b/ActiveSync/iCalToDo+ActiveSync.m
@@ -38,6 +38,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#import
#import
#import
+#import
#import
#import
@@ -49,6 +50,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#include "NSDate+ActiveSync.h"
#include "NSString+ActiveSync.h"
+
@implementation iCalToDo (ActiveSync)
- (NSString *) activeSyncRepresentationInContext: (WOContext *) context
@@ -62,11 +64,10 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
s = [NSMutableString string];
// Complete
- o = [self completed];
- [s appendFormat: @"%d", (o ? 1 : 0)];
+ [s appendFormat: @"%d", [[self status] isEqualToString: @"COMPLETED"] ? 1 : 0];
// DateCompleted
- if (o)
+ if ((o = [self completed]))
[s appendFormat: @"%@", [o activeSyncRepresentationInContext: context]];
// Start date
@@ -97,6 +98,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
// Reminder - FIXME
[s appendFormat: @"%d", 0];
+ // Sensitivity
if ([[self accessClass] isEqualToString: @"PRIVATE"])
v = 2;
else if ([[self accessClass] isEqualToString: @"CONFIDENTIAL"])
@@ -104,6 +106,8 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
else
v = 0;
+ [s appendFormat: @"%d", v];
+
categories = [self categories];
if ([categories count])
@@ -126,11 +130,20 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
// It is very important here to NOT set 0 in the response,
// otherwise it'll prevent WP8 phones from sync'ing. See #3028 for details.
o = [o activeSyncRepresentationInContext: context];
- [s appendString: @""];
- [s appendFormat: @"%d", 1];
- [s appendFormat: @"%d", [o length]];
- [s appendFormat: @"%@", o];
- [s appendString: @""];
+
+ if ([[[context request] headerForKey: @"MS-ASProtocolVersion"] isEqualToString: @"2.5"])
+ {
+ [s appendFormat: @"%@", o];
+ [s appendString: @"0"];
+ }
+ else
+ {
+ [s appendString: @""];
+ [s appendFormat: @"%d", 1];
+ [s appendFormat: @"%d", [o length]];
+ [s appendFormat: @"%@", o];
+ [s appendString: @""];
+ }
}
return s;
@@ -152,9 +165,16 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
[self setSummary: o];
// FIXME: merge with iCalEvent
- if ((o = [[theValues objectForKey: @"Body"] objectForKey: @"Data"]))
- [self setComment: o];
-
+ if ([[[context request] headerForKey: @"MS-ASProtocolVersion"] isEqualToString: @"2.5"])
+ {
+ if ((o = [theValues objectForKey: @"Body"]))
+ [self setComment: o];
+ }
+ else
+ {
+ if ((o = [[theValues objectForKey: @"Body"] objectForKey: @"Data"]))
+ [self setComment: o];
+ }
if ([[theValues objectForKey: @"Complete"] intValue] &&
((o = [theValues objectForKey: @"DateCompleted"])) )
@@ -166,6 +186,11 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
[completed setDate: o];
[self setStatus: @"COMPLETED"];
}
+ else
+ {
+ [self setStatus: @"IN-PROCESS"];
+ [self setCompleted: nil];
+ }
if ((o = [theValues objectForKey: @"DueDate"]))
{
@@ -177,6 +202,16 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
[due setDateTime: o];
}
+ if ((o = [theValues objectForKey: @"StartDate"]))
+ {
+ iCalDateTime *due;
+
+ o = [o calendarDate];
+ due = (iCalDateTime *) [self uniqueChildWithTag: @"dtstart"];
+ [due setTimeZone: tz];
+ [due setDateTime: o];
+ }
+
// 2 == high, 1 == normal, 0 == low
if ((o = [theValues objectForKey: @"Importance"]))
{
diff --git a/ChangeLog b/ChangeLog
index ab90551ac..8c90ec341 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,592 @@
+commit f11bcd3373f39967b870f8d8f3358759a6221b62
+Author: Ludovic Marcotte
+Date: Thu Mar 26 13:54:17 2015 -0400
+
+ Avoid using stringByReplacing... since old runtimes don't support it
+
+M UI/MailerUI/UIxMailEditor.m
+M UI/Scheduler/UIxCalMainView.m
+
+commit b933f53fc2580bd5b9913f7afebcf6b2d9bedc53
+Author: Ludovic Marcotte
+Date: Tue Mar 24 10:56:26 2015 -0400
+
+ Changes for the 2.2.17 release
+
+M ChangeLog
+M Documentation/docinfo.xml
+M Documentation/includes/global-attributes.asciidoc
+M NEWS
+M Version
+
+commit 097d5c2333515092f03ace5e64fc6824682ba974
+Author: Ludovic Marcotte
+Date: Tue Mar 24 08:40:13 2015 -0400
+
+ now possible to configure objectClass names for LDAP groups using GroupObjectClasses (#1499)
+
+M Documentation/SOGoInstallationGuide.asciidoc
+M NEWS
+M SoObjects/SOGo/LDAPSource.h
+M SoObjects/SOGo/LDAPSource.m
+M SoObjects/SOGo/SOGoGroup.m
+M SoObjects/SOGo/SOGoSource.h
+
+commit d75fb0722a0ced5bb937e5b754e95cd0261df7a7
+Author: Ludovic Marcotte
+Date: Tue Mar 24 08:34:29 2015 -0400
+
+ Code cleanups
+
+M UI/Scheduler/UIxAttendeesEditor.h
+M UI/Scheduler/UIxAttendeesEditor.m
+M UI/Scheduler/UIxComponentEditor.h
+M UI/Scheduler/UIxComponentEditor.m
+
+commit 26d876e67a32e6c2dc36a3a92449aa0b6638ec41
+Author: Francis Lachapelle
+Date: Mon Mar 23 16:28:32 2015 -0400
+
+ Improve event attendees editor
+
+ - Avoid repetitive AJAX queries;
+ - Improve handling of daylight changes.
+
+M UI/WebServerResources/JavascriptAPIExtensions.js
+M UI/WebServerResources/UIxAttendeesEditor.js
+
+commit cc3652b59b40bcb420d806412277509ac247d85d
+Author: Ludovic Marcotte
+Date: Mon Mar 23 17:23:29 2015 -0400
+
+ improvements to EAS SyncKey handling to avoid missing mails (#3048, #3058)
+
+M ActiveSync/SOGoActiveSyncDispatcher+Sync.m
+M NEWS
+
+commit 9d7bbbc312cbe8ccb535e24308be111a3a2eb710
+Author: Ludovic Marcotte
+Date: Mon Mar 23 14:55:14 2015 -0400
+
+ enable by default HTML mails support using EAS on Windows and BB phones
+
+M ActiveSync/SOGoActiveSyncDispatcher.m
+M NEWS
+
+commit d2915add6d69a8fd05d15bb8c2ed979fe6ad9026
+Author: Ludovic Marcotte
+Date: Mon Mar 23 14:08:31 2015 -0400
+
+ now favor login@domain as the default email address if multiple mail: fields are specified
+
+M NEWS
+M SoObjects/SOGo/SOGoUser.h
+M SoObjects/SOGo/SOGoUser.m
+
+commit 67139ce3e3f69160a683d8c11bee439be202a84e
+Author: Ludovic Marcotte
+Date: Mon Mar 23 11:35:45 2015 -0400
+
+ improved GetItemEstimate to count all vasnished/deleted mails too
+
+M ActiveSync/SOGoActiveSyncDispatcher.m
+M NEWS
+
+commit 9eb138b867ebd0a51b9a649891422bac70406510
+Author: Ludovic Marcotte
+Date: Mon Mar 23 10:54:36 2015 -0400
+
+ prevent potential freebusy lookup crashes during timezone changes with repetitive events
+
+M NEWS
+M UI/MainUI/SOGoUserHomePage.m
+
+commit baf3d1f986d48d4fa37a076ccf555d75f4d5dbda
+Author: Ludovic Marcotte
+Date: Fri Mar 20 09:10:49 2015 -0400
+
+ immediately delete mails from EAS clients when they are marked as deleted on the IMAP server
+
+M NEWS
+M SoObjects/Mailer/SOGoMailFolder.m
+
+commit ecd336fa5c6513ee8533dae05e5a328c81527a38
+Author: Ludovic Marcotte
+Date: Fri Mar 20 08:51:35 2015 -0400
+
+ Fix compilation warning and error
+
+M ActiveSync/NGMimeMessage+ActiveSync.m
+M ActiveSync/SOGoMailObject+ActiveSync.m
+
+commit afd73289d1ebb13c643f7a8290eac7da43b69330
+Author: Ludovic Marcotte
+Date: Fri Mar 20 08:45:42 2015 -0400
+
+ properly fallback over EAS to UTF-8 and then Latin1 for messages w/o charset (#3103)
+
+M ActiveSync/SOGoMailObject+ActiveSync.m
+M NEWS
+
+commit bff21ab1ff136ada7d93cc529a87da9dd254fa60
+Author: Ludovic Marcotte
+Date: Thu Mar 19 16:08:33 2015 -0400
+
+ support for mail prority using EAS
+
+M ActiveSync/SOGoMailObject+ActiveSync.m
+M NEWS
+
+commit 6d99226b1390d2dce1f7dfc6a3bab5143aad6364
+Author: Ludovic Marcotte
+Date: Thu Mar 19 14:53:01 2015 -0400
+
+ Always add DisplayName no matter EAS client version
+
+M ActiveSync/SOGoMailObject+ActiveSync.m
+
+commit 69974807b5740f245376f8e19f71559997bcae44
+Author: Francis Lachapelle
+Date: Thu Mar 19 08:32:37 2015 -0400
+
+ Fix Language-Region tags in Web interface
+
+ Fixes #3121
+
+M NEWS
+M UI/MailerUI/UIxMailEditor.m
+M UI/Scheduler/UIxCalMainView.m
+
+commit cdb766e2eff3b6e420da48b3f396814edff96b4f
+Author: Ludovic Marcotte
+Date: Wed Mar 18 10:15:33 2015 -0400
+
+ Documented NGMimeBuildMimeTempDirectory
+
+M Documentation/SOGoInstallationGuide.asciidoc
+
+commit e25a7e3d8d0a5f11a65a6fa337a637b3e1f44432
+Author: Ludovic Marcotte
+Date: Wed Mar 18 09:36:35 2015 -0400
+
+ Fix for bug #3138
+
+M ActiveSync/NGMimeMessage+ActiveSync.m
+M ActiveSync/SOGoActiveSyncDispatcher.m
+M NEWS
+
+commit 1d5ae27c996f63488653d3a8638b33320540741c
+Author: Francis Lachapelle
+Date: Tue Mar 17 15:31:35 2015 -0400
+
+ Fix issues with freebusy (Web, busyOffHours)
+
+M NEWS
+M SoObjects/Appointments/SOGoFreeBusyObject.m
+M UI/WebServerResources/UIxAttendeesEditor.js
+
+commit 1bdd5dfc9085e1f08f839b8179a7ccb37b038e5c
+Author: Jens Erat
+Date: Fri Mar 13 14:06:58 2015 +0100
+
+ Remove obsolete archive string from translations
+
+M UI/MailerUI/Arabic.lproj/Localizable.strings
+M UI/MailerUI/Danish.lproj/Localizable.strings
+M UI/MailerUI/Icelandic.lproj/Localizable.strings
+M UI/MailerUI/Italian.lproj/Localizable.strings
+M UI/MailerUI/NorwegianNynorsk.lproj/Localizable.strings
+M UI/MailerUI/Swedish.lproj/Localizable.strings
+M UI/MailerUI/Ukrainian.lproj/Localizable.strings
+M UI/MailerUI/Welsh.lproj/Localizable.strings
+
+commit 37fbd75d552873575f11853e4478d6dce9640201
+Author: Jens Erat
+Date: Fri Mar 13 11:41:07 2015 +0100
+
+ Rename "Archive" to "Export" in Webmail, ctd.
+
+ Fixes #2758 also for non-Inbox folders
+
+M UI/Templates/MailerUI/UIxMailMainFrame.wox
+
+commit f1fda8bd0617d5e6b924bb1ae57fb7840bbcc13f
+Author: Francis Lachapelle
+Date: Tue Mar 10 13:27:17 2015 -0400
+
+ Return a single phone number in JSON contacts list
+
+M SoObjects/Contacts/SOGoContactSourceFolder.m
+
+commit 517f4b0f49e2545f930408b922dabc1a4a9006eb
+Author: Francis Lachapelle
+Date: Mon Mar 9 14:04:21 2015 -0400
+
+ Fix possible JS exception in Contacts module
+
+M NEWS
+
+commit cbba0719bb58229d9be8d3bb5af5fba23da3ae14
+Author: Francis Lachapelle
+Date: Wed Mar 4 10:05:12 2015 -0500
+
+ Fix exception in SOGoUserManager
+
+M SoObjects/SOGo/SOGoUserManager.m
+
+commit 8e0849029ea93bfc9e1f9e2cb4ff2e0e5b6a4308
+Author: Francis Lachapelle
+Date: Tue Mar 3 21:18:46 2015 -0500
+
+ Fix contacts lookup by UID
+
+ When looking for a specific contact UID, we no longer match a pattern
+ that could return multiple results. We search for the exact UID only.
+
+M NEWS
+M SoObjects/Appointments/SOGoFreeBusyObject.m
+M SoObjects/SOGo/SOGoUserManager.h
+M SoObjects/SOGo/SOGoUserManager.m
+
+commit 61923225136db72ceb89294d504b65a7f62e7a7f
+Author: Francis Lachapelle
+Date: Tue Mar 3 20:59:32 2015 -0500
+
+ Fix some gcc warnings
+
+M SoObjects/SOGo/SOGoCASSession.m
+M SoObjects/SOGo/SOGoUser.m
+M SoObjects/SOGo/SOGoUserManager.m
+
+commit df59b73a221467523c18c56493c13c86003d7271
+Author: Ludovic Marcotte
+Date: Thu Feb 26 17:56:42 2015 -0500
+
+ use correct mail attachment elements for EAS 2.5 clients - patch from tfu
+
+M ActiveSync/SOGoMailObject+ActiveSync.m
+M NEWS
+
+commit a5672fda5210e6149bd893683c7ee4338f0c3cf9
+Author: Ludovic Marcotte
+Date: Thu Feb 26 17:55:36 2015 -0500
+
+ do not use syncKey from cache when davCollectionTag = -1 - patch from tfu
+
+M ActiveSync/SOGoActiveSyncDispatcher+Sync.m
+M NEWS
+
+commit b593ddc6436e0d4aff2ef24e6ff9b3cda0c0c396
+Author: Ludovic Marcotte
+Date: Thu Feb 26 17:53:58 2015 -0500
+
+ SmartReply improvements for missing body attributes - patch from tfu
+
+M ActiveSync/NSString+ActiveSync.h
+M ActiveSync/NSString+ActiveSync.m
+M ActiveSync/SOGoActiveSyncDispatcher.m
+M NEWS
+
+commit 38d61f720f0c9352619fa2cb214e81734ed05868
+Author: Ludovic Marcotte
+Date: Thu Feb 26 17:52:37 2015 -0500
+
+ use the correct body element for events for EAS 2.5 clients - patch from tfu
+
+M ActiveSync/iCalEvent+ActiveSync.m
+M NEWS
+
+commit 733e334726824be7ab3a41ca7d4b885550a18f9a
+Author: Ludovic Marcotte
+Date: Thu Feb 26 17:51:02 2015 -0500
+
+ fixed tasks disappearing issue with RoadSync - patch from tfu
+
+M ActiveSync/iCalToDo+ActiveSync.m
+M NEWS
+
+commit 22888ba5ac2cdc8d52d4c8eba9a94936376b7520
+Author: Ludovic Marcotte
+Date: Thu Feb 26 17:49:26 2015 -0500
+
+ use the correct mail body element for EAS 2.5 clients - patch from tfu
+
+M ActiveSync/SOGoMailObject+ActiveSync.m
+M NEWS
+
+commit 33c485d2f6d58443c0c67297b87d8cc3d67d1398
+Author: Ludovic Marcotte
+Date: Thu Feb 26 17:48:06 2015 -0500
+
+ fixed empty sync responses for EAS 2.5 and 12.0 clients - patch from tfu
+
+M ActiveSync/SOGoActiveSyncDispatcher+Sync.m
+M NEWS
+
+commit 9160e34134c1c24f5f51cd6c9081723f0963e7c5
+Author: Ludovic Marcotte
+Date: Thu Feb 26 17:46:34 2015 -0500
+
+ multiple collections support for GetItemEstimate - patch from tfu
+
+M ActiveSync/SOGoActiveSyncDispatcher.m
+M NEWS
+
+commit 5ceddc213ec08ccb5b44b07aae47921a19b36fd8
+Author: Ludovic Marcotte
+Date: Thu Feb 26 16:00:03 2015 -0500
+
+ Fix for bug #3116
+
+M ActiveSync/SOGoMailObject+ActiveSync.m
+M NEWS
+
+commit b97002565e3d21b417f99b704641abee1052642c
+Author: Ludovic Marcotte
+Date: Wed Feb 25 08:42:18 2015 -0500
+
+ Updated NEWS file regarding previous commit
+
+M NEWS
+
+commit 8d21bc3e36014d63db0e37bf7121d81c8200b5e1
+Author: Jesús García Sáez
+Date: Wed Feb 25 12:01:31 2015 +0100
+
+ Fix change password with DomainBasedUID
+
+ When users use full domain to login (SOGoEnableDomainBasedUID) the
+ user attributes in the cache were not being properly updated because
+ in this case the key is `uid@domain` instead of just `uid`.
+
+M SoObjects/SOGo/SOGoUserManager.m
+
+commit b26ea30b1b794e983ea272b5de713b0205fda0d8
+Author: Ludovic Marcotte
+Date: Thu Feb 12 11:06:57 2015 -0500
+
+ Update ChangeLog
+
+M ChangeLog
+
+commit 72b210cd2ebccc8e9cdc13c23abb76b7f455b780
+Author: Ludovic Marcotte
+Date: Thu Feb 12 10:29:46 2015 -0500
+
+ Fixed typo
+
+M NEWS
+
+commit a80d75d09cd0b6f310455e33847ae62433e258ca
+Author: Ludovic Marcotte
+Date: Thu Feb 12 10:27:10 2015 -0500
+
+ Preparation for the release
+
+M Documentation/docinfo.xml
+M Documentation/includes/global-attributes.asciidoc
+M NEWS
+M Version
+
+commit aae76dc4e6ce1909a1aeaee9500ec65e8e67aec8
+Author: Ludovic Marcotte
+Date: Thu Feb 12 09:57:10 2015 -0500
+
+ Limit curl request for webcalendars to 60 seconds
+
+M SoObjects/Appointments/SOGoWebAppointmentFolder.m
+
+commit c20ca0e33dd3e25ba41f7b2a2cd70a1c39d1e125
+Author: Francis Lachapelle
+Date: Thu Feb 12 09:29:40 2015 -0500
+
+ Fix exception in set operation of sogo-tool
+
+M NEWS
+M Tools/SOGoToolUserPreferences.m
+
+commit 601a9ca5598490f839938884f20d0a366388ed4b
+Author: Francis Lachapelle
+Date: Thu Feb 12 09:09:18 2015 -0500
+
+ Update translations
+
+M NEWS
+M UI/PreferencesUI/Czech.lproj/Localizable.strings
+M UI/PreferencesUI/Finnish.lproj/Localizable.strings
+M UI/PreferencesUI/French.lproj/Localizable.strings
+M UI/PreferencesUI/German.lproj/Localizable.strings
+M UI/PreferencesUI/Hungarian.lproj/Localizable.strings
+
+commit d7c283f7511c7753f2cf0fc5af0e2eaea826b66f
+Author: Ludovic Marcotte
+Date: Thu Feb 12 08:49:17 2015 -0500
+
+ improved fetching of text parts over EAS
+
+M ActiveSync/SOGoMailObject+ActiveSync.m
+M NEWS
+
+commit f3325b8b0d59a730716090b8f5e1319ce3edfd70
+Author: Ludovic Marcotte
+Date: Wed Feb 11 18:13:36 2015 -0500
+
+ Updated NEWS file wrt fixes
+
+M NEWS
+
+commit b4ea4d7e9fd90520675452bdcbf70155d6b87eef
+Author: Ludovic Marcotte
+Date: Wed Feb 11 14:39:57 2015 -0500
+
+ Improvement over previous commit
+
+M UI/Templates/PreferencesUI/UIxPreferences.wox
+M UI/WebServerResources/UIxPreferences.js
+
+commit 9447437d6fd9da2701312363ae92427d2404749a
+Author: Ludovic Marcotte
+Date: Wed Feb 11 14:33:32 2015 -0500
+
+ Added missing strings from previous commit
+
+M UI/PreferencesUI/English.lproj/Localizable.strings
+
+commit 322f72626a3253f9d290cfb6d83ff8f5fac2a4e4
+Author: Ludovic Marcotte
+Date: Wed Feb 11 14:30:40 2015 -0500
+
+ now possible to limit automatic forwards to internal/external domains
+
+M Documentation/SOGoInstallationGuide.asciidoc
+M NEWS
+M SoObjects/SOGo/SOGoDomainDefaults.h
+M SoObjects/SOGo/SOGoDomainDefaults.m
+M UI/PreferencesUI/UIxPreferences.m
+M UI/Templates/PreferencesUI/UIxPreferences.wox
+M UI/WebServerResources/UIxPreferences.js
+
+commit 55ae4cb8c0390f2007ea08896e1adcb722e72c10
+Author: Ludovic Marcotte
+Date: Wed Feb 11 12:59:04 2015 -0500
+
+ First pass at EAS' best practices for SOGo
+
+M Documentation/SOGoInstallationGuide.asciidoc
+M NEWS
+
+commit e5d01428ad074d45ed8719b3aaac5d09537faba4
+Author: Ludovic Marcotte
+Date: Wed Feb 11 11:31:35 2015 -0500
+
+ now possible for SOGo to change the sambaNTPassword/sambaLMPassword
+
+M Documentation/SOGoInstallationGuide.asciidoc
+M NEWS
+M SoObjects/SOGo/GNUmakefile
+M SoObjects/SOGo/LDAPSource.h
+M SoObjects/SOGo/LDAPSource.m
+M SoObjects/SOGo/NSData+Crypto.h
+M SoObjects/SOGo/NSData+Crypto.m
+M SoObjects/SOGo/NSString+Crypto.h
+M SoObjects/SOGo/NSString+Crypto.m
+A SoObjects/SOGo/lmhash.c
+A SoObjects/SOGo/lmhash.h
+M Tools/SOGoTool.h
+M Tools/SOGoTool.m
+
+commit 82178fd6ceb5c4cb9acb10a4be06c022c895de7a
+Author: Ludovic Marcotte
+Date: Wed Feb 11 08:31:32 2015 -0500
+
+ Initial patch from tfu for bug #3055
+
+M ActiveSync/SOGoMailObject+ActiveSync.m
+
+commit 6cf1ec998e33990e77725b9c84bb3ba7ec34dcbc
+Author: Ludovic Marcotte
+Date: Tue Feb 10 07:09:31 2015 -0500
+
+ Updated NEWS file for the #3095 bug fix in SOPE
+
+M NEWS
+
+commit 6c9b1a494fa1f0a44b118f6c4a399e958796704d
+Author: Ludovic Marcotte
+Date: Mon Feb 9 16:12:59 2015 -0500
+
+ Fix for bug #2332
+
+M NEWS
+M SoObjects/SOGo/SOGoSieveManager.m
+M UI/PreferencesUI/English.lproj/Localizable.strings
+M UI/PreferencesUI/UIxPreferences.m
+M UI/Templates/PreferencesUI/UIxPreferences.wox
+
+commit 98e4c8ca55b0424111bd7c98060f689316008572
+Author: Ludovic Marcotte
+Date: Mon Feb 9 09:10:55 2015 -0500
+
+ Fixed doc regarder domain vs. system settings
+
+M Documentation/SOGoInstallationGuide.asciidoc
+
+commit 3731819e1565a6198237218dff078047628b1316
+Author: Ludovic Marcotte
+Date: Thu Feb 5 16:21:27 2015 -0500
+
+ Added patch from tfu for #2995
+
+M ActiveSync/SOGoMailObject+ActiveSync.m
+M Documentation/SOGoInstallationGuide.asciidoc
+M NEWS
+
+commit eca22caefc921fa713c2b83e24831f52b397eca7
+Author: Ludovic Marcotte
+Date: Thu Feb 5 11:14:53 2015 -0500
+
+ Fix for bug #3078
+
+M NEWS
+M UI/MailerUI/UIxMailFolderActions.m
+
+commit 923bcf8ae291f574feb611d80c8b5a861dcb663a
+Author: Ludovic Marcotte
+Date: Thu Feb 5 09:29:29 2015 -0500
+
+ Applied patch for #3092
+
+M SOPE/GDLContentStore/GCSChannelManager.m
+
+commit 0b44fa81f4c1ec9c5b812d799bf76399ce240b5b
+Author: Francis Lachapelle
+Date: Tue Feb 3 13:20:57 2015 -0500
+
+ Update NEWS file
+
+M NEWS
+
+commit 776b17ff202c45ff63bfc5144dd0ccc37a2f3e58
+Author: Francis Lachapelle
+Date: Tue Feb 3 12:06:28 2015 -0500
+
+ Fix mail editor when not called from mail module
+
+ Fixes #3088
+
+M UI/WebServerResources/UIxMailEditor.js
+
+commit 2aa528dacca6c61d194bd2603cf0ba0d5a436647
+Author: Ludovic Marcotte
+Date: Fri Jan 30 11:08:43 2015 -0500
+
+ Preparation for the release
+
+M ChangeLog
+M Documentation/docinfo.xml
+M Documentation/includes/global-attributes.asciidoc
+M Version
+
commit d1d398091961f5d497b67313e098a8a5624089f4
Author: Francis Lachapelle
Date: Fri Jan 30 11:03:38 2015 -0500
diff --git a/Documentation/SOGoInstallationGuide.asciidoc b/Documentation/SOGoInstallationGuide.asciidoc
index e88affe42..e74cf48b0 100644
--- a/Documentation/SOGoInstallationGuide.asciidoc
+++ b/Documentation/SOGoInstallationGuide.asciidoc
@@ -1003,6 +1003,14 @@ this feature, every LDAP requests will fail. Note that some LDAP servers
require LDAP/SSL for password policies to work. This is the case for
example with 389 Directory Server.
+|updateSambaNTLMPasswords
+|If set to `YES`, SOGo will automatically update the sambaNTPassword
+and sambaLMPassword attributes when changing passwords. The attributes
+must be called sambaNTPassword and sambaLMPassword. You must also make
+sure the correct ACL is set in your LDAP server to allow users to change
+their own sambaNTPassword and sambaLMPassword password attributes.
+Defaults to `NO` when unset.
+
|isAddressBook
|If set to `YES`, this LDAP source is used as a shared address book
(with read-only access). Note that if set to `NO`, autocompletion will
@@ -1040,6 +1048,11 @@ of supported attributes.
user addressbooks (see _abOU_ below), this list of object classes will
be applied to new records as they are created.
+|GroupObjectClasses
+|A list (array) of names identifying groups within the LDAP source. If not
+set, SOGo will use `group`, `groupofnames`, `groupofuniquenames`
+and `posixgroup`.
+
|modifiers
|A list (array) of usernames that are authorized to perform
modifications to the address book defined by this LDAP source.
@@ -1329,27 +1342,27 @@ The following table describes the parameters that were set:
[cols="3,47,50a"]
|=======================================================================
-|D |SOGoProfileURL
+|S |SOGoProfileURL
|Parameter used to set the database URL so that SOGo can retrieve user
profiles.
For MySQL, set the database URL to something like:
`mysql://sogo:sogo@localhost:3306/sogo/sogo_user_profile`.
-|D |OCSFolderInfoURL
+|S |OCSFolderInfoURL
|Parameter used to set the database URL so that SOGo can retrieve the
location of user folders (address books and calendars).
For Oracle, set the database URL to something like:
`oracle://sogo:sogo@localhost:1526/sogo/sogo_folder_info`.
-|D |OCSSessionsFolderURL
+|S |OCSSessionsFolderURL
|Parameter used to set the database URL so that SOGo can store and
retrieve secured user sessions information. For PostgreSQL, the database
URL could be set to something like:
`postgresql://sogo:sogo@localhost:5432/sogo/sogo_sessions_folder`.
-|D |OCSEMailAlarmsFolderURL
+|S |OCSEMailAlarmsFolderURL
|Parameter used to set the database URL for email-based alarms (that can
be set on events and tasks). This parameter is relevant only if
_SOGoEnableEMailAlarms_ is set to `YES`. For PostgreSQL, the database
@@ -1681,6 +1694,13 @@ cronjob `sogo-tmpwatch`.
Defaults to `/var/spool/sogo`.
+|S |NGMimeBuildMimeTempDirectory
+|Parameter used to set the path where temporary files will be stored
+by SOPE when dealing with MIME messages.
+
+Defaults to `/tmp`.
+
+
|S |NGImap4DisableIMAP4Pooling
|Disables IMAP pooling when set to `YES`. Enable pooling by setting to
`NO` or using a caching proxy like imapproxy.
@@ -1784,6 +1804,12 @@ host.
Defaults to `NO` when unset.
+|D |SOGoForwardConstraints
+|Parameter used to set constraints on possible addresses used when
+automatically forwarding mails. When set to `0` (default), no constraint
+is enforced. When set to `1`, only internal domains can be used. When
+set to `2`, only external domains can be used.
+
|D |SOGoSieveScriptsEnabled
|Parameter used to activate the edition from the preferences windows of
server-side mail filters. Requires Sieve script support on the IMAP
@@ -2346,8 +2372,8 @@ easier. These GUI utilities can make use of templates to create and
pre-configure typical user accounts or any standardized LDAP record,
along with the correct object classes, fields and default values.
-Microsoft ActiveSync
---------------------
+Microsoft Enterprise ActiveSync
+-------------------------------
SOGo supports the Microsoft ActiveSync protocol.
@@ -2421,6 +2447,10 @@ Please be aware of the following limitations:
solution is to configure Outlook to use a LDAP server (over SSL) with
authentication. Outlook 2013 also does not seem to support multiple
address books over ActiveSync.
+* To successfully synchronize Outlook email categories, a corresponding
+mail label (Preferences->Mail Options) has to be created manually in SOGo
+for each label defined in Outlook. The name in SOGo and in Outlook must be
+identical.
* Make sure you do not use a self-signed certificate. While this will
work, Outlook will work intermittently as it will raise popups for
certificate validation, sometimes in background, preventing the user to
@@ -2457,6 +2487,83 @@ and send an email to iplicreq@microsoft.com
Inverse inc. provides this software for free, but is not responsible for
anything related to its usage.
+Microsoft Enterprise ActiveSync Tuning
+--------------------------------------
+
+First of all, it is important to know that most EAS devices will keep
+HTTP connections open to SOGo (and thus, Apache) for a long time. This
+is required for "push" to work properly. Connections can stay open for
+up to one hour, or 3600 seconds.
+
+The first parameter to check is related to Apache's proxying to
+SOGo:
+
+----
+ProxyPass /Microsoft-Server-ActiveSync \
+ http://127.0.0.1:20000/SOGo/Microsoft-Server-ActiveSync \
+ retry=60 connectiontimeout=5 timeout=360
+----
+
+The above line sets a timeout for up to 360 seconds, or 6 minutes. If
+you want to let EAS clients keep their HTTP connections open for up
+to an hour, you must change the timeout parameter and set it to 3600.
+
+If you change this value, the WOWatchDogRequestTimeout parameter must be changed
+accordingly in SOGo's configuration file (/etc/sogo/sogo.conf). By default,
+a SOGo child process is allowed to handle a request that can take up
+to 10 minutes before it gets killed by its parent process. When using
+EAS "push", the client expects to keep its connection open for up to one
+hour - so the WOWatchDogRequestTimeout, which is set in minutes,
+must be adjusted accordingly.
+
+EAS clients will keep HTTP connections open for a long time
+during these two EAS commands: Ping and Sync. By default, SOGo will prevent
+EAS clients from keeping connections for a long time. This is to avoid the
+situation where all SOGo child processes would be monopolized by EAS clients -
+rendering the SOGo web interface or DAV interface unavailable. The
+default SOGo behavior is thus similar to disable EAS push entirely.
+
+Two SOGo configuration parameters are available to modify this behavior:
+SOGoMaximumPingInterval (set by default to 10 seconds) and
+SOGoMaximumSyncInterval (set by default to 30 seconds). If you want
+connection to stay open for up to one hour, you should set these
+slightly under 3600 seconds (say 3540 - or 59 minutes). During a
+long-lived HTTP connection, the SOGo child process will perform
+internal polling to detect changes and return them to the EAS client
+if any changes are found. The parameter used to control this
+is SOGoInternalSyncInterval. By default, polling is done every 10
+seconds. This might generate too much load on large-scale system.
+
+The last configuration parameter to adjust is WOWorkersCount - which sets the
+number of SOGo child process that will be used to handle requests.
+You should have at least one child per EAS device configured to use
+"push". You must also have more children than you have EAS devices
+configured to use "push" - in order to handle normal SOGo requests to
+its Web or DAV interfaces.
+
+Here are some usage examples for EAS devices using "push". In all
+cases, the Apache timeout is set to 3600 and the
+WOWatchDogRequestTimeout parameter is set to 60.
+
+Example 1 - 100 users, 10 EAS devices:
+
+----
+WOWorkersCount = 15;
+SOGoMaximumPingInterval = 3540;
+SOGoMaximumSyncInterval = 3540;
+SOGoInternalSyncInterval = 30;
+----
+
+Example 2 - 1000 users, 100 EAS devices:
+
+----
+WOWorkersCount = 120;
+SOGoMaximumPingInterval = 3540;
+SOGoMaximumSyncInterval = 3540;
+SOGoInternalSyncInterval = 60;
+----
+
+
Using SOGo
----------
diff --git a/Documentation/docinfo.xml b/Documentation/docinfo.xml
index 00fc54948..5a83383db 100644
--- a/Documentation/docinfo.xml
+++ b/Documentation/docinfo.xml
@@ -1,7 +1,7 @@
-Version 2.2.15 - January 2015
-for version 2.2.15
-2015-01-30
+Version 2.2.17a - March 2015
+for version 2.2.17a
+2015-03-26
Permission is granted to copy, distribute and/or modify this document under the terms of the GNU Free Documentation License, Version 1.2 or any later version published by the Free Software Foundation; with no Invariant Sections, no Front-Cover Texts, and no Back-Cover Texts. A copy of the license is included in the section entitled "GNU Free Documentation License".
diff --git a/Documentation/includes/global-attributes.asciidoc b/Documentation/includes/global-attributes.asciidoc
index 0cbd771ae..815d94e72 100644
--- a/Documentation/includes/global-attributes.asciidoc
+++ b/Documentation/includes/global-attributes.asciidoc
@@ -13,6 +13,6 @@
// TODO have the build system take care of this
-:release_version: 2.2.15
+:release_version: 2.2.17a
// vim: set syntax=asciidoc tabstop=2 shiftwidth=2 expandtab:
diff --git a/NEWS b/NEWS
index 38c6e8f3e..1644f8b71 100644
--- a/NEWS
+++ b/NEWS
@@ -20,6 +20,65 @@ Bug fixes
- Fix exception modifications import in recurrence series
- Fix server side crash parsing rtf emails with images (with word97 format)
+2.2.17a (2014-03-15)
+--------------------
+
+Bug fixes
+ - avoid calling -stringByReplacingOccurrencesOfString:... for old GNUstep runtime
+
+2.2.17 (2015-03-24)
+-------------------
+
+Enhancements
+ - support for mail prority using EAS
+ - immediately delete mails from EAS clients when they are marked as deleted on the IMAP server
+ - now favor login@domain as the default email address if multiple mail: fields are specified
+ - enable by default HTML mails support using EAS on Windows and BB phones
+ - now possible to configure objectClass names for LDAP groups using GroupObjectClasses (#1499)
+
+Bug fixes
+ - fixed login issue after password change (#2601)
+ - fixed potential encoding issue using EAS and 8-bit mails (#3116)
+ - multiple collections support for GetItemEstimate using EAS
+ - fixed empty sync responses for EAS 2.5 and 12.0 clients
+ - use the correct mail body element for EAS 2.5 clients
+ - fixed tasks disappearing issue with RoadSync
+ - use the correct body element for events for EAS 2.5 clients
+ - SmartReply improvements for missing body attributes
+ - do not use syncKey from cache when davCollectionTag = -1
+ - use correct mail attachment elements for EAS 2.5 clients
+ - fixed contacts lookup by UID in freebusy
+ - reduced telephone number to a single value in JSON response of contacts list
+ - fixed freebusy data when 'busy off hours' is enabled and period starts during the weekend
+ - fixed fetching of freebusy data from the Web interface
+ - fixed EAS handling of Bcc in emails (#3138)
+ - fixed Language-Region tags in Web interface (#3121)
+ - properly fallback over EAS to UTF-8 and then Latin1 for messages w/o charset (#3103)
+ - prevent potential freebusy lookup crashes during timezone changes with repetitive events
+ - improved GetItemEstimate to count all vasnished/deleted mails too
+ - improvements to EAS SyncKey handling to avoid missing mails (#3048, #3058)
+
+2.2.16 (2015-02-12)
+-------------------
+
+New features
+ - now possible for SOGo to change the sambaNTPassword/sambaLMPassword
+ - now possible to limit automatic forwards to internal/external domains
+
+Enhancements
+ - added support for email categories using EAS (#2995)
+ - now possible to always send vacation messages (#2332)
+ - added EAS best practices to the documentation
+ - improved fetching of text parts over EAS
+ - updated Czech, Finnish, French, German and Hungarian translations
+
+Bug fixes
+ - (regression) fixed sending a message when mail module is not active (#3088)
+ - mail labels with blanks are not handled correctly (#3078)
+ - fixed BlackBerry issues sending multiple mails over EAS (#3095)
+ - fixed plain/text mails showing on one line on Android/EAS (#3055)
+ - fixed exception in sogo-tool when parsing arguments of a set operation
+
2.2.15-zentyal2 (2015-03-16)
----------------------------
diff --git a/SOPE/GDLContentStore/GCSChannelManager.m b/SOPE/GDLContentStore/GCSChannelManager.m
index b21b230a6..d04d70446 100644
--- a/SOPE/GDLContentStore/GCSChannelManager.m
+++ b/SOPE/GDLContentStore/GCSChannelManager.m
@@ -240,7 +240,7 @@ static NSTimeInterval ChannelCollectionTimer = 5 * 60;
handle = currentHandle;
else if (debugPools)
[self logWithFormat: @"DBPOOL: cannot use handle (%@ vs %@) ",
- [_url absoluteString], [handle->url absoluteString]];
+ [_url absoluteString], [currentHandle->url absoluteString]];
return handle;
}
diff --git a/SoObjects/Appointments/SOGoFreeBusyObject.m b/SoObjects/Appointments/SOGoFreeBusyObject.m
index 6624a2cfe..1c6e55662 100644
--- a/SoObjects/Appointments/SOGoFreeBusyObject.m
+++ b/SoObjects/Appointments/SOGoFreeBusyObject.m
@@ -62,17 +62,14 @@
SOGoUserManager *um;
NSString *domain;
NSDictionary *contactInfos;
- NSArray *contacts;
um = [SOGoUserManager sharedUserManager];
contactInfos = [um contactInfosForUserWithUIDorEmail: uid];
if (contactInfos == nil)
{
+ // Search among global addressbooks
domain = [[context activeUser] domain];
- [um fetchContactsMatching: uid inDomain: domain];
- contacts = [um fetchContactsMatching: uid inDomain: domain];
- if ([contacts count] == 1)
- contactInfos = [contacts lastObject];
+ contactInfos = [um fetchContactWithUID: uid inDomain: domain];
}
/* iCal.app compatibility:
@@ -279,7 +276,6 @@
if ([uid length])
{
SOGoUserManager *um;
- NSArray *contacts;
NSString *domain, *email;
NSDictionary *contact;
MSExchangeFreeBusy *exchangeFreeBusy;
@@ -287,10 +283,9 @@
um = [SOGoUserManager sharedUserManager];
domain = [[context activeUser] domain];
- contacts = [um fetchContactsMatching: uid inDomain: domain];
- if ([contacts count] == 1)
+ contact = [um fetchContactWithUID: uid inDomain: domain];
+ if (contact)
{
- contact = [contacts lastObject];
email = [contact valueForKey: @"c_email"];
source = [contact objectForKey: @"source"];
if ([email length]
@@ -386,8 +381,7 @@
([currentStartDate compare: startDate] == NSOrderedAscending)? startDate : currentStartDate, @"startDate",
currentEndDate, @"endDate", nil]];
- if (!firstRange
- && currentEndDate != endDate
+ if (currentEndDate != endDate
&& ([currentEndDate dayOfWeek] == 6 || [currentEndDate dayOfWeek] == 0))
{
// Fill weekend days
diff --git a/SoObjects/Appointments/SOGoWebAppointmentFolder.m b/SoObjects/Appointments/SOGoWebAppointmentFolder.m
index 3bd6c4206..cb4c7543c 100644
--- a/SoObjects/Appointments/SOGoWebAppointmentFolder.m
+++ b/SoObjects/Appointments/SOGoWebAppointmentFolder.m
@@ -1,6 +1,6 @@
/* SOGoWebAppointmentFolder.m - this file is part of SOGo
*
- * Copyright (C) 2009-2014 Inverse inc.
+ * Copyright (C) 2009-2015 Inverse inc.
*
* 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
@@ -137,6 +137,7 @@ size_t curl_body_function(void *ptr, size_t size, size_t nmemb, void *buffer)
curl_easy_setopt (curl, CURLOPT_URL, [location UTF8String]);
curl_easy_setopt (curl, CURLOPT_SSL_VERIFYPEER, 0L);
curl_easy_setopt (curl, CURLOPT_SSL_VERIFYHOST, 0L);
+ curl_easy_setopt (curl, CURLOPT_TIMEOUT, 60L);
authInfos = [self _loadAuthData];
if (authInfos)
diff --git a/SoObjects/Contacts/SOGoContactSourceFolder.m b/SoObjects/Contacts/SOGoContactSourceFolder.m
index 19e726a96..e385d9c09 100644
--- a/SoObjects/Contacts/SOGoContactSourceFolder.m
+++ b/SoObjects/Contacts/SOGoContactSourceFolder.m
@@ -267,6 +267,13 @@
data = [oldRecord objectForKey: @"homephone"];
if (![data length])
data = @"";
+ else if ([data isKindOfClass: [NSArray class]])
+ {
+ if ([data count] > 0)
+ data = [data objectAtIndex: 0];
+ else
+ data = @"";
+ }
[newRecord setObject: data forKey: @"c_telephonenumber"];
// Custom attribute for group-lookups. See LDAPSource.m where
diff --git a/SoObjects/Mailer/SOGoMailFolder.m b/SoObjects/Mailer/SOGoMailFolder.m
index d83ca78a6..c65c6c43c 100644
--- a/SoObjects/Mailer/SOGoMailFolder.m
+++ b/SoObjects/Mailer/SOGoMailFolder.m
@@ -2129,13 +2129,13 @@ _compareFetchResultsByMODSEQ (id entry1, id entry2, void *data)
value: nextModseq];
searchQualifier = [[EOAndQualifier alloc]
initWithQualifiers:
- kvQualifier, [self _nonDeletedQualifier], nil];
+ kvQualifier, nil];
[kvQualifier release];
[searchQualifier autorelease];
}
else
{
- searchQualifier = [self _nonDeletedQualifier];
+ searchQualifier = nil;
}
if (theStartDate)
@@ -2144,7 +2144,7 @@ _compareFetchResultsByMODSEQ (id entry1, id entry2, void *data)
sinceDateQualifier = [EOQualifier qualifierWithQualifierFormat:
@"(DATE >= %@)", theStartDate];
- searchQualifier = [[EOAndQualifier alloc] initWithQualifiers: searchQualifier, sinceDateQualifier,
+ searchQualifier = [[EOAndQualifier alloc] initWithQualifiers: sinceDateQualifier, searchQualifier,
nil];
[searchQualifier autorelease];
}
@@ -2155,7 +2155,7 @@ _compareFetchResultsByMODSEQ (id entry1, id entry2, void *data)
sortOrdering: nil];
fetchResults = [(NSDictionary *)[self fetchUIDs: uids
- parts: [NSArray arrayWithObject: @"modseq"]]
+ parts: [NSArray arrayWithObjects: @"modseq", @"flags", nil]]
objectForKey: @"fetch"];
/* NOTE: we sort items manually because Cyrus does not properly sort
@@ -2166,7 +2166,7 @@ _compareFetchResultsByMODSEQ (id entry1, id entry2, void *data)
for (i = 0; i < [fetchResults count]; i++)
{
- d = [NSDictionary dictionaryWithObject: [[fetchResults objectAtIndex: i] objectForKey: @"modseq"]
+ d = [NSDictionary dictionaryWithObject: ([[[fetchResults objectAtIndex: i] objectForKey: @"flags"] containsObject: @"deleted"]) ? [NSNull null] : [[fetchResults objectAtIndex: i] objectForKey: @"modseq"]
forKey: [[[fetchResults objectAtIndex: i] objectForKey: @"uid"] stringValue]];
[allTokens addObject: d];
}
diff --git a/SoObjects/SOGo/GNUmakefile b/SoObjects/SOGo/GNUmakefile
index 70b458001..4e506d166 100644
--- a/SoObjects/SOGo/GNUmakefile
+++ b/SoObjects/SOGo/GNUmakefile
@@ -77,11 +77,10 @@ SOGo_HEADER_FILES = \
WORequest+SOGo.h \
WOResourceManager+SOGo.h \
WOResponse+SOGo.h \
- WOContext+SOGo.h \
+ WOContext+SOGo.h \
\
SOGoCredentialsFile.h
-# daemon tool
all::
@touch SOGoBuild.m
@@ -156,10 +155,11 @@ SOGo_OBJC_FILES = \
WORequest+SOGo.m \
WOResourceManager+SOGo.m \
WOResponse+SOGo.m \
- WOContext+SOGo.m \
+ WOContext+SOGo.m \
\
SOGoCredentialsFile.m
+SOGo_C_FILES = lmhash.c
SOGo_RESOURCE_FILES = \
SOGoDefaults.plist \
diff --git a/SoObjects/SOGo/LDAPSource.h b/SoObjects/SOGo/LDAPSource.h
index 982c39543..3877ec4ee 100644
--- a/SoObjects/SOGo/LDAPSource.h
+++ b/SoObjects/SOGo/LDAPSource.h
@@ -1,6 +1,6 @@
/* LDAPSource.h - this file is part of SOGo
*
- * Copyright (C) 2007-2014 Inverse inc.
+ * Copyright (C) 2007-2015 Inverse inc.
*
* 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
@@ -72,12 +72,14 @@
NSDictionary *contactMapping;
NSArray *contactObjectClasses;
+ NSArray *groupObjectClasses;
NSDictionary *modulesConstraints;
NSMutableArray *searchAttributes;
BOOL passwordPolicy;
+ BOOL updateSambaNTLMPasswords;
/* resources handling */
NSString *kindField;
@@ -105,6 +107,7 @@
UIDField: (NSString *) newUIDField
mailFields: (NSArray *) newMailFields
searchFields: (NSArray *) newSearchFields
+groupObjectClasses: (NSArray *) newGroupObjectClasses
IMAPHostField: (NSString *) newIMAPHostField
IMAPLoginField: (NSString *) newIMAPLoginField
SieveHostField: (NSString *) newSieveHostField
diff --git a/SoObjects/SOGo/LDAPSource.m b/SoObjects/SOGo/LDAPSource.m
index daeddb740..598e56ffe 100644
--- a/SoObjects/SOGo/LDAPSource.m
+++ b/SoObjects/SOGo/LDAPSource.m
@@ -1,6 +1,6 @@
/* LDAPSource.m - this file is part of SOGo
*
- * Copyright (C) 2007-2014 Inverse inc.
+ * Copyright (C) 2007-2015 Inverse inc.
*
* 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
@@ -50,7 +50,6 @@ static Class NSStringK;
stringByReplacingString: @"'" withString: @"\\'"] \
stringByReplacingString: @"%" withString: @"%%"]
-
@implementation LDAPSource
+ (void) initialize
@@ -102,6 +101,8 @@ static Class NSStringK;
contactMapping = nil;
searchFields = [NSArray arrayWithObjects: @"sn", @"displayname", @"telephonenumber", nil];
[searchFields retain];
+ groupObjectClasses = [NSArray arrayWithObjects: @"group", @"groupofnames", @"groupofuniquenames", @"posixgroup", nil];
+ [groupObjectClasses retain];
IMAPHostField = nil;
IMAPLoginField = nil;
SieveHostField = nil;
@@ -113,6 +114,7 @@ static Class NSStringK;
searchAttributes = nil;
passwordPolicy = NO;
+ updateSambaNTLMPasswords = NO;
kindField = nil;
multipleBookingsField = nil;
@@ -144,6 +146,7 @@ static Class NSStringK;
[contactMapping release];
[mailFields release];
[searchFields release];
+ [groupObjectClasses release];
[IMAPHostField release];
[IMAPLoginField release];
[SieveHostField release];
@@ -189,6 +192,7 @@ static Class NSStringK;
UIDField: [udSource objectForKey: @"UIDFieldName"]
mailFields: [udSource objectForKey: @"MailFieldNames"]
searchFields: [udSource objectForKey: @"SearchFieldNames"]
+ groupObjectClasses: [udSource objectForKey: @"GroupObjectClasses"]
IMAPHostField: [udSource objectForKey: @"IMAPHostFieldName"]
IMAPLoginField: [udSource objectForKey: @"IMAPLoginFieldName"]
SieveHostField: [udSource objectForKey: @"SieveHostFieldName"]
@@ -245,6 +249,9 @@ static Class NSStringK;
if ([udSource objectForKey: @"passwordPolicy"])
passwordPolicy = [[udSource objectForKey: @"passwordPolicy"] boolValue];
+ if ([udSource objectForKey: @"updateSambaNTLMPasswords"])
+ updateSambaNTLMPasswords = [[udSource objectForKey: @"updateSambaNTLMPasswords"] boolValue];
+
ASSIGN(MSExchangeHostname, [udSource objectForKey: @"MSExchangeHostname"]);
}
@@ -307,6 +314,7 @@ static Class NSStringK;
UIDField: (NSString *) newUIDField
mailFields: (NSArray *) newMailFields
searchFields: (NSArray *) newSearchFields
+groupObjectClasses: (NSArray *) newGroupObjectClasses
IMAPHostField: (NSString *) newIMAPHostField
IMAPLoginField: (NSString *) newIMAPLoginField
SieveHostField: (NSString *) newSieveHostField
@@ -331,6 +339,8 @@ static Class NSStringK;
ASSIGN(mailFields, newMailFields);
if (newSearchFields)
ASSIGN(searchFields, newSearchFields);
+ if (newGroupObjectClasses)
+ ASSIGN(groupObjectClasses, newGroupObjectClasses);
if (newBindFields)
{
// Before SOGo v1.2.0, bindFields was a comma-separated list
@@ -598,6 +608,40 @@ static Class NSStringK;
return [NSString stringWithFormat: @"{%@}%@", _userPasswordAlgorithm, pass];
}
+- (BOOL) _ldapModifyAttribute: (NSString *) theAttribute
+ withValue: (NSString *) theValue
+ userDN: (NSString *) theUserDN
+ password: (NSString *) theUserPassword
+ connection: (NGLdapConnection *) bindConnection
+{
+ NGLdapModification *mod;
+ NGLdapAttribute *attr;
+ NSArray *changes;
+
+ BOOL didChange;
+
+ attr = [[NGLdapAttribute alloc] initWithAttributeName: theAttribute];
+ [attr addStringValue: theValue];
+
+ mod = [NGLdapModification replaceModification: attr];
+
+ changes = [NSArray arrayWithObject: mod];
+
+ if ([bindConnection bindWithMethod: @"simple"
+ binddn: theUserDN
+ credentials: theUserPassword])
+ {
+ didChange = [bindConnection modifyEntryWithDN: theUserDN
+ changes: changes];
+ }
+ else
+ didChange = NO;
+
+ RELEASE(attr);
+
+ return didChange;
+}
+
//
//
//
@@ -651,12 +695,8 @@ static Class NSStringK;
{
// We don't use a password policy - we simply use
// a modify-op to change the password
- NGLdapModification *mod;
- NGLdapAttribute *attr;
- NSArray *changes;
NSString* encryptedPass;
-
- attr = [[NGLdapAttribute alloc] initWithAttributeName: @"userPassword"];
+
if ([_userPasswordAlgorithm isEqualToString: @"none"])
{
encryptedPass = newPassword;
@@ -665,24 +705,33 @@ static Class NSStringK;
{
encryptedPass = [self _encryptPassword: newPassword];
}
- if(encryptedPass != nil)
+
+ if (encryptedPass != nil)
{
- [attr addStringValue: encryptedPass];
- mod = [NGLdapModification replaceModification: attr];
- changes = [NSArray arrayWithObject: mod];
*perr = PolicyNoError;
-
- if ([bindConnection bindWithMethod: @"simple"
- binddn: userDN
- credentials: oldPassword])
- {
- didChange = [bindConnection modifyEntryWithDN: userDN
- changes: changes];
- }
- else
- didChange = NO;
+ didChange = [self _ldapModifyAttribute: @"userPassword"
+ withValue: encryptedPass
+ userDN: userDN
+ password: oldPassword
+ connection: bindConnection];
}
}
+
+ // We must check if we must update the Samba NT/LM password hashes
+ if (didChange && updateSambaNTLMPasswords)
+ {
+ [self _ldapModifyAttribute: @"sambaNTPassword"
+ withValue: [newPassword asNTHash]
+ userDN: userDN
+ password: newPassword
+ connection: bindConnection];
+
+ [self _ldapModifyAttribute: @"sambaLMPassword"
+ withValue: [newPassword asLMHash]
+ userDN: userDN
+ password: newPassword
+ connection: bindConnection];
+ }
}
}
}
@@ -989,6 +1038,8 @@ static Class NSStringK;
NSString *value;
static NSArray *resourceKinds = nil;
NSMutableArray *classes;
+ NSEnumerator *gclasses;
+ NSString *gclass;
id o;
if (!resourceKinds)
@@ -1017,24 +1068,27 @@ static Class NSStringK;
if (classes)
{
- // We check if our entry is a group. If so, we set the
- // 'isGroup' custom attribute.
- if ([classes containsObject: @"group"] ||
- [classes containsObject: @"groupofnames"] ||
- [classes containsObject: @"groupofuniquenames"] ||
- [classes containsObject: @"posixgroup"])
- {
- [ldifRecord setObject: [NSNumber numberWithInt: 1]
- forKey: @"isGroup"];
- }
// We check if our entry is a resource. We also support
// determining resources based on the KindFieldName attribute
// value - see below.
- else if ([classes containsObject: @"calendarresource"])
+ if ([classes containsObject: @"calendarresource"])
{
[ldifRecord setObject: [NSNumber numberWithInt: 1]
forKey: @"isResource"];
}
+ else
+ {
+ // We check if our entry is a group. If so, we set the
+ // 'isGroup' custom attribute.
+ gclasses = [groupObjectClasses objectEnumerator];
+ while (gclass = [gclasses nextObject])
+ if ([classes containsObject: [gclass lowercaseString]])
+ {
+ [ldifRecord setObject: [NSNumber numberWithInt: 1]
+ forKey: @"isGroup"];
+ break;
+ }
+ }
}
// We check if that entry corresponds to a resource. For this,
@@ -1326,6 +1380,11 @@ static Class NSStringK;
return modifiers;
}
+- (NSArray *) groupObjectClasses
+{
+ return groupObjectClasses;
+}
+
static NSArray *
_convertRecordToLDAPAttributes (LDAPSourceSchema *schema, NSDictionary *ldifRecord)
{
@@ -1641,6 +1700,7 @@ _makeLDAPChanges (NGLdapConnection *ldapConnection,
UIDField: @"cn"
mailFields: nil
searchFields: nil
+ groupObjectClasses: nil
IMAPHostField: nil
IMAPLoginField: nil
SieveHostField: nil
diff --git a/SoObjects/SOGo/NSData+Crypto.h b/SoObjects/SOGo/NSData+Crypto.h
index 547e2d0b6..da69a40ba 100644
--- a/SoObjects/SOGo/NSData+Crypto.h
+++ b/SoObjects/SOGo/NSData+Crypto.h
@@ -1,7 +1,7 @@
/* NSData+Crypto.h - this file is part of SOGo
*
* Copyright (C) 2012 Nicolas Höft
- * Copyright (C) 2012 Inverse inc.
+ * Copyright (C) 2012-2015 Inverse inc.
*
* Author: Nicolas Höft
* Inverse inc.
@@ -32,8 +32,10 @@
@interface NSData (SOGoCryptoExtension)
- (NSData *) asCryptedPassUsingScheme: (NSString *) passwordScheme
- withSalt: (NSData *) theSalt;
+ withSalt: (NSData *) theSalt;
+- (NSData *) asLM;
+- (NSData *) asMD4;
- (NSData *) asMD5;
- (NSData *) asSMD5UsingSalt: (NSData *) theSalt;
- (NSData *) asSHA1;
diff --git a/SoObjects/SOGo/NSData+Crypto.m b/SoObjects/SOGo/NSData+Crypto.m
index 5c670b445..d869f9b68 100644
--- a/SoObjects/SOGo/NSData+Crypto.m
+++ b/SoObjects/SOGo/NSData+Crypto.m
@@ -1,7 +1,7 @@
/* NSData+Crypto.m - this file is part of SOGo
*
* Copyright (C) 2012 Nicolas Höft
- * Copyright (C) 2012 Inverse inc.
+ * Copyright (C) 2012-2015 Inverse inc.
* Copyright (C) 2012 Jeroen Dekkers
*
* Author: Nicolas Höft
@@ -39,12 +39,14 @@
#include
#include
#include
+#define MD4_DIGEST_LENGTH 16
#define MD5_DIGEST_LENGTH 16
#define SHA_DIGEST_LENGTH 20
#define SHA256_DIGEST_LENGTH 32
#define SHA512_DIGEST_LENGTH 64
#elif defined(HAVE_OPENSSL)
#include
+#include
#include
#include
#else
@@ -145,7 +147,7 @@ static void _nettle_md5_compress(uint32_t *digest, const uint8_t *input);
* @return Pseudo-random binary data with length theLength or nil, if an error occured
*/
+ (NSData *) generateSaltForLength: (unsigned int) theLength
- withBase64: (BOOL) doBase64
+ withBase64: (BOOL) doBase64
{
char *buf;
int fd;
@@ -177,7 +179,7 @@ static void _nettle_md5_compress(uint32_t *digest, const uint8_t *input);
* @return Binary data from the encryption by the specified scheme. On error the funciton returns nil.
*/
- (NSData *) asCryptedPassUsingScheme: (NSString *) passwordScheme
- withSalt: (NSData *) theSalt
+ withSalt: (NSData *) theSalt
{
if ([passwordScheme caseInsensitiveCompare: @"none"] == NSOrderedSame ||
[passwordScheme caseInsensitiveCompare: @"plain"] == NSOrderedSame ||
@@ -193,6 +195,10 @@ static void _nettle_md5_compress(uint32_t *digest, const uint8_t *input);
{
return [self asMD5CryptUsingSalt: theSalt];
}
+ else if ([passwordScheme caseInsensitiveCompare: @"md4"] == NSOrderedSame)
+ {
+ return [self asMD4];
+ }
else if ([passwordScheme caseInsensitiveCompare: @"md5"] == NSOrderedSame ||
[passwordScheme caseInsensitiveCompare: @"plain-md5"] == NSOrderedSame ||
[passwordScheme caseInsensitiveCompare: @"ldap-md5"] == NSOrderedSame)
@@ -235,6 +241,49 @@ static void _nettle_md5_compress(uint32_t *digest, const uint8_t *input);
return nil;
}
+- (NSData *) asLM
+{
+ NSData *out;
+
+ unsigned char buf[14];
+ unsigned char *o;
+ unsigned int len;
+
+ memset(buf, 0, 14);
+ len = ([self length] >= 14 ? 14 : [self length]);
+ [self getBytes: buf length: len];
+
+ o = malloc(16*sizeof(unsigned char));
+
+ auth_LMhash(o, buf, len);
+
+ out = [NSData dataWithBytes: o length: 16];
+ free(o);
+
+ return out;
+}
+
+/**
+ * Hash the data with MD4. Uses openssl functions to generate it.
+ *
+ * @return Binary data from MD4 hashing. On error the funciton returns nil.
+ */
+- (NSData *) asMD4
+{
+ unsigned char md4[MD4_DIGEST_LENGTH];
+ memset(md4, 0, MD4_DIGEST_LENGTH);
+
+#if defined(HAVE_GNUTLS)
+ if (!check_gnutls_init())
+ return nil;
+
+ md4_buffer([self bytes], [self length], md4);
+#elif defined(HAVE_OPENSSL)
+ MD4([self bytes], [self length], md4);
+#endif
+
+ return [NSData dataWithBytes: md4 length: MD4_DIGEST_LENGTH];
+}
/**
* Hash the data with MD5. Uses openssl functions to generate it
diff --git a/SoObjects/SOGo/NSString+Crypto.h b/SoObjects/SOGo/NSString+Crypto.h
index 100bfaced..ba4e23cbe 100644
--- a/SoObjects/SOGo/NSString+Crypto.h
+++ b/SoObjects/SOGo/NSString+Crypto.h
@@ -1,7 +1,7 @@
/* NSString+Crypto.h - this file is part of SOGo
*
* Copyright (C) 2012 Nicolas Höft
- * Copyright (C) 2012 Inverse inc.
+ * Copyright (C) 2012-2015 Inverse inc.
*
* Author: Nicolas Höft
* Inverse inc.
@@ -39,13 +39,12 @@ typedef enum {
@interface NSString (SOGoCryptoExtension)
-
- (BOOL) isEqualToCrypted: (NSString *) cryptedPassword
- withDefaultScheme: (NSString *) theScheme;
+ withDefaultScheme: (NSString *) theScheme;
- (NSString *) asCryptedPassUsingScheme: (NSString *) passwordScheme
withSalt: (NSData *) theSalt
- andEncoding: (keyEncoding) encoding;
+ andEncoding: (keyEncoding) encoding;
// this method uses the default encoding (base64, plain, hex)
// and generates a salt when necessary
@@ -56,6 +55,9 @@ typedef enum {
- (NSString *) asSHA1String;
- (NSString *) asMD5String;
+- (NSString *) asNTHash;
+- (NSString *) asLMHash;
+
+ (NSArray *) getDefaultEncodingForScheme: (NSString *) passwordScheme;
@end
diff --git a/SoObjects/SOGo/NSString+Crypto.m b/SoObjects/SOGo/NSString+Crypto.m
index 7a720d442..bd0fa17ba 100644
--- a/SoObjects/SOGo/NSString+Crypto.m
+++ b/SoObjects/SOGo/NSString+Crypto.m
@@ -1,7 +1,7 @@
/* NSString+Crypto.m - this file is part of SOGo
*
* Copyright (C) 2012 Nicolas Höft
- * Copyright (C) 2012 Inverse inc.
+ * Copyright (C) 2012-2015 Inverse inc.
*
* Author: Nicolas Höft
* Inverse inc.
@@ -29,6 +29,8 @@
#import "NSData+Crypto.h"
#import
+#include "lmhash.h"
+
@implementation NSString (SOGoCryptoExtension)
/**
@@ -90,7 +92,8 @@
encodingAndScheme = [NSString getDefaultEncodingForScheme: scheme];
pass = [self substringWithRange: range];
- // return array with [scheme, password, encoding]
+
+ // Returns an array with [scheme, password, encoding]
return [NSArray arrayWithObjects: [encodingAndScheme objectAtIndex: 1], pass, [encodingAndScheme objectAtIndex: 0], nil];
}
@@ -103,7 +106,7 @@
* @return YES if the passwords are identical using this encryption scheme
*/
- (BOOL) isEqualToCrypted: (NSString *) cryptedPassword
- withDefaultScheme: (NSString *) theScheme
+ withDefaultScheme: (NSString *) theScheme
{
NSArray *passInfo;
NSString *selfCrypted;
@@ -193,7 +196,7 @@
*/
- (NSString *) asCryptedPassUsingScheme: (NSString *) passwordScheme
withSalt: (NSData *) theSalt
- andEncoding: (keyEncoding) userEncoding
+ andEncoding: (keyEncoding) userEncoding
{
keyEncoding dataEncoding;
NSData* cryptedData;
@@ -229,7 +232,7 @@
{
// base64 encoding
NSString *s = [[NSString alloc] initWithData: [cryptedData dataByEncodingBase64WithLineLength: 1024]
- encoding: NSASCIIStringEncoding];
+ encoding: NSASCIIStringEncoding];
return [s autorelease];
}
@@ -277,7 +280,8 @@
}
// in order to keep backwards-compatibility, hex encoding is used for sha1 here
- if ([passwordScheme caseInsensitiveCompare: @"md5"] == NSOrderedSame ||
+ if ([passwordScheme caseInsensitiveCompare: @"md4"] == NSOrderedSame ||
+ [passwordScheme caseInsensitiveCompare: @"md5"] == NSOrderedSame ||
[passwordScheme caseInsensitiveCompare: @"plain-md5"] == NSOrderedSame ||
[passwordScheme caseInsensitiveCompare: @"sha"] == NSOrderedSame ||
[passwordScheme caseInsensitiveCompare: @"cram-md5"] == NSOrderedSame)
@@ -294,6 +298,7 @@
{
encoding = encBase64;
}
+
return [NSArray arrayWithObjects: [NSNumber numberWithInt: encoding], trueScheme, nil];
}
@@ -306,7 +311,7 @@
{
NSData *cryptData;
cryptData = [self dataUsingEncoding: NSUTF8StringEncoding];
- return [NSData encodeDataAsHexString: [cryptData asSHA1] ];
+ return [NSData encodeDataAsHexString: [cryptData asSHA1]];
}
/**
@@ -318,7 +323,36 @@
{
NSData *cryptData;
cryptData = [self dataUsingEncoding: NSUTF8StringEncoding];
- return [NSData encodeDataAsHexString: [cryptData asMD5] ];
+ return [NSData encodeDataAsHexString: [cryptData asMD5]];
+}
+
+/**
+ * Encrypts the data using the NT-hash password scheme.
+ *
+ * @return If successful, NT-hash encrypted and with hex encoded string
+ */
+- (NSString *) asNTHash
+{
+ NSData *d;
+
+ d = [self dataUsingEncoding: NSUTF16LittleEndianStringEncoding];
+
+ return [[NSData encodeDataAsHexString: [d asMD4]] uppercaseString];
+}
+
+/**
+ * Encrypts the data using the LM-hash password scheme.
+ *
+ * @return If successful, LM-hash encrypted and with hex encoded string
+ */
+- (NSString *) asLMHash
+{
+ NSData *d;
+
+ // See http://en.wikipedia.org/wiki/LM_hash#Algorithm
+ d = [[self uppercaseString] dataUsingEncoding: NSWindowsCP1252StringEncoding];
+
+ return [[NSData encodeDataAsHexString: [d asLM]] uppercaseString];
}
@end
diff --git a/SoObjects/SOGo/SOGoCASSession.m b/SoObjects/SOGo/SOGoCASSession.m
index 8c77df29b..6eac8b2c5 100644
--- a/SoObjects/SOGo/SOGoCASSession.m
+++ b/SoObjects/SOGo/SOGoCASSession.m
@@ -113,7 +113,6 @@
+ (void) handleLogoutRequest: (NSString *) logoutRequest
{
CASLogoutRequest *rq;
- SOGoCache *cache;
NSBundle *bundle;
NSString *mapFile, *sessionIndex;
diff --git a/SoObjects/SOGo/SOGoDomainDefaults.h b/SoObjects/SOGo/SOGoDomainDefaults.h
index 4743917af..fbc665852 100644
--- a/SoObjects/SOGo/SOGoDomainDefaults.h
+++ b/SoObjects/SOGo/SOGoDomainDefaults.h
@@ -1,6 +1,6 @@
/* SOGoDomainDefaults.h - this file is part of SOGo
*
- * Copyright (C) 2009-2014 Inverse inc.
+ * Copyright (C) 2009-2015 Inverse inc.
*
* 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
@@ -49,6 +49,7 @@
- (BOOL) forceExternalLoginWithEmail;
- (BOOL) sieveScriptsEnabled;
- (BOOL) forwardEnabled;
+- (int) forwardConstraints;
- (BOOL) vacationEnabled;
- (NSString *) mailingMechanism;
- (NSString *) smtpServer;
diff --git a/SoObjects/SOGo/SOGoDomainDefaults.m b/SoObjects/SOGo/SOGoDomainDefaults.m
index 35a078b3f..ff02b6a96 100644
--- a/SoObjects/SOGo/SOGoDomainDefaults.m
+++ b/SoObjects/SOGo/SOGoDomainDefaults.m
@@ -1,6 +1,6 @@
/* SOGoDomainDefaults.m - this file is part of SOGo
*
- * Copyright (C) 2009-2014 Inverse inc.
+ * Copyright (C) 2009-2015 Inverse inc.
*
* 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
@@ -201,6 +201,15 @@
return [self boolForKey: @"SOGoForwardEnabled"];
}
+- (int) forwardConstraints
+{
+ unsigned int v;
+
+ v = [self integerForKey: @"SOGoForwardConstraints"];
+
+ return (v > 2 ? 0 : v);
+}
+
- (BOOL) vacationEnabled
{
return [self boolForKey: @"SOGoVacationEnabled"];
diff --git a/SoObjects/SOGo/SOGoGroup.m b/SoObjects/SOGo/SOGoGroup.m
index 648b97ae4..cacdd7770 100644
--- a/SoObjects/SOGo/SOGoGroup.m
+++ b/SoObjects/SOGo/SOGoGroup.m
@@ -137,6 +137,8 @@
NGLdapEntry *entry;
NSObject *source;
id o;
+ NSEnumerator *gclasses;
+ NSString *gclass;
int i;
@@ -194,12 +196,11 @@
}
}
- // Found a group, let's return it.
- if ([classes containsObject: @"group"] ||
- [classes containsObject: @"groupofnames"] ||
- [classes containsObject: @"groupofuniquenames"] ||
- [classes containsObject: @"posixgroup"])
- {
+ gclasses = [[source groupObjectClasses] objectEnumerator];
+ while (gclass = [gclasses nextObject])
+ if ([classes containsObject: gclass])
+ {
+ // Found a group, let's return it.
o = [[self alloc] initWithIdentifier: theValue
domain: domain
source: source
diff --git a/SoObjects/SOGo/SOGoSieveManager.m b/SoObjects/SOGo/SOGoSieveManager.m
index 2f949ad20..f2f5a7b41 100644
--- a/SoObjects/SOGo/SOGoSieveManager.m
+++ b/SoObjects/SOGo/SOGoSieveManager.m
@@ -835,13 +835,16 @@ static NSString *sieveScriptName = @"sogo";
if (values && [[values objectForKey: @"enabled"] boolValue])
{
+ NSMutableString *vacation_script;
NSArray *addresses;
NSString *text;
- BOOL ignore;
+
+ BOOL ignore, alwaysSend;
int days, i;
-
+
days = [[values objectForKey: @"daysBetweenResponse"] intValue];
addresses = [values objectForKey: @"autoReplyEmailAddresses"];
+ alwaysSend = [[values objectForKey: @"alwaysSend"] boolValue];
ignore = [[values objectForKey: @"ignoreLists"] boolValue];
text = [values objectForKey: @"autoReplyText"];
b = YES;
@@ -849,28 +852,38 @@ static NSString *sieveScriptName = @"sogo";
if (days == 0)
days = 7;
+ vacation_script = [NSMutableString string];
+
[req addObjectUniquely: @"vacation"];
// Skip mailing lists
if (ignore)
- [script appendString: @"if allof ( not exists [\"list-help\", \"list-unsubscribe\", \"list-subscribe\", \"list-owner\", \"list-post\", \"list-archive\", \"list-id\", \"Mailing-List\"], not header :comparator \"i;ascii-casemap\" :is \"Precedence\" [\"list\", \"bulk\", \"junk\"], not header :comparator \"i;ascii-casemap\" :matches \"To\" \"Multiple recipients of*\" ) {"];
+ [vacation_script appendString: @"if allof ( not exists [\"list-help\", \"list-unsubscribe\", \"list-subscribe\", \"list-owner\", \"list-post\", \"list-archive\", \"list-id\", \"Mailing-List\"], not header :comparator \"i;ascii-casemap\" :is \"Precedence\" [\"list\", \"bulk\", \"junk\"], not header :comparator \"i;ascii-casemap\" :matches \"To\" \"Multiple recipients of*\" ) {"];
- [script appendFormat: @"vacation :days %d :addresses [", days];
+ [vacation_script appendFormat: @"vacation :days %d :addresses [", days];
for (i = 0; i < [addresses count]; i++)
{
- [script appendFormat: @"\"%@\"", [addresses objectAtIndex: i]];
+ [vacation_script appendFormat: @"\"%@\"", [addresses objectAtIndex: i]];
if (i == [addresses count]-1)
- [script appendString: @"] "];
+ [vacation_script appendString: @"] "];
else
- [script appendString: @", "];
+ [vacation_script appendString: @", "];
}
- [script appendFormat: @"text:\r\n%@\r\n.\r\n;\r\n", text];
+ [vacation_script appendFormat: @"text:\r\n%@\r\n.\r\n;\r\n", text];
if (ignore)
- [script appendString: @"}\r\n"];
+ [vacation_script appendString: @"}\r\n"];
+
+ //
+ // See http://sogo.nu/bugs/view.php?id=2332 for details
+ //
+ if (alwaysSend)
+ [script insertString: vacation_script atIndex: 0];
+ else
+ [script appendString: vacation_script];
}
diff --git a/SoObjects/SOGo/SOGoSource.h b/SoObjects/SOGo/SOGoSource.h
index 2415f408c..b55a5a14a 100644
--- a/SoObjects/SOGo/SOGoSource.h
+++ b/SoObjects/SOGo/SOGoSource.h
@@ -110,6 +110,7 @@
- (NSString *) baseDN;
- (NSString *) MSExchangeHostname;
+- (NSArray *) groupObjectClasses;
@end
#endif /* SOGOSOURCE_H */
diff --git a/SoObjects/SOGo/SOGoUser.h b/SoObjects/SOGo/SOGoUser.h
index fad66e4e8..5702f10cc 100644
--- a/SoObjects/SOGo/SOGoUser.h
+++ b/SoObjects/SOGo/SOGoUser.h
@@ -1,5 +1,5 @@
/*
- Copyright (C) 2006-2013 Inverse inc.
+ Copyright (C) 2006-2015 Inverse inc.
Copyright (C) 2005 SKYRIX Software AG
This file is part of SOGo.
diff --git a/SoObjects/SOGo/SOGoUser.m b/SoObjects/SOGo/SOGoUser.m
index da0ed8c6c..91d3b1b5a 100644
--- a/SoObjects/SOGo/SOGoUser.m
+++ b/SoObjects/SOGo/SOGoUser.m
@@ -37,6 +37,7 @@
#import
#import
+#import
#import "NSArray+Utilities.h"
#import "SOGoCache.h"
@@ -234,7 +235,6 @@
[currentPassword release];
[cn release];
[loginInDomain release];
- //[language release];
[super dealloc];
}
@@ -577,13 +577,13 @@
- (void) _appendSystemMailAccount
{
NSString *fullName, *replyTo, *imapLogin, *imapServer, *cImapServer, *signature,
- *encryption, *scheme, *action, *query, *customEmail, *sieveServer;
+ *encryption, *scheme, *action, *query, *customEmail, *defaultEmail, *sieveServer;
NSMutableDictionary *mailAccount, *identity, *mailboxes, *receipts;
NSNumber *port;
NSMutableArray *identities;
NSArray *mails;
NSURL *url, *cUrl;
- unsigned int count, max;
+ unsigned int count, max, default_identity;
NSInteger defaultPort;
[self userDefaults];
@@ -661,8 +661,9 @@
[mailAccount setObject: sieveServer forKey: @"sieveServerName"];
}
-
- /* identities */
+ // Identities
+ defaultEmail = [NSString stringWithFormat: @"%@@%@", [self loginInDomain], [self domain]];
+ default_identity = 0;
identities = [NSMutableArray new];
mails = [self allEmails];
[mailAccount setObject: [mails objectAtIndex: 0] forKey: @"name"];
@@ -702,6 +703,10 @@
if (signature)
[identity setObject: signature forKey: @"signature"];
[identities addObject: identity];
+
+ if ([[identity objectForKey: @"email"] caseInsensitiveCompare: defaultEmail] == NSOrderedSame)
+ default_identity = [identities count]-1;
+
[identity release];
}
}
@@ -723,10 +728,14 @@
if (signature)
[identity setObject: signature forKey: @"signature"];
[identities addObject: identity];
+
+ if ([[identity objectForKey: @"email"] caseInsensitiveCompare: defaultEmail] == NSOrderedSame)
+ default_identity = [identities count]-1;
+
[identity release];
}
- [[identities objectAtIndex: 0] setObject: [NSNumber numberWithBool: YES]
- forKey: @"isDefault"];
+ [[identities objectAtIndex: default_identity] setObject: [NSNumber numberWithBool: YES]
+ forKey: @"isDefault"];
[mailAccount setObject: identities forKey: @"identities"];
[identities release];
diff --git a/SoObjects/SOGo/SOGoUserManager.h b/SoObjects/SOGo/SOGoUserManager.h
index 237347053..d30821108 100644
--- a/SoObjects/SOGo/SOGoUserManager.h
+++ b/SoObjects/SOGo/SOGoUserManager.h
@@ -65,10 +65,13 @@
- (NSDictionary *) contactInfosForUserWithUIDorEmail: (NSString *) uid;
- (NSDictionary *) contactInfosForUserWithUIDorEmail: (NSString *) uid
inDomain: (NSString *) domain;
+- (NSDictionary *) fetchContactWithUID: (NSString *) uid
+ inDomain: (NSString *) domain;
- (NSArray *) fetchContactsMatching: (NSString *) match
inDomain: (NSString *) domain;
- (NSArray *) fetchUsersMatching: (NSString *) filter
inDomain: (NSString *) domain;
+- (NSArray *) _compactAndCompleteContacts: (NSEnumerator *) contacts;
- (NSString *) getCNForUID: (NSString *) uid;
- (NSString *) getEmailForUID: (NSString *) uid;
diff --git a/SoObjects/SOGo/SOGoUserManager.m b/SoObjects/SOGo/SOGoUserManager.m
index 4d6a5b911..99e9995b2 100644
--- a/SoObjects/SOGo/SOGoUserManager.m
+++ b/SoObjects/SOGo/SOGoUserManager.m
@@ -18,6 +18,7 @@
* Boston, MA 02111-1307, USA.
*/
#import
+#import
#import
#import
#import
@@ -914,6 +915,37 @@ static Class NSNullK;
return currentUser;
}
+/**
+ * Fetch the contact information identified by the specified UID in the global addressbooks.
+ */
+- (NSDictionary *) fetchContactWithUID: (NSString *) uid
+ inDomain: (NSString *) domain
+{
+ NSDictionary *contact;
+ NSMutableArray *contacts;
+ NSEnumerator *sources;
+ NSString *sourceID;
+ id currentSource;
+
+ contacts = [NSMutableArray array];
+ contact = nil;
+ sources = [[self addressBookSourceIDsInDomain: domain] objectEnumerator];
+ while ((sourceID = [sources nextObject]))
+ {
+ currentSource = [_sources objectForKey: sourceID];
+ contact = [currentSource lookupContactEntry: uid];
+ if (contact)
+ [contacts addObject: contact];
+ }
+
+ if ([contacts count])
+ contact = [[self _compactAndCompleteContacts: [contacts objectEnumerator]] lastObject];
+ else
+ contact = nil;
+
+ return contact;
+}
+
- (NSArray *) _compactAndCompleteContacts: (NSEnumerator *) contacts
{
NSMutableDictionary *compactContacts, *returnContact;
diff --git a/SoObjects/SOGo/lmhash.c b/SoObjects/SOGo/lmhash.c
new file mode 100644
index 000000000..070ba1e25
--- /dev/null
+++ b/SoObjects/SOGo/lmhash.c
@@ -0,0 +1,742 @@
+#include "lmhash.h"
+
+#include
+#include
+
+/* ========================================================================== **
+ *
+ * LMhash.h
+ *
+ * Copyright:
+ * Copyright (C) 2004 by Christopher R. Hertel
+ *
+ * Email: crh@ubiqx.mn.org
+ *
+ * $Id: LMhash.h,v 0.1 2004/05/30 02:26:31 crh Exp $
+ *
+ * -------------------------------------------------------------------------- **
+ *
+ * Description:
+ *
+ * Implemention of the LAN Manager hash (LM hash) and LM response
+ * algorithms.
+ *
+ * -------------------------------------------------------------------------- **
+ *
+ * License:
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ * -------------------------------------------------------------------------- **
+ *
+ * Notes:
+ *
+ * This module implements the LM hash. The NT hash is simply the MD4() of
+ * the password, so we don't need a separate implementation of that. This
+ * module also implements the LM response, which can be combined with the
+ * NT hash to produce the NTLM response.
+ *
+ * This implementation was created based on the description in my own book.
+ * The book description was, in turn, written after studying many existing
+ * examples in various documentation. Jeremy Allison and Andrew Tridgell
+ * deserve lots of credit for having figured out the secrets of Lan Manager
+ * authentication many years ago.
+ *
+ * See:
+ * Implementing CIFS - the Common Internet File System
+ * by your truly. ISBN 0-13-047116-X, Prentice Hall PTR., August 2003
+ * Section 15.3, in particular.
+ * (Online at: http://ubiqx.org/cifs/SMB.html#SMB.8.3)
+ *
+ * ========================================================================== **
+ */
+
+
+/* Initial permutation map.
+ * In the first step of DES, the bits of the initial plaintext are rearranged
+ * according to the map given below. This map and those like it are read by
+ * the Permute() function (below) which uses the maps as a guide when moving
+ * bits from one place to another.
+ *
+ * Note that the values here are all one less than those shown in Schneier.
+ * That's because C likes to start counting from 0, not 1.
+ *
+ * According to Schneier (Ch12, pg 271), the purpose of the initial
+ * permutation was to make it easier to load plaintext and ciphertext into
+ * a DES ecryption chip. I have no idea why that would be the case.
+ */
+static const uint8_t InitialPermuteMap[64] =
+ {
+ 57, 49, 41, 33, 25, 17, 9, 1,
+ 59, 51, 43, 35, 27, 19, 11, 3,
+ 61, 53, 45, 37, 29, 21, 13, 5,
+ 63, 55, 47, 39, 31, 23, 15, 7,
+ 56, 48, 40, 32, 24, 16, 8, 0,
+ 58, 50, 42, 34, 26, 18, 10, 2,
+ 60, 52, 44, 36, 28, 20, 12, 4,
+ 62, 54, 46, 38, 30, 22, 14, 6
+ };
+
+
+/* Key permutation map.
+ * Like the input data and encryption result, the key is permuted before
+ * the algorithm really gets going. The original algorithm called for an
+ * eight-byte key in which each byte contained a parity bit. During the
+ * key permutiation, the parity bits were discarded. The DES algorithm,
+ * as used with SMB, does not make use of the parity bits. Instead, SMB
+ * passes 7-byte keys to DES. For DES implementations that expect parity,
+ * the parity bits must be added. In this case, however, we're just going
+ * to start with a 7-byte (56 bit) key. KeyPermuteMap, below, is adjusted
+ * accordingly and, of course, each entry in the map is reduced by 1 with
+ * respect to the documented values because C likes to start counting from
+ * 0, not 1.
+ */
+static const uint8_t KeyPermuteMap[56] =
+ {
+ 49, 42, 35, 28, 21, 14, 7, 0,
+ 50, 43, 36, 29, 22, 15, 8, 1,
+ 51, 44, 37, 30, 23, 16, 9, 2,
+ 52, 45, 38, 31, 55, 48, 41, 34,
+ 27, 20, 13, 6, 54, 47, 40, 33,
+ 26, 19, 12, 5, 53, 46, 39, 32,
+ 25, 18, 11, 4, 24, 17, 10, 3,
+ };
+
+
+/* Key rotation table.
+ * At the start of each round of encryption, the key is split and each
+ * 28-bit half is rotated left. The number of bits of rotation per round
+ * is given in the table below.
+ */
+static const uint8_t KeyRotation[16] =
+ { 1, 1, 2, 2, 2, 2, 2, 2, 1, 2, 2, 2, 2, 2, 2, 1 };
+
+
+/* Key compression table.
+ * This table is used to select 48 of the 56 bits of the key.
+ * The left and right halves of the source text are each 32 bits,
+ * but they are expanded to 48 bits and the results are XOR'd
+ * against the compressed (48-bit) key.
+ */
+static const uint8_t KeyCompression[48] =
+ {
+ 13, 16, 10, 23, 0, 4, 2, 27,
+ 14, 5, 20, 9, 22, 18, 11, 3,
+ 25, 7, 15, 6, 26, 19, 12, 1,
+ 40, 51, 30, 36, 46, 54, 29, 39,
+ 50, 44, 32, 47, 43, 48, 38, 55,
+ 33, 52, 45, 41, 49, 35, 28, 31
+ };
+
+
+/* Data expansion table.
+ * This table is used after the data block (64-bits) has been split
+ * into two 32-bit (4-byte) halves (generally denoted L and R).
+ * Each 32-bit half is "expanded", using this table, to a 48 bit
+ * data block, which is then XOR'd with the 48 bit subkey for the
+ * round.
+ */
+static const uint8_t DataExpansion[48] =
+ {
+ 31, 0, 1, 2, 3, 4, 3, 4,
+ 5, 6, 7, 8, 7, 8, 9, 10,
+ 11, 12, 11, 12, 13, 14, 15, 16,
+ 15, 16, 17, 18, 19, 20, 19, 20,
+ 21, 22, 23, 24, 23, 24, 25, 26,
+ 27, 28, 27, 28, 29, 30, 31, 0
+ };
+
+
+/* The (in)famous S-boxes.
+ * These are used to perform substitutions.
+ * Six bits worth of input will return four bits of output.
+ * The four bit values are stored in these tables. Each table has
+ * 64 entries...and 6 bits provides a number between 0 and 63.
+ * There are eight S-boxes, one per 6 bits of a 48-bit value.
+ * Thus, 48 bits are reduced to 32 bits. Obviously, this step
+ * follows the DataExpansion step.
+ *
+ * Note that the literature generally shows this as 8 arrays each
+ * with four rows and 16 colums. There is a complex formula for
+ * mapping the 6 bit input values to the correct row and column.
+ * I've pre-computed that mapping, and the tables below provide
+ * direct 6-bit input to 4-bit output. See pp 274-274 in Schneier.
+ */
+static const uint8_t SBox[8][64] =
+ {
+ { /* S0 */
+ 14, 0, 4, 15, 13, 7, 1, 4, 2, 14, 15, 2, 11, 13, 8, 1,
+ 3, 10, 10, 6, 6, 12, 12, 11, 5, 9, 9, 5, 0, 3, 7, 8,
+ 4, 15, 1, 12, 14, 8, 8, 2, 13, 4, 6, 9, 2, 1, 11, 7,
+ 15, 5, 12, 11, 9, 3, 7, 14, 3, 10, 10, 0, 5, 6, 0, 13
+ },
+ { /* S1 */
+ 15, 3, 1, 13, 8, 4, 14, 7, 6, 15, 11, 2, 3, 8, 4, 14,
+ 9, 12, 7, 0, 2, 1, 13, 10, 12, 6, 0, 9, 5, 11, 10, 5,
+ 0, 13, 14, 8, 7, 10, 11, 1, 10, 3, 4, 15, 13, 4, 1, 2,
+ 5, 11, 8, 6, 12, 7, 6, 12, 9, 0, 3, 5, 2, 14, 15, 9
+ },
+ { /* S2 */
+ 10, 13, 0, 7, 9, 0, 14, 9, 6, 3, 3, 4, 15, 6, 5, 10,
+ 1, 2, 13, 8, 12, 5, 7, 14, 11, 12, 4, 11, 2, 15, 8, 1,
+ 13, 1, 6, 10, 4, 13, 9, 0, 8, 6, 15, 9, 3, 8, 0, 7,
+ 11, 4, 1, 15, 2, 14, 12, 3, 5, 11, 10, 5, 14, 2, 7, 12
+ },
+ { /* S3 */
+ 7, 13, 13, 8, 14, 11, 3, 5, 0, 6, 6, 15, 9, 0, 10, 3,
+ 1, 4, 2, 7, 8, 2, 5, 12, 11, 1, 12, 10, 4, 14, 15, 9,
+ 10, 3, 6, 15, 9, 0, 0, 6, 12, 10, 11, 1, 7, 13, 13, 8,
+ 15, 9, 1, 4, 3, 5, 14, 11, 5, 12, 2, 7, 8, 2, 4, 14
+ },
+ { /* S4 */
+ 2, 14, 12, 11, 4, 2, 1, 12, 7, 4, 10, 7, 11, 13, 6, 1,
+ 8, 5, 5, 0, 3, 15, 15, 10, 13, 3, 0, 9, 14, 8, 9, 6,
+ 4, 11, 2, 8, 1, 12, 11, 7, 10, 1, 13, 14, 7, 2, 8, 13,
+ 15, 6, 9, 15, 12, 0, 5, 9, 6, 10, 3, 4, 0, 5, 14, 3
+ },
+ { /* S5 */
+ 12, 10, 1, 15, 10, 4, 15, 2, 9, 7, 2, 12, 6, 9, 8, 5,
+ 0, 6, 13, 1, 3, 13, 4, 14, 14, 0, 7, 11, 5, 3, 11, 8,
+ 9, 4, 14, 3, 15, 2, 5, 12, 2, 9, 8, 5, 12, 15, 3, 10,
+ 7, 11, 0, 14, 4, 1, 10, 7, 1, 6, 13, 0, 11, 8, 6, 13
+ },
+ { /* S6 */
+ 4, 13, 11, 0, 2, 11, 14, 7, 15, 4, 0, 9, 8, 1, 13, 10,
+ 3, 14, 12, 3, 9, 5, 7, 12, 5, 2, 10, 15, 6, 8, 1, 6,
+ 1, 6, 4, 11, 11, 13, 13, 8, 12, 1, 3, 4, 7, 10, 14, 7,
+ 10, 9, 15, 5, 6, 0, 8, 15, 0, 14, 5, 2, 9, 3, 2, 12
+ },
+ { /* S7 */
+ 13, 1, 2, 15, 8, 13, 4, 8, 6, 10, 15, 3, 11, 7, 1, 4,
+ 10, 12, 9, 5, 3, 6, 14, 11, 5, 0, 0, 14, 12, 9, 7, 2,
+ 7, 2, 11, 1, 4, 14, 1, 7, 9, 4, 12, 10, 14, 8, 2, 13,
+ 0, 15, 6, 12, 10, 9, 13, 0, 15, 3, 3, 5, 5, 6, 8, 11
+ }
+ };
+
+
+/* P-Box permutation.
+ * This permutation is applied to the result of the S-Box Substitutions.
+ * It's a straight-forward re-arrangement of the bits.
+ */
+static const uint8_t PBox[32] =
+ {
+ 15, 6, 19, 20, 28, 11, 27, 16,
+ 0, 14, 22, 25, 4, 17, 30, 9,
+ 1, 7, 23, 13, 31, 26, 2, 8,
+ 18, 12, 29, 5, 21, 10, 3, 24
+ };
+
+
+/* Final permutation map.
+ * This is supposed to be the inverse of the Initial Permutation,
+ * but there's been a bit of fiddling done.
+ * As always, the values given are one less than those in the literature
+ * (because C starts counting from 0, not 1). In addition, the penultimate
+ * step in DES is to swap the left and right hand sides of the ciphertext.
+ * The inverse of the Initial Permutation is then applied to produce the
+ * final result.
+ * To save a step, the map below does the left/right swap as well as the
+ * inverse permutation.
+ */
+static const uint8_t FinalPermuteMap[64] =
+ {
+ 7, 39, 15, 47, 23, 55, 31, 63,
+ 6, 38, 14, 46, 22, 54, 30, 62,
+ 5, 37, 13, 45, 21, 53, 29, 61,
+ 4, 36, 12, 44, 20, 52, 28, 60,
+ 3, 35, 11, 43, 19, 51, 27, 59,
+ 2, 34, 10, 42, 18, 50, 26, 58,
+ 1, 33, 9, 41, 17, 49, 25, 57,
+ 0, 32, 8, 40, 16, 48, 24, 56
+ };
+
+
+/* -------------------------------------------------------------------------- **
+ * Macros:
+ *
+ * CLRBIT( STR, IDX )
+ * Input: STR - (uchar *) pointer to an array of 8-bit bytes.
+ * IDX - (int) bitwise index of a bit within the STR array
+ * that is to be cleared (that is, given a value of 0).
+ * Notes: This macro clears a bit within an array of bits (which is
+ * built within an array of bytes).
+ * - The macro converts to an assignment of the form A &= B.
+ * - The string of bytes is viewed as an array of bits, read from
+ * highest order bit first. The highest order bit of a byte
+ * would, therefore, be bit 0 (within that byte).
+ *
+ * SETBIT( STR, IDX )
+ * Input: STR - (uchar *) pointer to an array of 8-bit bytes.
+ * IDX - (int) bitwise index of a bit within the STR array
+ * that is to be set (that is, given a value of 1).
+ * Notes: This macro sets a bit within an array of bits (which is
+ * built within an array of bytes).
+ * - The macro converts to an assignment of the form A |= B.
+ * - The string of bytes is viewed as an array of bits, read from
+ * highest order bit first. The highest order bit of a byte
+ * would, therefore, be bit 0 (within that byte).
+ *
+ * GETBIT( STR, IDX )
+ * Input: STR - (uchar *) pointer to an array of 8-bit bytes.
+ * IDX - (int) bit-wise index of a bit within the STR array
+ * that is to be read.
+ * Output: True (1) if the indexed bit was set, else false (0).
+ *
+ * -------------------------------------------------------------------------- **
+ */
+
+#define CLRBIT( STR, IDX ) ( (STR)[(IDX)/8] &= ~(0x01 << (7 - ((IDX)%8))) )
+
+#define SETBIT( STR, IDX ) ( (STR)[(IDX)/8] |= (0x01 << (7 - ((IDX)%8))) )
+
+#define GETBIT( STR, IDX ) (( ((STR)[(IDX)/8]) >> (7 - ((IDX)%8)) ) & 0x01)
+
+
+/* -------------------------------------------------------------------------- **
+ * Static Functions:
+ */
+
+static void Permute( uchar *dst,
+ const uchar *src,
+ const uint8_t *map,
+ const int mapsize )
+ /* ------------------------------------------------------------------------ **
+ * Performs a DES permutation, which re-arranges the bits in an array of
+ * bytes.
+ *
+ * Input: dst - Destination into which to put the re-arranged bits.
+ * src - Source from which to read the bits.
+ * map - Permutation map.
+ * mapsize - Number of bytes represented by the