Merge tag 'SOGo-2.2.17a' into jjgarcia/merge-from-upstream
SOGo 2.2.17a Conflicts: NEWSpull/65/head
commit
321e16c382
|
@ -1,6 +1,6 @@
|
||||||
/*
|
/*
|
||||||
|
|
||||||
Copyright (c) 2014, Inverse inc.
|
Copyright (c) 2015, Inverse inc.
|
||||||
All rights reserved.
|
All rights reserved.
|
||||||
|
|
||||||
Redistribution and use in source and binary forms, with or without
|
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)
|
@implementation NGMimeMessage (ActiveSync)
|
||||||
|
|
||||||
- (NSArray *) allRecipients
|
- (void) _addRecipients: (NSEnumerator *) enumerator
|
||||||
|
toArray: (NSMutableArray *) recipients
|
||||||
{
|
{
|
||||||
NSEnumerator *enumerator, *addressList;
|
|
||||||
NSMutableArray *recipients;
|
|
||||||
NGMailAddressParser *parser;
|
NGMailAddressParser *parser;
|
||||||
|
NSEnumerator *addressList;
|
||||||
NGMailAddress *address;
|
NGMailAddress *address;
|
||||||
NSString *s;
|
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];
|
recipients = [NSMutableArray array];
|
||||||
|
|
||||||
enumerator = [[self headersForKey: @"to"] objectEnumerator];
|
[self _addRecipients: [[self headersForKey: @"to"] objectEnumerator]
|
||||||
while ((s = [enumerator nextObject]))
|
toArray: recipients];
|
||||||
{
|
|
||||||
parser = [NGMailAddressParser mailAddressParserWithString: s];
|
|
||||||
addressList = [[parser parseAddressList] objectEnumerator];
|
|
||||||
|
|
||||||
while ((address = [addressList nextObject]))
|
[self _addRecipients: [[self headersForKey: @"cc"] objectEnumerator]
|
||||||
[recipients addObject: [address address]];
|
toArray: recipients];
|
||||||
}
|
|
||||||
|
|
||||||
enumerator = [[self headersForKey: @"cc"] objectEnumerator];
|
[self _addRecipients: [[self headersForKey: @"bcc"] objectEnumerator]
|
||||||
while ((s = [enumerator nextObject]))
|
toArray: recipients];
|
||||||
{
|
|
||||||
parser = [NGMailAddressParser mailAddressParserWithString: s];
|
|
||||||
addressList = [[parser parseAddressList] objectEnumerator];
|
|
||||||
|
|
||||||
while ((address = [addressList nextObject]))
|
|
||||||
[recipients addObject: [address address]];
|
|
||||||
}
|
|
||||||
|
|
||||||
return recipients;
|
return recipients;
|
||||||
}
|
}
|
||||||
|
|
|
@ -49,6 +49,8 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
- (NSString *) deviceType;
|
- (NSString *) deviceType;
|
||||||
- (NSString *) attachmentName;
|
- (NSString *) attachmentName;
|
||||||
- (NSString *) command;
|
- (NSString *) command;
|
||||||
|
- (NSString *) collectionid;
|
||||||
|
- (NSString *) itemid;
|
||||||
- (NSData *) convertHexStringToBytes;
|
- (NSData *) convertHexStringToBytes;
|
||||||
|
|
||||||
@end
|
@end
|
||||||
|
|
|
@ -298,6 +298,31 @@ static NSArray *easCommandParameters = nil;
|
||||||
return s;
|
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.
|
// FIXME: combine with our OpenChange code.
|
||||||
//
|
//
|
||||||
|
|
|
@ -540,6 +540,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
|
||||||
{
|
{
|
||||||
NSMutableDictionary *folderMetadata, *dateCache, *syncCache;
|
NSMutableDictionary *folderMetadata, *dateCache, *syncCache;
|
||||||
|
NSString *davCollectionTagToStore;
|
||||||
NSAutoreleasePool *pool;
|
NSAutoreleasePool *pool;
|
||||||
NSMutableString *s;
|
NSMutableString *s;
|
||||||
|
|
||||||
|
@ -618,6 +619,8 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
|
||||||
more_available = NO;
|
more_available = NO;
|
||||||
|
|
||||||
|
davCollectionTagToStore = [theCollection davCollectionTag];
|
||||||
|
|
||||||
switch (theFolderType)
|
switch (theFolderType)
|
||||||
{
|
{
|
||||||
// Handle all the GCS components
|
// Handle all the GCS components
|
||||||
|
@ -788,7 +791,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
[folderMetadata removeObjectForKey: @"MoreAvailable"];
|
[folderMetadata removeObjectForKey: @"MoreAvailable"];
|
||||||
[folderMetadata setObject: [theCollection davCollectionTag] forKey: @"SyncKey"];
|
[folderMetadata setObject: davCollectionTagToStore forKey: @"SyncKey"];
|
||||||
}
|
}
|
||||||
|
|
||||||
[self _setFolderMetadata: folderMetadata
|
[self _setFolderMetadata: folderMetadata
|
||||||
|
@ -963,7 +966,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
[folderMetadata removeObjectForKey: @"MoreAvailable"];
|
[folderMetadata removeObjectForKey: @"MoreAvailable"];
|
||||||
[folderMetadata setObject: [theCollection davCollectionTag] forKey: @"SyncKey"];
|
[folderMetadata setObject: davCollectionTagToStore forKey: @"SyncKey"];
|
||||||
}
|
}
|
||||||
|
|
||||||
[self _setFolderMetadata: folderMetadata forKey: [self _getNameInCache: theCollection withType: theFolderType]];
|
[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.
|
// Make sure that client is updated with the right syncKey. - This keeps vtodo's and vevent's syncKey in sync.
|
||||||
syncKeyInCache = [folderMetadata objectForKey: @"SyncKey"];
|
syncKeyInCache = [folderMetadata objectForKey: @"SyncKey"];
|
||||||
if (syncKeyInCache && !([davCollectionTag isEqualToString:syncKeyInCache]))
|
if (syncKeyInCache && !([davCollectionTag isEqualToString:syncKeyInCache]) && ![davCollectionTag isEqualToString: @"-1"])
|
||||||
{
|
{
|
||||||
davCollectionTag = syncKeyInCache;
|
davCollectionTag = syncKeyInCache;
|
||||||
*changeDetected = YES;
|
*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.
|
// We always return the last generated response.
|
||||||
// If we only return <Sync><Collections/></Sync>,
|
// If we only return <Sync><Collections/></Sync>,
|
||||||
|
|
|
@ -1127,6 +1127,8 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
id currentCollection;
|
id currentCollection;
|
||||||
NSMutableString *s;
|
NSMutableString *s;
|
||||||
NSData *d;
|
NSData *d;
|
||||||
|
NSArray *allCollections;
|
||||||
|
int j;
|
||||||
|
|
||||||
SOGoMicrosoftActiveSyncFolderType folderType;
|
SOGoMicrosoftActiveSyncFolderType folderType;
|
||||||
int status, count;
|
int status, count;
|
||||||
|
@ -1135,7 +1137,15 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
status = 1;
|
status = 1;
|
||||||
count = 0;
|
count = 0;
|
||||||
|
|
||||||
collectionId = [[(id)[theDocumentElement getElementsByTagName: @"CollectionId"] lastObject] textValue];
|
[s appendString: @"<?xml version=\"1.0\" encoding=\"utf-8\"?>"];
|
||||||
|
[s appendString: @"<!DOCTYPE ActiveSync PUBLIC \"-//MICROSOFT//DTD ActiveSync//EN\" \"http://www.microsoft.com/\">"];
|
||||||
|
[s appendString: @"<GetItemEstimate xmlns=\"GetItemEstimate:\">"];
|
||||||
|
|
||||||
|
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];
|
realCollectionId = [collectionId realCollectionIdWithFolderType: &folderType];
|
||||||
|
|
||||||
if (folderType == ActiveSyncMailFolder)
|
if (folderType == ActiveSyncMailFolder)
|
||||||
|
@ -1156,45 +1166,43 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
//
|
//
|
||||||
if (folderType == ActiveSyncMailFolder)
|
if (folderType == ActiveSyncMailFolder)
|
||||||
{
|
{
|
||||||
EOQualifier *notDeletedQualifier, *sinceDateQualifier;
|
|
||||||
EOAndQualifier *qualifier;
|
|
||||||
NSCalendarDate *filter;
|
NSCalendarDate *filter;
|
||||||
NSArray *uids;
|
NSString *syncKey;
|
||||||
|
NSArray *allMessages;
|
||||||
|
|
||||||
filter = [NSCalendarDate dateFromFilterType: [[(id)[theDocumentElement getElementsByTagName: @"FilterType"] lastObject] textValue]];
|
filter = [NSCalendarDate dateFromFilterType: [[(id)[[allCollections objectAtIndex: j] getElementsByTagName: @"FilterType"] lastObject] textValue]];
|
||||||
|
syncKey = [[(id)[[allCollections objectAtIndex: j] getElementsByTagName: @"SyncKey"] lastObject] textValue];
|
||||||
|
|
||||||
notDeletedQualifier = [EOQualifier qualifierWithQualifierFormat:
|
allMessages = [currentCollection syncTokenFieldsWithProperties: nil matchingSyncToken: syncKey fromDate: filter];
|
||||||
@"(not (flags = %@))",
|
|
||||||
@"deleted"];
|
|
||||||
sinceDateQualifier = [EOQualifier qualifierWithQualifierFormat:
|
|
||||||
@"(DATE >= %@)", filter];
|
|
||||||
|
|
||||||
qualifier = [[EOAndQualifier alloc] initWithQualifiers: notDeletedQualifier, sinceDateQualifier,
|
count = [allMessages count];
|
||||||
nil];
|
|
||||||
AUTORELEASE(qualifier);
|
|
||||||
|
|
||||||
uids = [currentCollection fetchUIDsMatchingQualifier: qualifier
|
|
||||||
sortOrdering: @"REVERSE ARRIVAL"
|
|
||||||
threaded: NO];
|
|
||||||
count = [uids count];
|
|
||||||
|
|
||||||
// Add the number of UIDs expected to "soft delete"
|
// Add the number of UIDs expected to "soft delete"
|
||||||
count += [self _softDeleteCountWithFilter: filter collectionId: nameInCache];
|
count += [self _softDeleteCountWithFilter: filter collectionId: nameInCache];
|
||||||
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
count = [[currentCollection toOneRelationshipKeys] count];
|
count = [[currentCollection toOneRelationshipKeys] count];
|
||||||
}
|
}
|
||||||
|
|
||||||
[s appendString: @"<?xml version=\"1.0\" encoding=\"utf-8\"?>"];
|
|
||||||
[s appendString: @"<!DOCTYPE ActiveSync PUBLIC \"-//MICROSOFT//DTD ActiveSync//EN\" \"http://www.microsoft.com/\">"];
|
[s appendString: @"<Response>"];
|
||||||
[s appendFormat: @"<GetItemEstimate xmlns=\"GetItemEstimate:\"><Response><Status>%d</Status><Collection>", status];
|
[s appendFormat: @"<Status>%d</Status><Collection>", status];
|
||||||
|
|
||||||
|
if (folderType == ActiveSyncMailFolder)
|
||||||
|
[s appendString: @"<Class>Email</Class>"];
|
||||||
|
else if (folderType == ActiveSyncContactFolder)
|
||||||
|
[s appendString: @"<Class>Contacts</Class>"];
|
||||||
|
else if (folderType == ActiveSyncEventFolder)
|
||||||
|
[s appendString: @"<Class>Calendar</Class>"];
|
||||||
|
else if (folderType == ActiveSyncTaskFolder)
|
||||||
|
[s appendString: @"<Class>Tasks</Class>"];
|
||||||
|
|
||||||
[s appendFormat: @"<CollectionId>%@</CollectionId>",collectionId];
|
[s appendFormat: @"<CollectionId>%@</CollectionId>",collectionId];
|
||||||
[s appendFormat: @"<Estimate>%d</Estimate>", count];
|
[s appendFormat: @"<Estimate>%d</Estimate></Collection></Response>", count];
|
||||||
|
}
|
||||||
|
|
||||||
[s appendString: @"</Collection></Response></GetItemEstimate>"];
|
[s appendString: @"</GetItemEstimate>"];
|
||||||
|
|
||||||
d = [[s dataUsingEncoding: NSUTF8StringEncoding] xml2wbxml];
|
d = [[s dataUsingEncoding: NSUTF8StringEncoding] xml2wbxml];
|
||||||
|
|
||||||
|
@ -1929,6 +1937,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
[s appendString: @"<?xml version=\"1.0\" encoding=\"utf-8\"?>"];
|
[s appendString: @"<?xml version=\"1.0\" encoding=\"utf-8\"?>"];
|
||||||
[s appendString: @"<!DOCTYPE ActiveSync PUBLIC \"-//MICROSOFT//DTD ActiveSync//EN\" \"http://www.microsoft.com/\">"];
|
[s appendString: @"<!DOCTYPE ActiveSync PUBLIC \"-//MICROSOFT//DTD ActiveSync//EN\" \"http://www.microsoft.com/\">"];
|
||||||
[s appendString: @"<Provision xmlns=\"Provision:\">"];
|
[s appendString: @"<Provision xmlns=\"Provision:\">"];
|
||||||
|
[s appendString: @"<AllowHTMLEmail>1</AllowHTMLEmail>"];
|
||||||
[s appendString: @"</Provision>"];
|
[s appendString: @"</Provision>"];
|
||||||
|
|
||||||
d = [[s dataUsingEncoding: NSUTF8StringEncoding] xml2wbxml];
|
d = [[s dataUsingEncoding: NSUTF8StringEncoding] xml2wbxml];
|
||||||
|
@ -2345,7 +2354,21 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
id value;
|
id value;
|
||||||
|
|
||||||
folderId = [[(id)[theDocumentElement getElementsByTagName: @"FolderId"] lastObject] textValue];
|
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];
|
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 = [folderId realCollectionIdWithFolderType: &folderType];
|
||||||
realCollectionId = [self globallyUniqueIDToIMAPFolderName: realCollectionId type: 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
|
// 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.
|
// 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)
|
[[theRequest headerForKey: @"content-type"] caseInsensitiveCompare: @"message/rfc822"] == NSOrderedSame)
|
||||||
{
|
{
|
||||||
NSString *s, *xml;
|
NSString *s, *xml;
|
||||||
|
@ -2561,7 +2586,9 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
s = [theRequest contentAsString];
|
s = [theRequest contentAsString];
|
||||||
}
|
}
|
||||||
|
|
||||||
xml = [NSString stringWithFormat: @"<?xml version=\"1.0\"?><!DOCTYPE ActiveSync PUBLIC \"-//MICROSOFT//DTD ActiveSync//EN\" \"http://www.microsoft.com/\"><SendMail xmlns=\"ComposeMail:\"><SaveInSentItems/><MIME>%@</MIME></SendMail>", [s stringByEncodingBase64]];
|
xml = [NSString stringWithFormat: @"<?xml version=\"1.0\"?><!DOCTYPE ActiveSync PUBLIC \"-//MICROSOFT//DTD ActiveSync//EN\" \"http://www.microsoft.com/\"><%@ xmlns=\"ComposeMail:\"><SaveInSentItems/><MIME>%@</MIME></%@>", cmdName, [s stringByEncodingBase64], cmdName];
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
d = [xml dataUsingEncoding: NSASCIIStringEncoding];
|
d = [xml dataUsingEncoding: NSASCIIStringEncoding];
|
||||||
}
|
}
|
||||||
|
|
|
@ -47,6 +47,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
#import <NGExtensions/NSString+Encoding.h>
|
#import <NGExtensions/NSString+Encoding.h>
|
||||||
#import <NGImap4/NGImap4Envelope.h>
|
#import <NGImap4/NGImap4Envelope.h>
|
||||||
#import <NGImap4/NGImap4EnvelopeAddress.h>
|
#import <NGImap4/NGImap4EnvelopeAddress.h>
|
||||||
|
#import <NGImap4/NSString+Imap4.h>
|
||||||
#import <NGObjWeb/WOContext+SoObjects.h>
|
#import <NGObjWeb/WOContext+SoObjects.h>
|
||||||
|
|
||||||
#import <NGMime/NGMimeBodyPart.h>
|
#import <NGMime/NGMimeBodyPart.h>
|
||||||
|
@ -57,6 +58,10 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
#import <NGMail/NGMimeMessage.h>
|
#import <NGMail/NGMimeMessage.h>
|
||||||
#import <NGMail/NGMimeMessageGenerator.h>
|
#import <NGMail/NGMimeMessageGenerator.h>
|
||||||
|
|
||||||
|
#import <Mailer/SOGoMailLabel.h>
|
||||||
|
|
||||||
|
#import <SOGo/SOGoUserDefaults.h>
|
||||||
|
|
||||||
#include "iCalTimeZone+ActiveSync.h"
|
#include "iCalTimeZone+ActiveSync.h"
|
||||||
#include "NSData+ActiveSync.h"
|
#include "NSData+ActiveSync.h"
|
||||||
#include "NSDate+ActiveSync.h"
|
#include "NSDate+ActiveSync.h"
|
||||||
|
@ -204,6 +209,7 @@ struct GlobalObjectId {
|
||||||
//
|
//
|
||||||
//
|
//
|
||||||
- (NSData *) _preferredBodyDataInMultipartUsingType: (int) theType
|
- (NSData *) _preferredBodyDataInMultipartUsingType: (int) theType
|
||||||
|
nativeTypeFound: (int *) theNativeTypeFound
|
||||||
{
|
{
|
||||||
NSString *encoding, *key, *plainKey, *htmlKey, *type, *subtype;
|
NSString *encoding, *key, *plainKey, *htmlKey, *type, *subtype;
|
||||||
NSDictionary *textParts, *part;
|
NSDictionary *textParts, *part;
|
||||||
|
@ -222,6 +228,10 @@ struct GlobalObjectId {
|
||||||
type = [part valueForKey: @"type"];
|
type = [part valueForKey: @"type"];
|
||||||
subtype = [part valueForKey: @"subtype"];
|
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"])
|
if ([type isEqualToString: @"text"] && [subtype isEqualToString: @"html"])
|
||||||
htmlKey = key;
|
htmlKey = key;
|
||||||
else if ([type isEqualToString: @"text"] && [subtype isEqualToString: @"plain"])
|
else if ([type isEqualToString: @"text"] && [subtype isEqualToString: @"plain"])
|
||||||
|
@ -229,11 +239,22 @@ struct GlobalObjectId {
|
||||||
}
|
}
|
||||||
|
|
||||||
key = nil;
|
key = nil;
|
||||||
|
*theNativeTypeFound = 1;
|
||||||
|
|
||||||
if (theType == 2)
|
if (theType == 2 && htmlKey)
|
||||||
|
{
|
||||||
key = htmlKey;
|
key = htmlKey;
|
||||||
else if (theType == 1)
|
*theNativeTypeFound = 2;
|
||||||
|
}
|
||||||
|
else if (theType == 1 && plainKey)
|
||||||
key = plainKey;
|
key = plainKey;
|
||||||
|
else if (theType == 2 && plainKey)
|
||||||
|
key = plainKey;
|
||||||
|
else if (theType == 1 && htmlKey)
|
||||||
|
{
|
||||||
|
key = htmlKey;
|
||||||
|
*theNativeTypeFound = 2;
|
||||||
|
}
|
||||||
|
|
||||||
if (key)
|
if (key)
|
||||||
{
|
{
|
||||||
|
@ -251,9 +272,17 @@ struct GlobalObjectId {
|
||||||
charset = [[[self lookupInfoForBodyPart: key] objectForKey: @"parameterList"] objectForKey: @"charset"];
|
charset = [[[self lookupInfoForBodyPart: key] objectForKey: @"parameterList"] objectForKey: @"charset"];
|
||||||
|
|
||||||
if (![charset length])
|
if (![charset length])
|
||||||
charset = @"us-ascii";
|
charset = @"utf-8";
|
||||||
|
|
||||||
s = [NSString stringWithData: d usingEncodingNamed: charset];
|
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];
|
d = [s dataUsingEncoding: NSUTF8StringEncoding];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -298,7 +327,7 @@ struct GlobalObjectId {
|
||||||
[[[thePart contentType] type] isEqualToString: @"text"] &&
|
[[[thePart contentType] type] isEqualToString: @"text"] &&
|
||||||
([[[thePart contentType] subType] isEqualToString: @"plain"] || [[[thePart contentType] subType] isEqualToString: @"html"]))
|
([[[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;
|
NGMimeType *mimeType;
|
||||||
NSString *s;
|
NSString *s;
|
||||||
|
|
||||||
|
@ -309,9 +338,13 @@ struct GlobalObjectId {
|
||||||
charset = [[thePart contentType] valueOfParameter: @"charset"];
|
charset = [[thePart contentType] valueOfParameter: @"charset"];
|
||||||
|
|
||||||
if (![charset length])
|
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
|
else
|
||||||
{
|
{
|
||||||
|
@ -414,7 +447,7 @@ struct GlobalObjectId {
|
||||||
charset = [[[self lookupInfoForBodyPart: @""] objectForKey: @"parameterList"] objectForKey: @"charset"];
|
charset = [[[self lookupInfoForBodyPart: @""] objectForKey: @"parameterList"] objectForKey: @"charset"];
|
||||||
|
|
||||||
if (![charset length])
|
if (![charset length])
|
||||||
charset = @"us-ascii";
|
charset = @"utf-8";
|
||||||
|
|
||||||
d = [[self fetchPlainTextParts] objectForKey: @""];
|
d = [[self fetchPlainTextParts] objectForKey: @""];
|
||||||
|
|
||||||
|
@ -429,6 +462,10 @@ struct GlobalObjectId {
|
||||||
|
|
||||||
s = [NSString stringWithData: d usingEncodingNamed: charset];
|
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
|
// Check if we must convert html->plain
|
||||||
if (theType == 1 && [subtype isEqualToString: @"html"])
|
if (theType == 1 && [subtype isEqualToString: @"html"])
|
||||||
{
|
{
|
||||||
|
@ -439,14 +476,15 @@ struct GlobalObjectId {
|
||||||
}
|
}
|
||||||
else if ([type isEqualToString: @"multipart"])
|
else if ([type isEqualToString: @"multipart"])
|
||||||
{
|
{
|
||||||
d = [self _preferredBodyDataInMultipartUsingType: theType];
|
d = [self _preferredBodyDataInMultipartUsingType: theType nativeTypeFound: theNativeType];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if (theType == 4)
|
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.
|
// 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];
|
d = [self _sanitizedMIMEMessage];
|
||||||
else
|
else
|
||||||
d = [self content];
|
d = [self content];
|
||||||
|
@ -512,6 +550,10 @@ struct GlobalObjectId {
|
||||||
NSData *d, *globalObjId;
|
NSData *d, *globalObjId;
|
||||||
NSArray *attachmentKeys;
|
NSArray *attachmentKeys;
|
||||||
NSMutableString *s;
|
NSMutableString *s;
|
||||||
|
|
||||||
|
uint32_t v;
|
||||||
|
NSString *p;
|
||||||
|
|
||||||
id value;
|
id value;
|
||||||
|
|
||||||
iCalCalendar *calendar;
|
iCalCalendar *calendar;
|
||||||
|
@ -552,8 +594,25 @@ struct GlobalObjectId {
|
||||||
if (value)
|
if (value)
|
||||||
[s appendFormat: @"<Cc xmlns=\"Email:\">%@</Cc>", [value activeSyncRepresentationInContext: context]];
|
[s appendFormat: @"<Cc xmlns=\"Email:\">%@</Cc>", [value activeSyncRepresentationInContext: context]];
|
||||||
|
|
||||||
// Importance - FIXME
|
// Importance
|
||||||
[s appendFormat: @"<Importance xmlns=\"Email:\">%@</Importance>", @"1"];
|
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: @"<Importance xmlns=\"Email:\">%d</Importance>", v];
|
||||||
|
|
||||||
// Read
|
// Read
|
||||||
[s appendFormat: @"<Read xmlns=\"Email:\">%d</Read>", ([self read] ? 1 : 0)];
|
[s appendFormat: @"<Read xmlns=\"Email:\">%d</Read>", ([self read] ? 1 : 0)];
|
||||||
|
@ -728,8 +787,21 @@ struct GlobalObjectId {
|
||||||
|
|
||||||
len = [content length];
|
len = [content length];
|
||||||
|
|
||||||
|
if ([[[context request] headerForKey: @"MS-ASProtocolVersion"] isEqualToString: @"2.5"])
|
||||||
|
{
|
||||||
|
[s appendFormat: @"<Body xmlns=\"Email:\">%@</Body>", content];
|
||||||
|
[s appendFormat: @"<BodyTruncated xmlns=\"Email:\">%d</BodyTruncated>", truncated];
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
[s appendString: @"<Body xmlns=\"AirSyncBase:\">"];
|
[s appendString: @"<Body xmlns=\"AirSyncBase:\">"];
|
||||||
|
|
||||||
|
// Set the correct type if client requested text/html but we got text/plain
|
||||||
|
if (preferredBodyType == 2 && nativeBodyType == 1)
|
||||||
|
[s appendString: @"<Type>1</Type>"];
|
||||||
|
else
|
||||||
[s appendFormat: @"<Type>%d</Type>", preferredBodyType];
|
[s appendFormat: @"<Type>%d</Type>", preferredBodyType];
|
||||||
|
|
||||||
[s appendFormat: @"<Truncated>%d</Truncated>", truncated];
|
[s appendFormat: @"<Truncated>%d</Truncated>", truncated];
|
||||||
[s appendFormat: @"<Preview></Preview>"];
|
[s appendFormat: @"<Preview></Preview>"];
|
||||||
|
|
||||||
|
@ -740,6 +812,7 @@ struct GlobalObjectId {
|
||||||
}
|
}
|
||||||
[s appendString: @"</Body>"];
|
[s appendString: @"</Body>"];
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Attachments -namespace 16
|
// Attachments -namespace 16
|
||||||
attachmentKeys = [self fetchFileAttachmentKeys];
|
attachmentKeys = [self fetchFileAttachmentKeys];
|
||||||
|
@ -747,6 +820,9 @@ struct GlobalObjectId {
|
||||||
{
|
{
|
||||||
int i;
|
int i;
|
||||||
|
|
||||||
|
if ([[[context request] headerForKey: @"MS-ASProtocolVersion"] isEqualToString: @"2.5"])
|
||||||
|
[s appendString: @"<Attachments xmlns=\"Email:\">"];
|
||||||
|
else
|
||||||
[s appendString: @"<Attachments xmlns=\"AirSyncBase:\">"];
|
[s appendString: @"<Attachments xmlns=\"AirSyncBase:\">"];
|
||||||
|
|
||||||
for (i = 0; i < [attachmentKeys count]; i++)
|
for (i = 0; i < [attachmentKeys count]; i++)
|
||||||
|
@ -759,11 +835,22 @@ struct GlobalObjectId {
|
||||||
// FileReference must be a unique identifier across the whole store. We use the following structure:
|
// FileReference must be a unique identifier across the whole store. We use the following structure:
|
||||||
// mail/<foldername>/<message UID/<pathofpart>
|
// mail/<foldername>/<message UID/<pathofpart>
|
||||||
// mail/INBOX/2
|
// mail/INBOX/2
|
||||||
|
if ([[[context request] headerForKey: @"MS-ASProtocolVersion"] isEqualToString: @"2.5"])
|
||||||
|
[s appendFormat: @"<AttName>mail/%@/%@/%@</AttName>", [[[self container] relativeImap4Name] stringByEscapingURL], [self nameInContainer], [value objectForKey: @"path"]];
|
||||||
|
else
|
||||||
[s appendFormat: @"<FileReference>mail/%@/%@/%@</FileReference>", [[[self container] relativeImap4Name] stringByEscapingURL], [self nameInContainer], [value objectForKey: @"path"]];
|
[s appendFormat: @"<FileReference>mail/%@/%@/%@</FileReference>", [[[self container] relativeImap4Name] stringByEscapingURL], [self nameInContainer], [value objectForKey: @"path"]];
|
||||||
|
|
||||||
|
if ([[[context request] headerForKey: @"MS-ASProtocolVersion"] isEqualToString: @"2.5"])
|
||||||
|
{
|
||||||
|
[s appendFormat: @"<AttMethod>%d</AttMethod>", 1];
|
||||||
|
[s appendFormat: @"<AttSize>%d</AttSize>", [[value objectForKey: @"size"] intValue]];
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
[s appendFormat: @"<Method>%d</Method>", 1]; // See: http://msdn.microsoft.com/en-us/library/ee160322(v=exchg.80).aspx
|
[s appendFormat: @"<Method>%d</Method>", 1]; // See: http://msdn.microsoft.com/en-us/library/ee160322(v=exchg.80).aspx
|
||||||
[s appendFormat: @"<EstimatedDataSize>%d</EstimatedDataSize>", [[value objectForKey: @"size"] intValue]];
|
[s appendFormat: @"<EstimatedDataSize>%d</EstimatedDataSize>", [[value objectForKey: @"size"] intValue]];
|
||||||
//[s appendFormat: @"<IsInline>%d</IsInline>", 1];
|
//[s appendFormat: @"<IsInline>%d</IsInline>", 1];
|
||||||
|
}
|
||||||
[s appendString: @"</Attachment>"];
|
[s appendString: @"</Attachment>"];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -775,6 +862,27 @@ struct GlobalObjectId {
|
||||||
[s appendFormat: @"<FlagStatus>%d</FlagStatus>", ([self flagged] ? 2 : 0)];
|
[s appendFormat: @"<FlagStatus>%d</FlagStatus>", ([self flagged] ? 2 : 0)];
|
||||||
[s appendString: @"</Flag>"];
|
[s appendString: @"</Flag>"];
|
||||||
|
|
||||||
|
|
||||||
|
// Categroies/Labels
|
||||||
|
NSEnumerator *categories;
|
||||||
|
categories = [[[self fetchCoreInfos] objectForKey: @"flags"] objectEnumerator];
|
||||||
|
|
||||||
|
if (categories)
|
||||||
|
{
|
||||||
|
NSString *currentFlag;
|
||||||
|
NSDictionary *v;
|
||||||
|
|
||||||
|
v = [[[context activeUser] userDefaults] mailLabelsColors];
|
||||||
|
|
||||||
|
[s appendFormat: @"<Categories xmlns=\"Email:\">"];
|
||||||
|
while ((currentFlag = [categories nextObject]))
|
||||||
|
{
|
||||||
|
if ([[v objectForKey: currentFlag] objectAtIndex:0])
|
||||||
|
[s appendFormat: @"<Category>%@</Category>", [[[v objectForKey: currentFlag] objectAtIndex:0] activeSyncRepresentationInContext: context]];
|
||||||
|
}
|
||||||
|
[s appendFormat: @"</Categories>"];
|
||||||
|
}
|
||||||
|
|
||||||
// FIXME - support these in the future
|
// FIXME - support these in the future
|
||||||
//[s appendString: @"<ConversationId xmlns=\"Email2:\">foobar</ConversationId>"];
|
//[s appendString: @"<ConversationId xmlns=\"Email2:\">foobar</ConversationId>"];
|
||||||
//[s appendString: @"<ConversationIndex xmlns=\"Email2:\">zot=</ConversationIndex>"];
|
//[s appendString: @"<ConversationIndex xmlns=\"Email2:\">zot=</ConversationIndex>"];
|
||||||
|
@ -836,6 +944,65 @@ struct GlobalObjectId {
|
||||||
else
|
else
|
||||||
[self removeFlags: @"seen"];;
|
[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
|
@end
|
||||||
|
|
|
@ -40,6 +40,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
#import <NGExtensions/NSString+misc.h>
|
#import <NGExtensions/NSString+misc.h>
|
||||||
#import <NGObjWeb/WOContext.h>
|
#import <NGObjWeb/WOContext.h>
|
||||||
#import <NGObjWeb/WOContext+SoObjects.h>
|
#import <NGObjWeb/WOContext+SoObjects.h>
|
||||||
|
#import <NGObjWeb/WORequest.h>
|
||||||
|
|
||||||
#import <NGCards/iCalCalendar.h>
|
#import <NGCards/iCalCalendar.h>
|
||||||
#import <NGCards/iCalDateTime.h>
|
#import <NGCards/iCalDateTime.h>
|
||||||
|
@ -274,12 +275,21 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
// It is very important here to NOT set <Truncated>0</Truncated> in the response,
|
// It is very important here to NOT set <Truncated>0</Truncated> in the response,
|
||||||
// otherwise it'll prevent WP8 phones from sync'ing. See #3028 for details.
|
// otherwise it'll prevent WP8 phones from sync'ing. See #3028 for details.
|
||||||
o = [o activeSyncRepresentationInContext: context];
|
o = [o activeSyncRepresentationInContext: context];
|
||||||
|
|
||||||
|
if ([[[context request] headerForKey: @"MS-ASProtocolVersion"] isEqualToString: @"2.5"])
|
||||||
|
{
|
||||||
|
[s appendFormat: @"<Body xmlns=\"Calendar:\">%@</Body>", o];
|
||||||
|
[s appendString: @"<BodyTruncated xmlns=\"Calendar:\">0</BodyTruncated>"];
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
[s appendString: @"<Body xmlns=\"AirSyncBase:\">"];
|
[s appendString: @"<Body xmlns=\"AirSyncBase:\">"];
|
||||||
[s appendFormat: @"<Type>%d</Type>", 1];
|
[s appendFormat: @"<Type>%d</Type>", 1];
|
||||||
[s appendFormat: @"<EstimatedDataSize>%d</EstimatedDataSize>", [o length]];
|
[s appendFormat: @"<EstimatedDataSize>%d</EstimatedDataSize>", [o length]];
|
||||||
[s appendFormat: @"<Data>%@</Data>", o];
|
[s appendFormat: @"<Data>%@</Data>", o];
|
||||||
[s appendString: @"</Body>"];
|
[s appendString: @"</Body>"];
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
[s appendFormat: @"<NativeBodyType xmlns=\"AirSyncBase:\">%d</NativeBodyType>", 1];
|
[s appendFormat: @"<NativeBodyType xmlns=\"AirSyncBase:\">%d</NativeBodyType>", 1];
|
||||||
|
|
||||||
|
@ -394,8 +404,16 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
}
|
}
|
||||||
|
|
||||||
// FIXME: merge with iCalToDo
|
// FIXME: merge with iCalToDo
|
||||||
|
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"]))
|
if ((o = [[theValues objectForKey: @"Body"] objectForKey: @"Data"]))
|
||||||
[self setComment: o];
|
[self setComment: o];
|
||||||
|
}
|
||||||
|
|
||||||
if ((o = [theValues objectForKey: @"Location"]))
|
if ((o = [theValues objectForKey: @"Location"]))
|
||||||
[self setLocation: o];
|
[self setLocation: o];
|
||||||
|
|
|
@ -38,6 +38,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
#import <NGExtensions/NSString+misc.h>
|
#import <NGExtensions/NSString+misc.h>
|
||||||
#import <NGObjWeb/WOContext.h>
|
#import <NGObjWeb/WOContext.h>
|
||||||
#import <NGObjWeb/WOContext+SoObjects.h>
|
#import <NGObjWeb/WOContext+SoObjects.h>
|
||||||
|
#import <NGObjWeb/WORequest.h>
|
||||||
|
|
||||||
#import <SOGo/SOGoUser.h>
|
#import <SOGo/SOGoUser.h>
|
||||||
#import <SOGo/SOGoUserDefaults.h>
|
#import <SOGo/SOGoUserDefaults.h>
|
||||||
|
@ -49,6 +50,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
#include "NSDate+ActiveSync.h"
|
#include "NSDate+ActiveSync.h"
|
||||||
#include "NSString+ActiveSync.h"
|
#include "NSString+ActiveSync.h"
|
||||||
|
|
||||||
|
|
||||||
@implementation iCalToDo (ActiveSync)
|
@implementation iCalToDo (ActiveSync)
|
||||||
|
|
||||||
- (NSString *) activeSyncRepresentationInContext: (WOContext *) context
|
- (NSString *) activeSyncRepresentationInContext: (WOContext *) context
|
||||||
|
@ -62,11 +64,10 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
s = [NSMutableString string];
|
s = [NSMutableString string];
|
||||||
|
|
||||||
// Complete
|
// Complete
|
||||||
o = [self completed];
|
[s appendFormat: @"<Complete xmlns=\"Tasks:\">%d</Complete>", [[self status] isEqualToString: @"COMPLETED"] ? 1 : 0];
|
||||||
[s appendFormat: @"<Complete xmlns=\"Tasks:\">%d</Complete>", (o ? 1 : 0)];
|
|
||||||
|
|
||||||
// DateCompleted
|
// DateCompleted
|
||||||
if (o)
|
if ((o = [self completed]))
|
||||||
[s appendFormat: @"<DateCompleted xmlns=\"Tasks:\">%@</DateCompleted>", [o activeSyncRepresentationInContext: context]];
|
[s appendFormat: @"<DateCompleted xmlns=\"Tasks:\">%@</DateCompleted>", [o activeSyncRepresentationInContext: context]];
|
||||||
|
|
||||||
// Start date
|
// Start date
|
||||||
|
@ -97,6 +98,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
// Reminder - FIXME
|
// Reminder - FIXME
|
||||||
[s appendFormat: @"<ReminderSet xmlns=\"Tasks:\">%d</ReminderSet>", 0];
|
[s appendFormat: @"<ReminderSet xmlns=\"Tasks:\">%d</ReminderSet>", 0];
|
||||||
|
|
||||||
|
// Sensitivity
|
||||||
if ([[self accessClass] isEqualToString: @"PRIVATE"])
|
if ([[self accessClass] isEqualToString: @"PRIVATE"])
|
||||||
v = 2;
|
v = 2;
|
||||||
else if ([[self accessClass] isEqualToString: @"CONFIDENTIAL"])
|
else if ([[self accessClass] isEqualToString: @"CONFIDENTIAL"])
|
||||||
|
@ -104,6 +106,8 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
else
|
else
|
||||||
v = 0;
|
v = 0;
|
||||||
|
|
||||||
|
[s appendFormat: @"<Sensitivity xmlns=\"Tasks:\">%d</Sensitivity>", v];
|
||||||
|
|
||||||
categories = [self categories];
|
categories = [self categories];
|
||||||
|
|
||||||
if ([categories count])
|
if ([categories count])
|
||||||
|
@ -126,12 +130,21 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
// It is very important here to NOT set <Truncated>0</Truncated> in the response,
|
// It is very important here to NOT set <Truncated>0</Truncated> in the response,
|
||||||
// otherwise it'll prevent WP8 phones from sync'ing. See #3028 for details.
|
// otherwise it'll prevent WP8 phones from sync'ing. See #3028 for details.
|
||||||
o = [o activeSyncRepresentationInContext: context];
|
o = [o activeSyncRepresentationInContext: context];
|
||||||
|
|
||||||
|
if ([[[context request] headerForKey: @"MS-ASProtocolVersion"] isEqualToString: @"2.5"])
|
||||||
|
{
|
||||||
|
[s appendFormat: @"<Body xmlns=\"Tasks:\">%@</Body>", o];
|
||||||
|
[s appendString: @"<BodyTruncated xmlns=\"Tasks:\">0</BodyTruncated>"];
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
[s appendString: @"<Body xmlns=\"AirSyncBase:\">"];
|
[s appendString: @"<Body xmlns=\"AirSyncBase:\">"];
|
||||||
[s appendFormat: @"<Type>%d</Type>", 1];
|
[s appendFormat: @"<Type>%d</Type>", 1];
|
||||||
[s appendFormat: @"<EstimatedDataSize>%d</EstimatedDataSize>", [o length]];
|
[s appendFormat: @"<EstimatedDataSize>%d</EstimatedDataSize>", [o length]];
|
||||||
[s appendFormat: @"<Data>%@</Data>", o];
|
[s appendFormat: @"<Data>%@</Data>", o];
|
||||||
[s appendString: @"</Body>"];
|
[s appendString: @"</Body>"];
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return s;
|
return s;
|
||||||
}
|
}
|
||||||
|
@ -152,9 +165,16 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
[self setSummary: o];
|
[self setSummary: o];
|
||||||
|
|
||||||
// FIXME: merge with iCalEvent
|
// FIXME: merge with iCalEvent
|
||||||
|
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"]))
|
if ((o = [[theValues objectForKey: @"Body"] objectForKey: @"Data"]))
|
||||||
[self setComment: o];
|
[self setComment: o];
|
||||||
|
}
|
||||||
|
|
||||||
if ([[theValues objectForKey: @"Complete"] intValue] &&
|
if ([[theValues objectForKey: @"Complete"] intValue] &&
|
||||||
((o = [theValues objectForKey: @"DateCompleted"])) )
|
((o = [theValues objectForKey: @"DateCompleted"])) )
|
||||||
|
@ -166,6 +186,11 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
[completed setDate: o];
|
[completed setDate: o];
|
||||||
[self setStatus: @"COMPLETED"];
|
[self setStatus: @"COMPLETED"];
|
||||||
}
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
[self setStatus: @"IN-PROCESS"];
|
||||||
|
[self setCompleted: nil];
|
||||||
|
}
|
||||||
|
|
||||||
if ((o = [theValues objectForKey: @"DueDate"]))
|
if ((o = [theValues objectForKey: @"DueDate"]))
|
||||||
{
|
{
|
||||||
|
@ -177,6 +202,16 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
[due setDateTime: o];
|
[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
|
// 2 == high, 1 == normal, 0 == low
|
||||||
if ((o = [theValues objectForKey: @"Importance"]))
|
if ((o = [theValues objectForKey: @"Importance"]))
|
||||||
{
|
{
|
||||||
|
|
589
ChangeLog
589
ChangeLog
|
@ -1,3 +1,592 @@
|
||||||
|
commit f11bcd3373f39967b870f8d8f3358759a6221b62
|
||||||
|
Author: Ludovic Marcotte <lmarcotte@inverse.ca>
|
||||||
|
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 <lmarcotte@inverse.ca>
|
||||||
|
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 <lmarcotte@inverse.ca>
|
||||||
|
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 <lmarcotte@inverse.ca>
|
||||||
|
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 <flachapelle@inverse.ca>
|
||||||
|
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 <lmarcotte@inverse.ca>
|
||||||
|
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 <lmarcotte@inverse.ca>
|
||||||
|
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 <lmarcotte@inverse.ca>
|
||||||
|
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 <lmarcotte@inverse.ca>
|
||||||
|
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 <lmarcotte@inverse.ca>
|
||||||
|
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 <lmarcotte@inverse.ca>
|
||||||
|
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 <lmarcotte@inverse.ca>
|
||||||
|
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 <lmarcotte@inverse.ca>
|
||||||
|
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 <lmarcotte@inverse.ca>
|
||||||
|
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 <lmarcotte@inverse.ca>
|
||||||
|
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 <flachapelle@inverse.ca>
|
||||||
|
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 <lmarcotte@inverse.ca>
|
||||||
|
Date: Wed Mar 18 10:15:33 2015 -0400
|
||||||
|
|
||||||
|
Documented NGMimeBuildMimeTempDirectory
|
||||||
|
|
||||||
|
M Documentation/SOGoInstallationGuide.asciidoc
|
||||||
|
|
||||||
|
commit e25a7e3d8d0a5f11a65a6fa337a637b3e1f44432
|
||||||
|
Author: Ludovic Marcotte <lmarcotte@inverse.ca>
|
||||||
|
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 <flachapelle@inverse.ca>
|
||||||
|
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 <email@jenserat.de>
|
||||||
|
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 <email@jenserat.de>
|
||||||
|
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 <flachapelle@inverse.ca>
|
||||||
|
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 <flachapelle@inverse.ca>
|
||||||
|
Date: Mon Mar 9 14:04:21 2015 -0400
|
||||||
|
|
||||||
|
Fix possible JS exception in Contacts module
|
||||||
|
|
||||||
|
M NEWS
|
||||||
|
|
||||||
|
commit cbba0719bb58229d9be8d3bb5af5fba23da3ae14
|
||||||
|
Author: Francis Lachapelle <flachapelle@inverse.ca>
|
||||||
|
Date: Wed Mar 4 10:05:12 2015 -0500
|
||||||
|
|
||||||
|
Fix exception in SOGoUserManager
|
||||||
|
|
||||||
|
M SoObjects/SOGo/SOGoUserManager.m
|
||||||
|
|
||||||
|
commit 8e0849029ea93bfc9e1f9e2cb4ff2e0e5b6a4308
|
||||||
|
Author: Francis Lachapelle <flachapelle@inverse.ca>
|
||||||
|
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 <flachapelle@inverse.ca>
|
||||||
|
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 <lmarcotte@inverse.ca>
|
||||||
|
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 <lmarcotte@inverse.ca>
|
||||||
|
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 <lmarcotte@inverse.ca>
|
||||||
|
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 <lmarcotte@inverse.ca>
|
||||||
|
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 <lmarcotte@inverse.ca>
|
||||||
|
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 <lmarcotte@inverse.ca>
|
||||||
|
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 <lmarcotte@inverse.ca>
|
||||||
|
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 <lmarcotte@inverse.ca>
|
||||||
|
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 <lmarcotte@inverse.ca>
|
||||||
|
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 <lmarcotte@inverse.ca>
|
||||||
|
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 <jgarcia@zentyal.com>
|
||||||
|
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 <lmarcotte@inverse.ca>
|
||||||
|
Date: Thu Feb 12 11:06:57 2015 -0500
|
||||||
|
|
||||||
|
Update ChangeLog
|
||||||
|
|
||||||
|
M ChangeLog
|
||||||
|
|
||||||
|
commit 72b210cd2ebccc8e9cdc13c23abb76b7f455b780
|
||||||
|
Author: Ludovic Marcotte <lmarcotte@inverse.ca>
|
||||||
|
Date: Thu Feb 12 10:29:46 2015 -0500
|
||||||
|
|
||||||
|
Fixed typo
|
||||||
|
|
||||||
|
M NEWS
|
||||||
|
|
||||||
|
commit a80d75d09cd0b6f310455e33847ae62433e258ca
|
||||||
|
Author: Ludovic Marcotte <lmarcotte@inverse.ca>
|
||||||
|
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 <lmarcotte@inverse.ca>
|
||||||
|
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 <flachapelle@inverse.ca>
|
||||||
|
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 <flachapelle@inverse.ca>
|
||||||
|
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 <lmarcotte@inverse.ca>
|
||||||
|
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 <lmarcotte@inverse.ca>
|
||||||
|
Date: Wed Feb 11 18:13:36 2015 -0500
|
||||||
|
|
||||||
|
Updated NEWS file wrt fixes
|
||||||
|
|
||||||
|
M NEWS
|
||||||
|
|
||||||
|
commit b4ea4d7e9fd90520675452bdcbf70155d6b87eef
|
||||||
|
Author: Ludovic Marcotte <lmarcotte@inverse.ca>
|
||||||
|
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 <lmarcotte@inverse.ca>
|
||||||
|
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 <lmarcotte@inverse.ca>
|
||||||
|
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 <lmarcotte@inverse.ca>
|
||||||
|
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 <lmarcotte@inverse.ca>
|
||||||
|
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 <lmarcotte@inverse.ca>
|
||||||
|
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 <lmarcotte@inverse.ca>
|
||||||
|
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 <lmarcotte@inverse.ca>
|
||||||
|
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 <lmarcotte@inverse.ca>
|
||||||
|
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 <lmarcotte@inverse.ca>
|
||||||
|
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 <lmarcotte@inverse.ca>
|
||||||
|
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 <lmarcotte@inverse.ca>
|
||||||
|
Date: Thu Feb 5 09:29:29 2015 -0500
|
||||||
|
|
||||||
|
Applied patch for #3092
|
||||||
|
|
||||||
|
M SOPE/GDLContentStore/GCSChannelManager.m
|
||||||
|
|
||||||
|
commit 0b44fa81f4c1ec9c5b812d799bf76399ce240b5b
|
||||||
|
Author: Francis Lachapelle <flachapelle@inverse.ca>
|
||||||
|
Date: Tue Feb 3 13:20:57 2015 -0500
|
||||||
|
|
||||||
|
Update NEWS file
|
||||||
|
|
||||||
|
M NEWS
|
||||||
|
|
||||||
|
commit 776b17ff202c45ff63bfc5144dd0ccc37a2f3e58
|
||||||
|
Author: Francis Lachapelle <flachapelle@inverse.ca>
|
||||||
|
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 <lmarcotte@inverse.ca>
|
||||||
|
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
|
commit d1d398091961f5d497b67313e098a8a5624089f4
|
||||||
Author: Francis Lachapelle <flachapelle@inverse.ca>
|
Author: Francis Lachapelle <flachapelle@inverse.ca>
|
||||||
Date: Fri Jan 30 11:03:38 2015 -0500
|
Date: Fri Jan 30 11:03:38 2015 -0500
|
||||||
|
|
|
@ -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
|
require LDAP/SSL for password policies to work. This is the case for
|
||||||
example with 389 Directory Server.
|
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
|
|isAddressBook
|
||||||
|If set to `YES`, this LDAP source is used as a shared address book
|
|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
|
(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
|
user addressbooks (see _abOU_ below), this list of object classes will
|
||||||
be applied to new records as they are created.
|
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
|
|modifiers
|
||||||
|A list (array) of usernames that are authorized to perform
|
|A list (array) of usernames that are authorized to perform
|
||||||
modifications to the address book defined by this LDAP source.
|
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"]
|
[cols="3,47,50a"]
|
||||||
|=======================================================================
|
|=======================================================================
|
||||||
|D |SOGoProfileURL
|
|S |SOGoProfileURL
|
||||||
|Parameter used to set the database URL so that SOGo can retrieve user
|
|Parameter used to set the database URL so that SOGo can retrieve user
|
||||||
profiles.
|
profiles.
|
||||||
|
|
||||||
For MySQL, set the database URL to something like:
|
For MySQL, set the database URL to something like:
|
||||||
`mysql://sogo:sogo@localhost:3306/sogo/sogo_user_profile`.
|
`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
|
|Parameter used to set the database URL so that SOGo can retrieve the
|
||||||
location of user folders (address books and calendars).
|
location of user folders (address books and calendars).
|
||||||
|
|
||||||
For Oracle, set the database URL to something like:
|
For Oracle, set the database URL to something like:
|
||||||
`oracle://sogo:sogo@localhost:1526/sogo/sogo_folder_info`.
|
`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
|
|Parameter used to set the database URL so that SOGo can store and
|
||||||
retrieve secured user sessions information. For PostgreSQL, the database
|
retrieve secured user sessions information. For PostgreSQL, the database
|
||||||
URL could be set to something like:
|
URL could be set to something like:
|
||||||
`postgresql://sogo:sogo@localhost:5432/sogo/sogo_sessions_folder`.
|
`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
|
|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
|
be set on events and tasks). This parameter is relevant only if
|
||||||
_SOGoEnableEMailAlarms_ is set to `YES`. For PostgreSQL, the database
|
_SOGoEnableEMailAlarms_ is set to `YES`. For PostgreSQL, the database
|
||||||
|
@ -1681,6 +1694,13 @@ cronjob `sogo-tmpwatch`.
|
||||||
|
|
||||||
Defaults to `/var/spool/sogo`.
|
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
|
|S |NGImap4DisableIMAP4Pooling
|
||||||
|Disables IMAP pooling when set to `YES`. Enable pooling by setting to
|
|Disables IMAP pooling when set to `YES`. Enable pooling by setting to
|
||||||
`NO` or using a caching proxy like imapproxy.
|
`NO` or using a caching proxy like imapproxy.
|
||||||
|
@ -1784,6 +1804,12 @@ host.
|
||||||
|
|
||||||
Defaults to `NO` when unset.
|
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
|
|D |SOGoSieveScriptsEnabled
|
||||||
|Parameter used to activate the edition from the preferences windows of
|
|Parameter used to activate the edition from the preferences windows of
|
||||||
server-side mail filters. Requires Sieve script support on the IMAP
|
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,
|
pre-configure typical user accounts or any standardized LDAP record,
|
||||||
along with the correct object classes, fields and default values.
|
along with the correct object classes, fields and default values.
|
||||||
|
|
||||||
Microsoft ActiveSync
|
Microsoft Enterprise ActiveSync
|
||||||
--------------------
|
-------------------------------
|
||||||
|
|
||||||
SOGo supports the Microsoft ActiveSync protocol.
|
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
|
solution is to configure Outlook to use a LDAP server (over SSL) with
|
||||||
authentication. Outlook 2013 also does not seem to support multiple
|
authentication. Outlook 2013 also does not seem to support multiple
|
||||||
address books over ActiveSync.
|
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
|
* Make sure you do not use a self-signed certificate. While this will
|
||||||
work, Outlook will work intermittently as it will raise popups for
|
work, Outlook will work intermittently as it will raise popups for
|
||||||
certificate validation, sometimes in background, preventing the user to
|
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
|
Inverse inc. provides this software for free, but is not responsible for
|
||||||
anything related to its usage.
|
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
|
Using SOGo
|
||||||
----------
|
----------
|
||||||
|
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
<!-- TODO have the build system take care of this -->
|
<!-- TODO have the build system take care of this -->
|
||||||
<releaseinfo>Version 2.2.15 - January 2015</releaseinfo>
|
<releaseinfo>Version 2.2.17a - March 2015</releaseinfo>
|
||||||
<subtitle>for version 2.2.15</subtitle>
|
<subtitle>for version 2.2.17a</subtitle>
|
||||||
<date>2015-01-30</date>
|
<date>2015-03-26</date>
|
||||||
|
|
||||||
<legalnotice>
|
<legalnotice>
|
||||||
<para>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".</para>
|
<para>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".</para>
|
||||||
|
|
|
@ -13,6 +13,6 @@
|
||||||
|
|
||||||
// TODO have the build system take care of this
|
// 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:
|
// vim: set syntax=asciidoc tabstop=2 shiftwidth=2 expandtab:
|
||||||
|
|
59
NEWS
59
NEWS
|
@ -20,6 +20,65 @@ Bug fixes
|
||||||
- Fix exception modifications import in recurrence series
|
- Fix exception modifications import in recurrence series
|
||||||
- Fix server side crash parsing rtf emails with images (with word97 format)
|
- 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)
|
2.2.15-zentyal2 (2015-03-16)
|
||||||
----------------------------
|
----------------------------
|
||||||
|
|
||||||
|
|
|
@ -240,7 +240,7 @@ static NSTimeInterval ChannelCollectionTimer = 5 * 60;
|
||||||
handle = currentHandle;
|
handle = currentHandle;
|
||||||
else if (debugPools)
|
else if (debugPools)
|
||||||
[self logWithFormat: @"DBPOOL: cannot use handle (%@ vs %@) ",
|
[self logWithFormat: @"DBPOOL: cannot use handle (%@ vs %@) ",
|
||||||
[_url absoluteString], [handle->url absoluteString]];
|
[_url absoluteString], [currentHandle->url absoluteString]];
|
||||||
|
|
||||||
return handle;
|
return handle;
|
||||||
}
|
}
|
||||||
|
|
|
@ -62,17 +62,14 @@
|
||||||
SOGoUserManager *um;
|
SOGoUserManager *um;
|
||||||
NSString *domain;
|
NSString *domain;
|
||||||
NSDictionary *contactInfos;
|
NSDictionary *contactInfos;
|
||||||
NSArray *contacts;
|
|
||||||
|
|
||||||
um = [SOGoUserManager sharedUserManager];
|
um = [SOGoUserManager sharedUserManager];
|
||||||
contactInfos = [um contactInfosForUserWithUIDorEmail: uid];
|
contactInfos = [um contactInfosForUserWithUIDorEmail: uid];
|
||||||
if (contactInfos == nil)
|
if (contactInfos == nil)
|
||||||
{
|
{
|
||||||
|
// Search among global addressbooks
|
||||||
domain = [[context activeUser] domain];
|
domain = [[context activeUser] domain];
|
||||||
[um fetchContactsMatching: uid inDomain: domain];
|
contactInfos = [um fetchContactWithUID: uid inDomain: domain];
|
||||||
contacts = [um fetchContactsMatching: uid inDomain: domain];
|
|
||||||
if ([contacts count] == 1)
|
|
||||||
contactInfos = [contacts lastObject];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* iCal.app compatibility:
|
/* iCal.app compatibility:
|
||||||
|
@ -279,7 +276,6 @@
|
||||||
if ([uid length])
|
if ([uid length])
|
||||||
{
|
{
|
||||||
SOGoUserManager *um;
|
SOGoUserManager *um;
|
||||||
NSArray *contacts;
|
|
||||||
NSString *domain, *email;
|
NSString *domain, *email;
|
||||||
NSDictionary *contact;
|
NSDictionary *contact;
|
||||||
MSExchangeFreeBusy *exchangeFreeBusy;
|
MSExchangeFreeBusy *exchangeFreeBusy;
|
||||||
|
@ -287,10 +283,9 @@
|
||||||
|
|
||||||
um = [SOGoUserManager sharedUserManager];
|
um = [SOGoUserManager sharedUserManager];
|
||||||
domain = [[context activeUser] domain];
|
domain = [[context activeUser] domain];
|
||||||
contacts = [um fetchContactsMatching: uid inDomain: domain];
|
contact = [um fetchContactWithUID: uid inDomain: domain];
|
||||||
if ([contacts count] == 1)
|
if (contact)
|
||||||
{
|
{
|
||||||
contact = [contacts lastObject];
|
|
||||||
email = [contact valueForKey: @"c_email"];
|
email = [contact valueForKey: @"c_email"];
|
||||||
source = [contact objectForKey: @"source"];
|
source = [contact objectForKey: @"source"];
|
||||||
if ([email length]
|
if ([email length]
|
||||||
|
@ -386,8 +381,7 @@
|
||||||
([currentStartDate compare: startDate] == NSOrderedAscending)? startDate : currentStartDate, @"startDate",
|
([currentStartDate compare: startDate] == NSOrderedAscending)? startDate : currentStartDate, @"startDate",
|
||||||
currentEndDate, @"endDate", nil]];
|
currentEndDate, @"endDate", nil]];
|
||||||
|
|
||||||
if (!firstRange
|
if (currentEndDate != endDate
|
||||||
&& currentEndDate != endDate
|
|
||||||
&& ([currentEndDate dayOfWeek] == 6 || [currentEndDate dayOfWeek] == 0))
|
&& ([currentEndDate dayOfWeek] == 6 || [currentEndDate dayOfWeek] == 0))
|
||||||
{
|
{
|
||||||
// Fill weekend days
|
// Fill weekend days
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
/* SOGoWebAppointmentFolder.m - this file is part of SOGo
|
/* 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
|
* 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
|
* 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_URL, [location UTF8String]);
|
||||||
curl_easy_setopt (curl, CURLOPT_SSL_VERIFYPEER, 0L);
|
curl_easy_setopt (curl, CURLOPT_SSL_VERIFYPEER, 0L);
|
||||||
curl_easy_setopt (curl, CURLOPT_SSL_VERIFYHOST, 0L);
|
curl_easy_setopt (curl, CURLOPT_SSL_VERIFYHOST, 0L);
|
||||||
|
curl_easy_setopt (curl, CURLOPT_TIMEOUT, 60L);
|
||||||
|
|
||||||
authInfos = [self _loadAuthData];
|
authInfos = [self _loadAuthData];
|
||||||
if (authInfos)
|
if (authInfos)
|
||||||
|
|
|
@ -267,6 +267,13 @@
|
||||||
data = [oldRecord objectForKey: @"homephone"];
|
data = [oldRecord objectForKey: @"homephone"];
|
||||||
if (![data length])
|
if (![data length])
|
||||||
data = @"";
|
data = @"";
|
||||||
|
else if ([data isKindOfClass: [NSArray class]])
|
||||||
|
{
|
||||||
|
if ([data count] > 0)
|
||||||
|
data = [data objectAtIndex: 0];
|
||||||
|
else
|
||||||
|
data = @"";
|
||||||
|
}
|
||||||
[newRecord setObject: data forKey: @"c_telephonenumber"];
|
[newRecord setObject: data forKey: @"c_telephonenumber"];
|
||||||
|
|
||||||
// Custom attribute for group-lookups. See LDAPSource.m where
|
// Custom attribute for group-lookups. See LDAPSource.m where
|
||||||
|
|
|
@ -2129,13 +2129,13 @@ _compareFetchResultsByMODSEQ (id entry1, id entry2, void *data)
|
||||||
value: nextModseq];
|
value: nextModseq];
|
||||||
searchQualifier = [[EOAndQualifier alloc]
|
searchQualifier = [[EOAndQualifier alloc]
|
||||||
initWithQualifiers:
|
initWithQualifiers:
|
||||||
kvQualifier, [self _nonDeletedQualifier], nil];
|
kvQualifier, nil];
|
||||||
[kvQualifier release];
|
[kvQualifier release];
|
||||||
[searchQualifier autorelease];
|
[searchQualifier autorelease];
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
searchQualifier = [self _nonDeletedQualifier];
|
searchQualifier = nil;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (theStartDate)
|
if (theStartDate)
|
||||||
|
@ -2144,7 +2144,7 @@ _compareFetchResultsByMODSEQ (id entry1, id entry2, void *data)
|
||||||
|
|
||||||
sinceDateQualifier = [EOQualifier qualifierWithQualifierFormat:
|
sinceDateQualifier = [EOQualifier qualifierWithQualifierFormat:
|
||||||
@"(DATE >= %@)", theStartDate];
|
@"(DATE >= %@)", theStartDate];
|
||||||
searchQualifier = [[EOAndQualifier alloc] initWithQualifiers: searchQualifier, sinceDateQualifier,
|
searchQualifier = [[EOAndQualifier alloc] initWithQualifiers: sinceDateQualifier, searchQualifier,
|
||||||
nil];
|
nil];
|
||||||
[searchQualifier autorelease];
|
[searchQualifier autorelease];
|
||||||
}
|
}
|
||||||
|
@ -2155,7 +2155,7 @@ _compareFetchResultsByMODSEQ (id entry1, id entry2, void *data)
|
||||||
sortOrdering: nil];
|
sortOrdering: nil];
|
||||||
|
|
||||||
fetchResults = [(NSDictionary *)[self fetchUIDs: uids
|
fetchResults = [(NSDictionary *)[self fetchUIDs: uids
|
||||||
parts: [NSArray arrayWithObject: @"modseq"]]
|
parts: [NSArray arrayWithObjects: @"modseq", @"flags", nil]]
|
||||||
objectForKey: @"fetch"];
|
objectForKey: @"fetch"];
|
||||||
|
|
||||||
/* NOTE: we sort items manually because Cyrus does not properly sort
|
/* 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++)
|
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]];
|
forKey: [[[fetchResults objectAtIndex: i] objectForKey: @"uid"] stringValue]];
|
||||||
[allTokens addObject: d];
|
[allTokens addObject: d];
|
||||||
}
|
}
|
||||||
|
|
|
@ -81,7 +81,6 @@ SOGo_HEADER_FILES = \
|
||||||
\
|
\
|
||||||
SOGoCredentialsFile.h
|
SOGoCredentialsFile.h
|
||||||
|
|
||||||
# daemon tool
|
|
||||||
all::
|
all::
|
||||||
@touch SOGoBuild.m
|
@touch SOGoBuild.m
|
||||||
|
|
||||||
|
@ -160,6 +159,7 @@ SOGo_OBJC_FILES = \
|
||||||
\
|
\
|
||||||
SOGoCredentialsFile.m
|
SOGoCredentialsFile.m
|
||||||
|
|
||||||
|
SOGo_C_FILES = lmhash.c
|
||||||
|
|
||||||
SOGo_RESOURCE_FILES = \
|
SOGo_RESOURCE_FILES = \
|
||||||
SOGoDefaults.plist \
|
SOGoDefaults.plist \
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
/* LDAPSource.h - this file is part of SOGo
|
/* 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
|
* 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
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
@ -72,12 +72,14 @@
|
||||||
|
|
||||||
NSDictionary *contactMapping;
|
NSDictionary *contactMapping;
|
||||||
NSArray *contactObjectClasses;
|
NSArray *contactObjectClasses;
|
||||||
|
NSArray *groupObjectClasses;
|
||||||
|
|
||||||
NSDictionary *modulesConstraints;
|
NSDictionary *modulesConstraints;
|
||||||
|
|
||||||
NSMutableArray *searchAttributes;
|
NSMutableArray *searchAttributes;
|
||||||
|
|
||||||
BOOL passwordPolicy;
|
BOOL passwordPolicy;
|
||||||
|
BOOL updateSambaNTLMPasswords;
|
||||||
|
|
||||||
/* resources handling */
|
/* resources handling */
|
||||||
NSString *kindField;
|
NSString *kindField;
|
||||||
|
@ -105,6 +107,7 @@
|
||||||
UIDField: (NSString *) newUIDField
|
UIDField: (NSString *) newUIDField
|
||||||
mailFields: (NSArray *) newMailFields
|
mailFields: (NSArray *) newMailFields
|
||||||
searchFields: (NSArray *) newSearchFields
|
searchFields: (NSArray *) newSearchFields
|
||||||
|
groupObjectClasses: (NSArray *) newGroupObjectClasses
|
||||||
IMAPHostField: (NSString *) newIMAPHostField
|
IMAPHostField: (NSString *) newIMAPHostField
|
||||||
IMAPLoginField: (NSString *) newIMAPLoginField
|
IMAPLoginField: (NSString *) newIMAPLoginField
|
||||||
SieveHostField: (NSString *) newSieveHostField
|
SieveHostField: (NSString *) newSieveHostField
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
/* LDAPSource.m - this file is part of SOGo
|
/* 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
|
* 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
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
@ -50,7 +50,6 @@ static Class NSStringK;
|
||||||
stringByReplacingString: @"'" withString: @"\\'"] \
|
stringByReplacingString: @"'" withString: @"\\'"] \
|
||||||
stringByReplacingString: @"%" withString: @"%%"]
|
stringByReplacingString: @"%" withString: @"%%"]
|
||||||
|
|
||||||
|
|
||||||
@implementation LDAPSource
|
@implementation LDAPSource
|
||||||
|
|
||||||
+ (void) initialize
|
+ (void) initialize
|
||||||
|
@ -102,6 +101,8 @@ static Class NSStringK;
|
||||||
contactMapping = nil;
|
contactMapping = nil;
|
||||||
searchFields = [NSArray arrayWithObjects: @"sn", @"displayname", @"telephonenumber", nil];
|
searchFields = [NSArray arrayWithObjects: @"sn", @"displayname", @"telephonenumber", nil];
|
||||||
[searchFields retain];
|
[searchFields retain];
|
||||||
|
groupObjectClasses = [NSArray arrayWithObjects: @"group", @"groupofnames", @"groupofuniquenames", @"posixgroup", nil];
|
||||||
|
[groupObjectClasses retain];
|
||||||
IMAPHostField = nil;
|
IMAPHostField = nil;
|
||||||
IMAPLoginField = nil;
|
IMAPLoginField = nil;
|
||||||
SieveHostField = nil;
|
SieveHostField = nil;
|
||||||
|
@ -113,6 +114,7 @@ static Class NSStringK;
|
||||||
|
|
||||||
searchAttributes = nil;
|
searchAttributes = nil;
|
||||||
passwordPolicy = NO;
|
passwordPolicy = NO;
|
||||||
|
updateSambaNTLMPasswords = NO;
|
||||||
|
|
||||||
kindField = nil;
|
kindField = nil;
|
||||||
multipleBookingsField = nil;
|
multipleBookingsField = nil;
|
||||||
|
@ -144,6 +146,7 @@ static Class NSStringK;
|
||||||
[contactMapping release];
|
[contactMapping release];
|
||||||
[mailFields release];
|
[mailFields release];
|
||||||
[searchFields release];
|
[searchFields release];
|
||||||
|
[groupObjectClasses release];
|
||||||
[IMAPHostField release];
|
[IMAPHostField release];
|
||||||
[IMAPLoginField release];
|
[IMAPLoginField release];
|
||||||
[SieveHostField release];
|
[SieveHostField release];
|
||||||
|
@ -189,6 +192,7 @@ static Class NSStringK;
|
||||||
UIDField: [udSource objectForKey: @"UIDFieldName"]
|
UIDField: [udSource objectForKey: @"UIDFieldName"]
|
||||||
mailFields: [udSource objectForKey: @"MailFieldNames"]
|
mailFields: [udSource objectForKey: @"MailFieldNames"]
|
||||||
searchFields: [udSource objectForKey: @"SearchFieldNames"]
|
searchFields: [udSource objectForKey: @"SearchFieldNames"]
|
||||||
|
groupObjectClasses: [udSource objectForKey: @"GroupObjectClasses"]
|
||||||
IMAPHostField: [udSource objectForKey: @"IMAPHostFieldName"]
|
IMAPHostField: [udSource objectForKey: @"IMAPHostFieldName"]
|
||||||
IMAPLoginField: [udSource objectForKey: @"IMAPLoginFieldName"]
|
IMAPLoginField: [udSource objectForKey: @"IMAPLoginFieldName"]
|
||||||
SieveHostField: [udSource objectForKey: @"SieveHostFieldName"]
|
SieveHostField: [udSource objectForKey: @"SieveHostFieldName"]
|
||||||
|
@ -245,6 +249,9 @@ static Class NSStringK;
|
||||||
if ([udSource objectForKey: @"passwordPolicy"])
|
if ([udSource objectForKey: @"passwordPolicy"])
|
||||||
passwordPolicy = [[udSource objectForKey: @"passwordPolicy"] boolValue];
|
passwordPolicy = [[udSource objectForKey: @"passwordPolicy"] boolValue];
|
||||||
|
|
||||||
|
if ([udSource objectForKey: @"updateSambaNTLMPasswords"])
|
||||||
|
updateSambaNTLMPasswords = [[udSource objectForKey: @"updateSambaNTLMPasswords"] boolValue];
|
||||||
|
|
||||||
ASSIGN(MSExchangeHostname, [udSource objectForKey: @"MSExchangeHostname"]);
|
ASSIGN(MSExchangeHostname, [udSource objectForKey: @"MSExchangeHostname"]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -307,6 +314,7 @@ static Class NSStringK;
|
||||||
UIDField: (NSString *) newUIDField
|
UIDField: (NSString *) newUIDField
|
||||||
mailFields: (NSArray *) newMailFields
|
mailFields: (NSArray *) newMailFields
|
||||||
searchFields: (NSArray *) newSearchFields
|
searchFields: (NSArray *) newSearchFields
|
||||||
|
groupObjectClasses: (NSArray *) newGroupObjectClasses
|
||||||
IMAPHostField: (NSString *) newIMAPHostField
|
IMAPHostField: (NSString *) newIMAPHostField
|
||||||
IMAPLoginField: (NSString *) newIMAPLoginField
|
IMAPLoginField: (NSString *) newIMAPLoginField
|
||||||
SieveHostField: (NSString *) newSieveHostField
|
SieveHostField: (NSString *) newSieveHostField
|
||||||
|
@ -331,6 +339,8 @@ static Class NSStringK;
|
||||||
ASSIGN(mailFields, newMailFields);
|
ASSIGN(mailFields, newMailFields);
|
||||||
if (newSearchFields)
|
if (newSearchFields)
|
||||||
ASSIGN(searchFields, newSearchFields);
|
ASSIGN(searchFields, newSearchFields);
|
||||||
|
if (newGroupObjectClasses)
|
||||||
|
ASSIGN(groupObjectClasses, newGroupObjectClasses);
|
||||||
if (newBindFields)
|
if (newBindFields)
|
||||||
{
|
{
|
||||||
// Before SOGo v1.2.0, bindFields was a comma-separated list
|
// Before SOGo v1.2.0, bindFields was a comma-separated list
|
||||||
|
@ -598,6 +608,40 @@ static Class NSStringK;
|
||||||
return [NSString stringWithFormat: @"{%@}%@", _userPasswordAlgorithm, pass];
|
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
|
// We don't use a password policy - we simply use
|
||||||
// a modify-op to change the password
|
// a modify-op to change the password
|
||||||
NGLdapModification *mod;
|
|
||||||
NGLdapAttribute *attr;
|
|
||||||
NSArray *changes;
|
|
||||||
NSString* encryptedPass;
|
NSString* encryptedPass;
|
||||||
|
|
||||||
attr = [[NGLdapAttribute alloc] initWithAttributeName: @"userPassword"];
|
|
||||||
if ([_userPasswordAlgorithm isEqualToString: @"none"])
|
if ([_userPasswordAlgorithm isEqualToString: @"none"])
|
||||||
{
|
{
|
||||||
encryptedPass = newPassword;
|
encryptedPass = newPassword;
|
||||||
|
@ -665,23 +705,32 @@ static Class NSStringK;
|
||||||
{
|
{
|
||||||
encryptedPass = [self _encryptPassword: newPassword];
|
encryptedPass = [self _encryptPassword: newPassword];
|
||||||
}
|
}
|
||||||
|
|
||||||
if (encryptedPass != nil)
|
if (encryptedPass != nil)
|
||||||
{
|
{
|
||||||
[attr addStringValue: encryptedPass];
|
|
||||||
mod = [NGLdapModification replaceModification: attr];
|
|
||||||
changes = [NSArray arrayWithObject: mod];
|
|
||||||
*perr = PolicyNoError;
|
*perr = PolicyNoError;
|
||||||
|
didChange = [self _ldapModifyAttribute: @"userPassword"
|
||||||
|
withValue: encryptedPass
|
||||||
|
userDN: userDN
|
||||||
|
password: oldPassword
|
||||||
|
connection: bindConnection];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if ([bindConnection bindWithMethod: @"simple"
|
// We must check if we must update the Samba NT/LM password hashes
|
||||||
binddn: userDN
|
if (didChange && updateSambaNTLMPasswords)
|
||||||
credentials: oldPassword])
|
|
||||||
{
|
{
|
||||||
didChange = [bindConnection modifyEntryWithDN: userDN
|
[self _ldapModifyAttribute: @"sambaNTPassword"
|
||||||
changes: changes];
|
withValue: [newPassword asNTHash]
|
||||||
}
|
userDN: userDN
|
||||||
else
|
password: newPassword
|
||||||
didChange = NO;
|
connection: bindConnection];
|
||||||
}
|
|
||||||
|
[self _ldapModifyAttribute: @"sambaLMPassword"
|
||||||
|
withValue: [newPassword asLMHash]
|
||||||
|
userDN: userDN
|
||||||
|
password: newPassword
|
||||||
|
connection: bindConnection];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -989,6 +1038,8 @@ static Class NSStringK;
|
||||||
NSString *value;
|
NSString *value;
|
||||||
static NSArray *resourceKinds = nil;
|
static NSArray *resourceKinds = nil;
|
||||||
NSMutableArray *classes;
|
NSMutableArray *classes;
|
||||||
|
NSEnumerator *gclasses;
|
||||||
|
NSString *gclass;
|
||||||
id o;
|
id o;
|
||||||
|
|
||||||
if (!resourceKinds)
|
if (!resourceKinds)
|
||||||
|
@ -1017,24 +1068,27 @@ static Class NSStringK;
|
||||||
|
|
||||||
if (classes)
|
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
|
// We check if our entry is a resource. We also support
|
||||||
// determining resources based on the KindFieldName attribute
|
// determining resources based on the KindFieldName attribute
|
||||||
// value - see below.
|
// value - see below.
|
||||||
else if ([classes containsObject: @"calendarresource"])
|
if ([classes containsObject: @"calendarresource"])
|
||||||
{
|
{
|
||||||
[ldifRecord setObject: [NSNumber numberWithInt: 1]
|
[ldifRecord setObject: [NSNumber numberWithInt: 1]
|
||||||
forKey: @"isResource"];
|
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,
|
// We check if that entry corresponds to a resource. For this,
|
||||||
|
@ -1326,6 +1380,11 @@ static Class NSStringK;
|
||||||
return modifiers;
|
return modifiers;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
- (NSArray *) groupObjectClasses
|
||||||
|
{
|
||||||
|
return groupObjectClasses;
|
||||||
|
}
|
||||||
|
|
||||||
static NSArray *
|
static NSArray *
|
||||||
_convertRecordToLDAPAttributes (LDAPSourceSchema *schema, NSDictionary *ldifRecord)
|
_convertRecordToLDAPAttributes (LDAPSourceSchema *schema, NSDictionary *ldifRecord)
|
||||||
{
|
{
|
||||||
|
@ -1641,6 +1700,7 @@ _makeLDAPChanges (NGLdapConnection *ldapConnection,
|
||||||
UIDField: @"cn"
|
UIDField: @"cn"
|
||||||
mailFields: nil
|
mailFields: nil
|
||||||
searchFields: nil
|
searchFields: nil
|
||||||
|
groupObjectClasses: nil
|
||||||
IMAPHostField: nil
|
IMAPHostField: nil
|
||||||
IMAPLoginField: nil
|
IMAPLoginField: nil
|
||||||
SieveHostField: nil
|
SieveHostField: nil
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
/* NSData+Crypto.h - this file is part of SOGo
|
/* NSData+Crypto.h - this file is part of SOGo
|
||||||
*
|
*
|
||||||
* Copyright (C) 2012 Nicolas Höft
|
* Copyright (C) 2012 Nicolas Höft
|
||||||
* Copyright (C) 2012 Inverse inc.
|
* Copyright (C) 2012-2015 Inverse inc.
|
||||||
*
|
*
|
||||||
* Author: Nicolas Höft
|
* Author: Nicolas Höft
|
||||||
* Inverse inc.
|
* Inverse inc.
|
||||||
|
@ -34,6 +34,8 @@
|
||||||
- (NSData *) asCryptedPassUsingScheme: (NSString *) passwordScheme
|
- (NSData *) asCryptedPassUsingScheme: (NSString *) passwordScheme
|
||||||
withSalt: (NSData *) theSalt;
|
withSalt: (NSData *) theSalt;
|
||||||
|
|
||||||
|
- (NSData *) asLM;
|
||||||
|
- (NSData *) asMD4;
|
||||||
- (NSData *) asMD5;
|
- (NSData *) asMD5;
|
||||||
- (NSData *) asSMD5UsingSalt: (NSData *) theSalt;
|
- (NSData *) asSMD5UsingSalt: (NSData *) theSalt;
|
||||||
- (NSData *) asSHA1;
|
- (NSData *) asSHA1;
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
/* NSData+Crypto.m - this file is part of SOGo
|
/* NSData+Crypto.m - this file is part of SOGo
|
||||||
*
|
*
|
||||||
* Copyright (C) 2012 Nicolas Höft
|
* Copyright (C) 2012 Nicolas Höft
|
||||||
* Copyright (C) 2012 Inverse inc.
|
* Copyright (C) 2012-2015 Inverse inc.
|
||||||
* Copyright (C) 2012 Jeroen Dekkers
|
* Copyright (C) 2012 Jeroen Dekkers
|
||||||
*
|
*
|
||||||
* Author: Nicolas Höft
|
* Author: Nicolas Höft
|
||||||
|
@ -39,12 +39,14 @@
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
#include <gnutls/gnutls.h>
|
#include <gnutls/gnutls.h>
|
||||||
#include <gnutls/crypto.h>
|
#include <gnutls/crypto.h>
|
||||||
|
#define MD4_DIGEST_LENGTH 16
|
||||||
#define MD5_DIGEST_LENGTH 16
|
#define MD5_DIGEST_LENGTH 16
|
||||||
#define SHA_DIGEST_LENGTH 20
|
#define SHA_DIGEST_LENGTH 20
|
||||||
#define SHA256_DIGEST_LENGTH 32
|
#define SHA256_DIGEST_LENGTH 32
|
||||||
#define SHA512_DIGEST_LENGTH 64
|
#define SHA512_DIGEST_LENGTH 64
|
||||||
#elif defined(HAVE_OPENSSL)
|
#elif defined(HAVE_OPENSSL)
|
||||||
#include <openssl/evp.h>
|
#include <openssl/evp.h>
|
||||||
|
#include <openssl/md4.h>
|
||||||
#include <openssl/md5.h>
|
#include <openssl/md5.h>
|
||||||
#include <openssl/sha.h>
|
#include <openssl/sha.h>
|
||||||
#else
|
#else
|
||||||
|
@ -193,6 +195,10 @@ static void _nettle_md5_compress(uint32_t *digest, const uint8_t *input);
|
||||||
{
|
{
|
||||||
return [self asMD5CryptUsingSalt: theSalt];
|
return [self asMD5CryptUsingSalt: theSalt];
|
||||||
}
|
}
|
||||||
|
else if ([passwordScheme caseInsensitiveCompare: @"md4"] == NSOrderedSame)
|
||||||
|
{
|
||||||
|
return [self asMD4];
|
||||||
|
}
|
||||||
else if ([passwordScheme caseInsensitiveCompare: @"md5"] == NSOrderedSame ||
|
else if ([passwordScheme caseInsensitiveCompare: @"md5"] == NSOrderedSame ||
|
||||||
[passwordScheme caseInsensitiveCompare: @"plain-md5"] == NSOrderedSame ||
|
[passwordScheme caseInsensitiveCompare: @"plain-md5"] == NSOrderedSame ||
|
||||||
[passwordScheme caseInsensitiveCompare: @"ldap-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;
|
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
|
* Hash the data with MD5. Uses openssl functions to generate it
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
/* NSString+Crypto.h - this file is part of SOGo
|
/* NSString+Crypto.h - this file is part of SOGo
|
||||||
*
|
*
|
||||||
* Copyright (C) 2012 Nicolas Höft
|
* Copyright (C) 2012 Nicolas Höft
|
||||||
* Copyright (C) 2012 Inverse inc.
|
* Copyright (C) 2012-2015 Inverse inc.
|
||||||
*
|
*
|
||||||
* Author: Nicolas Höft
|
* Author: Nicolas Höft
|
||||||
* Inverse inc.
|
* Inverse inc.
|
||||||
|
@ -39,7 +39,6 @@ typedef enum {
|
||||||
|
|
||||||
@interface NSString (SOGoCryptoExtension)
|
@interface NSString (SOGoCryptoExtension)
|
||||||
|
|
||||||
|
|
||||||
- (BOOL) isEqualToCrypted: (NSString *) cryptedPassword
|
- (BOOL) isEqualToCrypted: (NSString *) cryptedPassword
|
||||||
withDefaultScheme: (NSString *) theScheme;
|
withDefaultScheme: (NSString *) theScheme;
|
||||||
|
|
||||||
|
@ -56,6 +55,9 @@ typedef enum {
|
||||||
- (NSString *) asSHA1String;
|
- (NSString *) asSHA1String;
|
||||||
- (NSString *) asMD5String;
|
- (NSString *) asMD5String;
|
||||||
|
|
||||||
|
- (NSString *) asNTHash;
|
||||||
|
- (NSString *) asLMHash;
|
||||||
|
|
||||||
+ (NSArray *) getDefaultEncodingForScheme: (NSString *) passwordScheme;
|
+ (NSArray *) getDefaultEncodingForScheme: (NSString *) passwordScheme;
|
||||||
|
|
||||||
@end
|
@end
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
/* NSString+Crypto.m - this file is part of SOGo
|
/* NSString+Crypto.m - this file is part of SOGo
|
||||||
*
|
*
|
||||||
* Copyright (C) 2012 Nicolas Höft
|
* Copyright (C) 2012 Nicolas Höft
|
||||||
* Copyright (C) 2012 Inverse inc.
|
* Copyright (C) 2012-2015 Inverse inc.
|
||||||
*
|
*
|
||||||
* Author: Nicolas Höft
|
* Author: Nicolas Höft
|
||||||
* Inverse inc.
|
* Inverse inc.
|
||||||
|
@ -29,6 +29,8 @@
|
||||||
#import "NSData+Crypto.h"
|
#import "NSData+Crypto.h"
|
||||||
#import <NGExtensions/NGBase64Coding.h>
|
#import <NGExtensions/NGBase64Coding.h>
|
||||||
|
|
||||||
|
#include "lmhash.h"
|
||||||
|
|
||||||
@implementation NSString (SOGoCryptoExtension)
|
@implementation NSString (SOGoCryptoExtension)
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -90,7 +92,8 @@
|
||||||
encodingAndScheme = [NSString getDefaultEncodingForScheme: scheme];
|
encodingAndScheme = [NSString getDefaultEncodingForScheme: scheme];
|
||||||
|
|
||||||
pass = [self substringWithRange: range];
|
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];
|
return [NSArray arrayWithObjects: [encodingAndScheme objectAtIndex: 1], pass, [encodingAndScheme objectAtIndex: 0], nil];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -277,7 +280,8 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
// in order to keep backwards-compatibility, hex encoding is used for sha1 here
|
// 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: @"plain-md5"] == NSOrderedSame ||
|
||||||
[passwordScheme caseInsensitiveCompare: @"sha"] == NSOrderedSame ||
|
[passwordScheme caseInsensitiveCompare: @"sha"] == NSOrderedSame ||
|
||||||
[passwordScheme caseInsensitiveCompare: @"cram-md5"] == NSOrderedSame)
|
[passwordScheme caseInsensitiveCompare: @"cram-md5"] == NSOrderedSame)
|
||||||
|
@ -294,6 +298,7 @@
|
||||||
{
|
{
|
||||||
encoding = encBase64;
|
encoding = encBase64;
|
||||||
}
|
}
|
||||||
|
|
||||||
return [NSArray arrayWithObjects: [NSNumber numberWithInt: encoding], trueScheme, nil];
|
return [NSArray arrayWithObjects: [NSNumber numberWithInt: encoding], trueScheme, nil];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -321,4 +326,33 @@
|
||||||
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
|
@end
|
||||||
|
|
|
@ -113,7 +113,6 @@
|
||||||
+ (void) handleLogoutRequest: (NSString *) logoutRequest
|
+ (void) handleLogoutRequest: (NSString *) logoutRequest
|
||||||
{
|
{
|
||||||
CASLogoutRequest *rq;
|
CASLogoutRequest *rq;
|
||||||
SOGoCache *cache;
|
|
||||||
NSBundle *bundle;
|
NSBundle *bundle;
|
||||||
NSString *mapFile, *sessionIndex;
|
NSString *mapFile, *sessionIndex;
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
/* SOGoDomainDefaults.h - this file is part of SOGo
|
/* 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
|
* 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
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
@ -49,6 +49,7 @@
|
||||||
- (BOOL) forceExternalLoginWithEmail;
|
- (BOOL) forceExternalLoginWithEmail;
|
||||||
- (BOOL) sieveScriptsEnabled;
|
- (BOOL) sieveScriptsEnabled;
|
||||||
- (BOOL) forwardEnabled;
|
- (BOOL) forwardEnabled;
|
||||||
|
- (int) forwardConstraints;
|
||||||
- (BOOL) vacationEnabled;
|
- (BOOL) vacationEnabled;
|
||||||
- (NSString *) mailingMechanism;
|
- (NSString *) mailingMechanism;
|
||||||
- (NSString *) smtpServer;
|
- (NSString *) smtpServer;
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
/* SOGoDomainDefaults.m - this file is part of SOGo
|
/* 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
|
* 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
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
@ -201,6 +201,15 @@
|
||||||
return [self boolForKey: @"SOGoForwardEnabled"];
|
return [self boolForKey: @"SOGoForwardEnabled"];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
- (int) forwardConstraints
|
||||||
|
{
|
||||||
|
unsigned int v;
|
||||||
|
|
||||||
|
v = [self integerForKey: @"SOGoForwardConstraints"];
|
||||||
|
|
||||||
|
return (v > 2 ? 0 : v);
|
||||||
|
}
|
||||||
|
|
||||||
- (BOOL) vacationEnabled
|
- (BOOL) vacationEnabled
|
||||||
{
|
{
|
||||||
return [self boolForKey: @"SOGoVacationEnabled"];
|
return [self boolForKey: @"SOGoVacationEnabled"];
|
||||||
|
|
|
@ -137,6 +137,8 @@
|
||||||
NGLdapEntry *entry;
|
NGLdapEntry *entry;
|
||||||
NSObject <SOGoSource> *source;
|
NSObject <SOGoSource> *source;
|
||||||
id o;
|
id o;
|
||||||
|
NSEnumerator *gclasses;
|
||||||
|
NSString *gclass;
|
||||||
|
|
||||||
int i;
|
int i;
|
||||||
|
|
||||||
|
@ -194,12 +196,11 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Found a group, let's return it.
|
gclasses = [[source groupObjectClasses] objectEnumerator];
|
||||||
if ([classes containsObject: @"group"] ||
|
while (gclass = [gclasses nextObject])
|
||||||
[classes containsObject: @"groupofnames"] ||
|
if ([classes containsObject: gclass])
|
||||||
[classes containsObject: @"groupofuniquenames"] ||
|
|
||||||
[classes containsObject: @"posixgroup"])
|
|
||||||
{
|
{
|
||||||
|
// Found a group, let's return it.
|
||||||
o = [[self alloc] initWithIdentifier: theValue
|
o = [[self alloc] initWithIdentifier: theValue
|
||||||
domain: domain
|
domain: domain
|
||||||
source: source
|
source: source
|
||||||
|
|
|
@ -835,13 +835,16 @@ static NSString *sieveScriptName = @"sogo";
|
||||||
|
|
||||||
if (values && [[values objectForKey: @"enabled"] boolValue])
|
if (values && [[values objectForKey: @"enabled"] boolValue])
|
||||||
{
|
{
|
||||||
|
NSMutableString *vacation_script;
|
||||||
NSArray *addresses;
|
NSArray *addresses;
|
||||||
NSString *text;
|
NSString *text;
|
||||||
BOOL ignore;
|
|
||||||
|
BOOL ignore, alwaysSend;
|
||||||
int days, i;
|
int days, i;
|
||||||
|
|
||||||
days = [[values objectForKey: @"daysBetweenResponse"] intValue];
|
days = [[values objectForKey: @"daysBetweenResponse"] intValue];
|
||||||
addresses = [values objectForKey: @"autoReplyEmailAddresses"];
|
addresses = [values objectForKey: @"autoReplyEmailAddresses"];
|
||||||
|
alwaysSend = [[values objectForKey: @"alwaysSend"] boolValue];
|
||||||
ignore = [[values objectForKey: @"ignoreLists"] boolValue];
|
ignore = [[values objectForKey: @"ignoreLists"] boolValue];
|
||||||
text = [values objectForKey: @"autoReplyText"];
|
text = [values objectForKey: @"autoReplyText"];
|
||||||
b = YES;
|
b = YES;
|
||||||
|
@ -849,28 +852,38 @@ static NSString *sieveScriptName = @"sogo";
|
||||||
if (days == 0)
|
if (days == 0)
|
||||||
days = 7;
|
days = 7;
|
||||||
|
|
||||||
|
vacation_script = [NSMutableString string];
|
||||||
|
|
||||||
[req addObjectUniquely: @"vacation"];
|
[req addObjectUniquely: @"vacation"];
|
||||||
|
|
||||||
// Skip mailing lists
|
// Skip mailing lists
|
||||||
if (ignore)
|
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++)
|
for (i = 0; i < [addresses count]; i++)
|
||||||
{
|
{
|
||||||
[script appendFormat: @"\"%@\"", [addresses objectAtIndex: i]];
|
[vacation_script appendFormat: @"\"%@\"", [addresses objectAtIndex: i]];
|
||||||
|
|
||||||
if (i == [addresses count]-1)
|
if (i == [addresses count]-1)
|
||||||
[script appendString: @"] "];
|
[vacation_script appendString: @"] "];
|
||||||
else
|
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)
|
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];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -110,6 +110,7 @@
|
||||||
- (NSString *) baseDN;
|
- (NSString *) baseDN;
|
||||||
- (NSString *) MSExchangeHostname;
|
- (NSString *) MSExchangeHostname;
|
||||||
|
|
||||||
|
- (NSArray *) groupObjectClasses;
|
||||||
@end
|
@end
|
||||||
|
|
||||||
#endif /* SOGOSOURCE_H */
|
#endif /* SOGOSOURCE_H */
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
/*
|
/*
|
||||||
Copyright (C) 2006-2013 Inverse inc.
|
Copyright (C) 2006-2015 Inverse inc.
|
||||||
Copyright (C) 2005 SKYRIX Software AG
|
Copyright (C) 2005 SKYRIX Software AG
|
||||||
|
|
||||||
This file is part of SOGo.
|
This file is part of SOGo.
|
||||||
|
|
|
@ -37,6 +37,7 @@
|
||||||
#import <NGExtensions/NSObject+Logs.h>
|
#import <NGExtensions/NSObject+Logs.h>
|
||||||
|
|
||||||
#import <Appointments/SOGoAppointmentFolders.h>
|
#import <Appointments/SOGoAppointmentFolders.h>
|
||||||
|
#import <Contacts/SOGoContactFolders.h>
|
||||||
|
|
||||||
#import "NSArray+Utilities.h"
|
#import "NSArray+Utilities.h"
|
||||||
#import "SOGoCache.h"
|
#import "SOGoCache.h"
|
||||||
|
@ -234,7 +235,6 @@
|
||||||
[currentPassword release];
|
[currentPassword release];
|
||||||
[cn release];
|
[cn release];
|
||||||
[loginInDomain release];
|
[loginInDomain release];
|
||||||
//[language release];
|
|
||||||
[super dealloc];
|
[super dealloc];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -577,13 +577,13 @@
|
||||||
- (void) _appendSystemMailAccount
|
- (void) _appendSystemMailAccount
|
||||||
{
|
{
|
||||||
NSString *fullName, *replyTo, *imapLogin, *imapServer, *cImapServer, *signature,
|
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;
|
NSMutableDictionary *mailAccount, *identity, *mailboxes, *receipts;
|
||||||
NSNumber *port;
|
NSNumber *port;
|
||||||
NSMutableArray *identities;
|
NSMutableArray *identities;
|
||||||
NSArray *mails;
|
NSArray *mails;
|
||||||
NSURL *url, *cUrl;
|
NSURL *url, *cUrl;
|
||||||
unsigned int count, max;
|
unsigned int count, max, default_identity;
|
||||||
NSInteger defaultPort;
|
NSInteger defaultPort;
|
||||||
|
|
||||||
[self userDefaults];
|
[self userDefaults];
|
||||||
|
@ -661,8 +661,9 @@
|
||||||
[mailAccount setObject: sieveServer forKey: @"sieveServerName"];
|
[mailAccount setObject: sieveServer forKey: @"sieveServerName"];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Identities
|
||||||
/* identities */
|
defaultEmail = [NSString stringWithFormat: @"%@@%@", [self loginInDomain], [self domain]];
|
||||||
|
default_identity = 0;
|
||||||
identities = [NSMutableArray new];
|
identities = [NSMutableArray new];
|
||||||
mails = [self allEmails];
|
mails = [self allEmails];
|
||||||
[mailAccount setObject: [mails objectAtIndex: 0] forKey: @"name"];
|
[mailAccount setObject: [mails objectAtIndex: 0] forKey: @"name"];
|
||||||
|
@ -702,6 +703,10 @@
|
||||||
if (signature)
|
if (signature)
|
||||||
[identity setObject: signature forKey: @"signature"];
|
[identity setObject: signature forKey: @"signature"];
|
||||||
[identities addObject: identity];
|
[identities addObject: identity];
|
||||||
|
|
||||||
|
if ([[identity objectForKey: @"email"] caseInsensitiveCompare: defaultEmail] == NSOrderedSame)
|
||||||
|
default_identity = [identities count]-1;
|
||||||
|
|
||||||
[identity release];
|
[identity release];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -723,9 +728,13 @@
|
||||||
if (signature)
|
if (signature)
|
||||||
[identity setObject: signature forKey: @"signature"];
|
[identity setObject: signature forKey: @"signature"];
|
||||||
[identities addObject: identity];
|
[identities addObject: identity];
|
||||||
|
|
||||||
|
if ([[identity objectForKey: @"email"] caseInsensitiveCompare: defaultEmail] == NSOrderedSame)
|
||||||
|
default_identity = [identities count]-1;
|
||||||
|
|
||||||
[identity release];
|
[identity release];
|
||||||
}
|
}
|
||||||
[[identities objectAtIndex: 0] setObject: [NSNumber numberWithBool: YES]
|
[[identities objectAtIndex: default_identity] setObject: [NSNumber numberWithBool: YES]
|
||||||
forKey: @"isDefault"];
|
forKey: @"isDefault"];
|
||||||
|
|
||||||
[mailAccount setObject: identities forKey: @"identities"];
|
[mailAccount setObject: identities forKey: @"identities"];
|
||||||
|
|
|
@ -65,10 +65,13 @@
|
||||||
- (NSDictionary *) contactInfosForUserWithUIDorEmail: (NSString *) uid;
|
- (NSDictionary *) contactInfosForUserWithUIDorEmail: (NSString *) uid;
|
||||||
- (NSDictionary *) contactInfosForUserWithUIDorEmail: (NSString *) uid
|
- (NSDictionary *) contactInfosForUserWithUIDorEmail: (NSString *) uid
|
||||||
inDomain: (NSString *) domain;
|
inDomain: (NSString *) domain;
|
||||||
|
- (NSDictionary *) fetchContactWithUID: (NSString *) uid
|
||||||
|
inDomain: (NSString *) domain;
|
||||||
- (NSArray *) fetchContactsMatching: (NSString *) match
|
- (NSArray *) fetchContactsMatching: (NSString *) match
|
||||||
inDomain: (NSString *) domain;
|
inDomain: (NSString *) domain;
|
||||||
- (NSArray *) fetchUsersMatching: (NSString *) filter
|
- (NSArray *) fetchUsersMatching: (NSString *) filter
|
||||||
inDomain: (NSString *) domain;
|
inDomain: (NSString *) domain;
|
||||||
|
- (NSArray *) _compactAndCompleteContacts: (NSEnumerator *) contacts;
|
||||||
|
|
||||||
- (NSString *) getCNForUID: (NSString *) uid;
|
- (NSString *) getCNForUID: (NSString *) uid;
|
||||||
- (NSString *) getEmailForUID: (NSString *) uid;
|
- (NSString *) getEmailForUID: (NSString *) uid;
|
||||||
|
|
|
@ -18,6 +18,7 @@
|
||||||
* Boston, MA 02111-1307, USA.
|
* Boston, MA 02111-1307, USA.
|
||||||
*/
|
*/
|
||||||
#import <Foundation/NSArray.h>
|
#import <Foundation/NSArray.h>
|
||||||
|
#import <Foundation/NSCalendarDate.h>
|
||||||
#import <Foundation/NSDictionary.h>
|
#import <Foundation/NSDictionary.h>
|
||||||
#import <Foundation/NSEnumerator.h>
|
#import <Foundation/NSEnumerator.h>
|
||||||
#import <Foundation/NSException.h>
|
#import <Foundation/NSException.h>
|
||||||
|
@ -914,6 +915,37 @@ static Class NSNullK;
|
||||||
return currentUser;
|
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
|
- (NSArray *) _compactAndCompleteContacts: (NSEnumerator *) contacts
|
||||||
{
|
{
|
||||||
NSMutableDictionary *compactContacts, *returnContact;
|
NSMutableDictionary *compactContacts, *returnContact;
|
||||||
|
|
|
@ -0,0 +1,742 @@
|
||||||
|
#include "lmhash.h"
|
||||||
|
|
||||||
|
#include <stddef.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
|
/* ========================================================================== **
|
||||||
|
*
|
||||||
|
* 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 <map>. This also
|
||||||
|
* represents the number of bytes to be copied to <dst>.
|
||||||
|
*
|
||||||
|
* Output: none.
|
||||||
|
*
|
||||||
|
* Notes: <src> and <dst> must not point to the same location.
|
||||||
|
*
|
||||||
|
* - No checks are done to ensure that there is enough room
|
||||||
|
* in <dst>, or that the bit numbers in <map> do not exceed
|
||||||
|
* the bits available in <src>. A good reason to make this
|
||||||
|
* function static (private).
|
||||||
|
*
|
||||||
|
* - The <mapsize> value is in bytes. All permutations in DES
|
||||||
|
* use tables that are a multiple of 8 bits, so there is no
|
||||||
|
* need to handle partial bytes. (Yes, I know that there
|
||||||
|
* are some machines out there that still use bytes of a size
|
||||||
|
* other than 8 bits. For our purposes we'll stick with 8-bit
|
||||||
|
* bytes.)
|
||||||
|
*
|
||||||
|
* ------------------------------------------------------------------------ **
|
||||||
|
*/
|
||||||
|
{
|
||||||
|
int bitcount;
|
||||||
|
int i;
|
||||||
|
|
||||||
|
/* Clear all bits in the destination.
|
||||||
|
*/
|
||||||
|
for( i = 0; i < mapsize; i++ )
|
||||||
|
dst[i] = 0;
|
||||||
|
|
||||||
|
/* Set destination bit if the mapped source bit it set. */
|
||||||
|
bitcount = mapsize * 8;
|
||||||
|
for( i = 0; i < bitcount; i++ )
|
||||||
|
{
|
||||||
|
if( GETBIT( src, map[i] ) )
|
||||||
|
SETBIT( dst, i );
|
||||||
|
}
|
||||||
|
} /* Permute */
|
||||||
|
|
||||||
|
|
||||||
|
static void KeyShift( uchar *key, const int numbits )
|
||||||
|
/* ------------------------------------------------------------------------ **
|
||||||
|
* Split the 56-bit key in half & left rotate each half by <numbits> bits.
|
||||||
|
*
|
||||||
|
* Input: key - The 56-bit key to be split-rotated.
|
||||||
|
* numbits - The number of bits by which to rotate the key.
|
||||||
|
*
|
||||||
|
* Output: none.
|
||||||
|
*
|
||||||
|
* Notes: There are probably several better ways to implement this.
|
||||||
|
*
|
||||||
|
* ------------------------------------------------------------------------ **
|
||||||
|
*/
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
uchar keep = key[0]; /* Copy the highest order bits of the key. */
|
||||||
|
|
||||||
|
/* Repeat the shift process <numbits> times.
|
||||||
|
*/
|
||||||
|
for( i = 0; i < numbits; i++ )
|
||||||
|
{
|
||||||
|
int j;
|
||||||
|
|
||||||
|
/* Shift the entire thing, byte by byte.
|
||||||
|
*/
|
||||||
|
for( j = 0; j < 7; j++ )
|
||||||
|
{
|
||||||
|
if( j && (key[j] & 0x80) ) /* If the top bit of this byte is set. */
|
||||||
|
key[j-1] |= 0x01; /* ...shift it to last byte's low bit. */
|
||||||
|
key[j] <<= 1; /* Then left-shift the whole byte. */
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Now move the high-order bits of each 28-bit half-key to their
|
||||||
|
* correct locations.
|
||||||
|
* Bit 27 is the lowest order bit of the first half-key.
|
||||||
|
* Before the shift, it was the highest order bit of the 2nd half-key.
|
||||||
|
*/
|
||||||
|
if( GETBIT( key, 27 ) ) /* If bit 27 is set... */
|
||||||
|
{
|
||||||
|
CLRBIT( key, 27 ); /* ...clear bit 27. */
|
||||||
|
SETBIT( key, 55 ); /* ...set lowest order bit of 2nd half-key. */
|
||||||
|
}
|
||||||
|
|
||||||
|
/* We kept the highest order bit of the first half-key in <keep>.
|
||||||
|
* If it's set, copy it to bit 27.
|
||||||
|
*/
|
||||||
|
if( keep & 0x80 )
|
||||||
|
SETBIT( key, 27 );
|
||||||
|
|
||||||
|
/* Rotate the <keep> byte too, in case <numbits> is 2 and there's
|
||||||
|
* a second round coming.
|
||||||
|
*/
|
||||||
|
keep <<= 1;
|
||||||
|
}
|
||||||
|
} /* KeyShift */
|
||||||
|
|
||||||
|
|
||||||
|
static void sbox( uchar *dst, const uchar *src )
|
||||||
|
/* ------------------------------------------------------------------------ **
|
||||||
|
* Perform S-Box substitutions.
|
||||||
|
*
|
||||||
|
* Input: dst - Destination byte array into which the S-Box substituted
|
||||||
|
* bitmap will be written.
|
||||||
|
* src - Source byte array.
|
||||||
|
*
|
||||||
|
* Output: none.
|
||||||
|
*
|
||||||
|
* Notes: It's really not possible (for me, anyway) to understand how
|
||||||
|
* this works without reading one or more detailed explanations.
|
||||||
|
* Quick overview, though:
|
||||||
|
*
|
||||||
|
* After the DataExpansion step (in which a 32-bit bit array is
|
||||||
|
* expanded to a 48-bit bit array) the expanded data block is
|
||||||
|
* XOR'd with 48-bits worth of key. That 48 bits then needs to
|
||||||
|
* be condensed back into 32 bits.
|
||||||
|
*
|
||||||
|
* The S-Box substitution handles the data reduction by breaking
|
||||||
|
* the 48-bit value into eight 6-bit values. For each of these
|
||||||
|
* 6-bit values there is a table (an S-Box table). The table
|
||||||
|
* contains 64 possible values. Conveniently, a 6-bit integer
|
||||||
|
* can represent a value between 0 and 63.
|
||||||
|
*
|
||||||
|
* So, if you think of the 48-bit bit array as an array of 6-bit
|
||||||
|
* integers, you use S-Box table 0 with the 0th 6-bit value.
|
||||||
|
* Table 1 is used with the 6-bit value #1, and so on until #7.
|
||||||
|
* Within each table, the correct substitution is found based
|
||||||
|
* simply on the value of the 6-bit integer.
|
||||||
|
*
|
||||||
|
* Well, the original algorithm (and most documentation) don't
|
||||||
|
* make it so simple. There's a complex formula for mapping
|
||||||
|
* the 6-bit values to the correct substitution. Fortunately,
|
||||||
|
* those lookups can be precomputed (and have been for this
|
||||||
|
* implementation). See pp 274-274 in Schneier.
|
||||||
|
*
|
||||||
|
* Oh, and the substitute values are all 4-bit values, so each
|
||||||
|
* 6-bits gets reduced to 4-bits resulting in a 32-bit bit array.
|
||||||
|
*
|
||||||
|
* ------------------------------------------------------------------------ **
|
||||||
|
*/
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
|
||||||
|
/* Clear the destination array.
|
||||||
|
*/
|
||||||
|
for( i = 0; i < 4; i++ )
|
||||||
|
dst[i] = 0;
|
||||||
|
|
||||||
|
/* For each set of six input bits...
|
||||||
|
*/
|
||||||
|
for( i = 0; i < 8; i++ )
|
||||||
|
{
|
||||||
|
int j;
|
||||||
|
int Snum;
|
||||||
|
int bitnum;
|
||||||
|
|
||||||
|
/* Extract the 6-bit integer from the source.
|
||||||
|
* This will be the lookup key within the SBox[i] array.
|
||||||
|
*/
|
||||||
|
for( Snum = j = 0, bitnum = (i * 6); j < 6; j++, bitnum++ )
|
||||||
|
{
|
||||||
|
Snum <<= 1;
|
||||||
|
Snum |= GETBIT( src, bitnum );
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Find the correct value in the correct SBox[]
|
||||||
|
* and copy it into the destination.
|
||||||
|
* Left shift the nibble four bytes for even values of <i>.
|
||||||
|
*/
|
||||||
|
if( 0 == (i%2) )
|
||||||
|
dst[i/2] |= ((SBox[i][Snum]) << 4);
|
||||||
|
else
|
||||||
|
dst[i/2] |= SBox[i][Snum];
|
||||||
|
}
|
||||||
|
} /* sbox */
|
||||||
|
|
||||||
|
|
||||||
|
static void xor( uchar *dst, const uchar *a, const uchar *b, const int count )
|
||||||
|
/* ------------------------------------------------------------------------ **
|
||||||
|
* Perform an XOR operation on two byte arrays.
|
||||||
|
*
|
||||||
|
* Input: dst - Destination array to which the result will be written.
|
||||||
|
* a - The first string of bytes.
|
||||||
|
* b - The second string of bytes.
|
||||||
|
* count - Number of bytes to XOR against one another.
|
||||||
|
*
|
||||||
|
* Output: none.
|
||||||
|
*
|
||||||
|
* Notes: This function operates on whole byte chunks. There's no need
|
||||||
|
* to XOR partial bytes so no need to write code to handle it.
|
||||||
|
*
|
||||||
|
* - This function essentially implements dst = a ^ b; for byte
|
||||||
|
* arrays.
|
||||||
|
*
|
||||||
|
* - <dst> may safely point to the same location as <a> or <b>.
|
||||||
|
*
|
||||||
|
* ------------------------------------------------------------------------ **
|
||||||
|
*/
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
|
||||||
|
for( i = 0; i < count; i++ )
|
||||||
|
dst[i] = a[i] ^ b[i];
|
||||||
|
} /* xor */
|
||||||
|
|
||||||
|
|
||||||
|
/* -------------------------------------------------------------------------- **
|
||||||
|
* Functions:
|
||||||
|
*/
|
||||||
|
|
||||||
|
uchar *auth_DESkey8to7( uchar *dst, const uchar *key )
|
||||||
|
/* ------------------------------------------------------------------------ **
|
||||||
|
* Compress an 8-byte DES key to its 7-byte form.
|
||||||
|
*
|
||||||
|
* Input: dst - Pointer to a memory location (minimum 7 bytes) to accept
|
||||||
|
* the compressed key.
|
||||||
|
* key - Pointer to an 8-byte DES key. See the notes below.
|
||||||
|
*
|
||||||
|
* Output: A pointer to the compressed key (same as <dst>) or NULL if
|
||||||
|
* either <src> or <dst> were NULL.
|
||||||
|
*
|
||||||
|
* Notes: There are no checks done to ensure that <dst> and <key> point
|
||||||
|
* to sufficient space. Please be carefull.
|
||||||
|
*
|
||||||
|
* The two pointers, <dst> and <key> may point to the same
|
||||||
|
* memory location. Internally, a temporary buffer is used and
|
||||||
|
* the results are copied back to <dst>.
|
||||||
|
*
|
||||||
|
* The DES algorithm uses 8 byte keys by definition. The first
|
||||||
|
* step in the algorithm, however, involves removing every eigth
|
||||||
|
* bit to produce a 56-bit key (seven bytes). SMB authentication
|
||||||
|
* skips this step and uses 7-byte keys. The <auth_DEShash()>
|
||||||
|
* algorithm in this module expects 7-byte keys. This function
|
||||||
|
* is used to convert an 8-byte DES key into a 7-byte SMB DES key.
|
||||||
|
*
|
||||||
|
* ------------------------------------------------------------------------ **
|
||||||
|
*/
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
uchar tmp[7];
|
||||||
|
static const uint8_t map8to7[56] =
|
||||||
|
{
|
||||||
|
0, 1, 2, 3, 4, 5, 6,
|
||||||
|
8, 9, 10, 11, 12, 13, 14,
|
||||||
|
16, 17, 18, 19, 20, 21, 22,
|
||||||
|
24, 25, 26, 27, 28, 29, 30,
|
||||||
|
32, 33, 34, 35, 36, 37, 38,
|
||||||
|
40, 41, 42, 43, 44, 45, 46,
|
||||||
|
48, 49, 50, 51, 52, 53, 54,
|
||||||
|
56, 57, 58, 59, 60, 61, 62
|
||||||
|
};
|
||||||
|
|
||||||
|
if( (NULL == dst) || (NULL == key) )
|
||||||
|
return( NULL );
|
||||||
|
|
||||||
|
Permute( tmp, key, map8to7, 7 );
|
||||||
|
for( i = 0; i < 7; i++ )
|
||||||
|
dst[i] = tmp[i];
|
||||||
|
|
||||||
|
return( dst );
|
||||||
|
} /* auth_DESkey8to7 */
|
||||||
|
|
||||||
|
|
||||||
|
uchar *auth_DEShash( uchar *dst, const uchar *key, const uchar *src )
|
||||||
|
/* ------------------------------------------------------------------------ **
|
||||||
|
* DES encryption of the input data using the input key.
|
||||||
|
*
|
||||||
|
* Input: dst - Destination buffer. It *must* be at least eight bytes
|
||||||
|
* in length, to receive the encrypted result.
|
||||||
|
* key - Encryption key. Exactly seven bytes will be used.
|
||||||
|
* If your key is shorter, ensure that you pad it to seven
|
||||||
|
* bytes.
|
||||||
|
* src - Source data to be encrypted. Exactly eight bytes will
|
||||||
|
* be used. If your source data is shorter, ensure that
|
||||||
|
* you pad it to eight bytes.
|
||||||
|
*
|
||||||
|
* Output: A pointer to the encrpyted data (same as <dst>).
|
||||||
|
*
|
||||||
|
* Notes: In SMB, the DES function is used as a hashing function rather
|
||||||
|
* than an encryption/decryption tool. When used for generating
|
||||||
|
* the LM hash the <src> input is the known value "KGS!@#$%" and
|
||||||
|
* the key is derived from the password entered by the user.
|
||||||
|
* When used to generate the LM or NTLM response, the <key> is
|
||||||
|
* derived from the LM or NTLM hash, and the challenge is used
|
||||||
|
* as the <src> input.
|
||||||
|
* See: http://ubiqx.org/cifs/SMB.html#SMB.8.3
|
||||||
|
*
|
||||||
|
* - This function is called "DEShash" rather than just "DES"
|
||||||
|
* because it is only used for creating LM hashes and the
|
||||||
|
* LM/NTLM responses. For all practical purposes, however, it
|
||||||
|
* is a full DES encryption implementation.
|
||||||
|
*
|
||||||
|
* - This DES implementation does not need to be fast, nor is a
|
||||||
|
* DES decryption function needed. The goal is to keep the
|
||||||
|
* code small, simple, and well documented.
|
||||||
|
*
|
||||||
|
* - The input values are copied and refiddled within the module
|
||||||
|
* and the result is not written to <dst> until the very last
|
||||||
|
* step, so it's okay if <dst> points to the same memory as
|
||||||
|
* <key> or <src>.
|
||||||
|
*
|
||||||
|
* ------------------------------------------------------------------------ **
|
||||||
|
*/
|
||||||
|
{
|
||||||
|
int i; /* Loop counter. */
|
||||||
|
uchar K[7]; /* Holds the key, as we manipulate it. */
|
||||||
|
uchar D[8]; /* The data block, as we manipulate it. */
|
||||||
|
|
||||||
|
/* Create the permutations of the key and the source.
|
||||||
|
*/
|
||||||
|
Permute( K, key, KeyPermuteMap, 7 );
|
||||||
|
Permute( D, src, InitialPermuteMap, 8 );
|
||||||
|
|
||||||
|
/* DES encryption proceeds in 16 rounds.
|
||||||
|
* The stuff inside the loop is known in the literature as "function f".
|
||||||
|
*/
|
||||||
|
for( i = 0; i < 16; i++ )
|
||||||
|
{
|
||||||
|
int j;
|
||||||
|
uchar *L = D; /* The left 4 bytes (half) of the data block. */
|
||||||
|
uchar *R = &(D[4]); /* The right half of the ciphertext block. */
|
||||||
|
uchar Rexp[6]; /* Expanded right half. */
|
||||||
|
uchar Rn[4]; /* New value of R, as we manipulate it. */
|
||||||
|
uchar SubK[6]; /* The 48-bit subkey. */
|
||||||
|
|
||||||
|
/* Generate the subkey for this round.
|
||||||
|
*/
|
||||||
|
KeyShift( K, KeyRotation[i] );
|
||||||
|
Permute( SubK, K, KeyCompression, 6 );
|
||||||
|
|
||||||
|
/* Expand the right half (R) of the data block to 48 bytes,
|
||||||
|
* then XOR the result with the Subkey for this round.
|
||||||
|
*/
|
||||||
|
Permute( Rexp, R, DataExpansion, 6 );
|
||||||
|
xor( Rexp, Rexp, SubK, 6 );
|
||||||
|
|
||||||
|
/* S-Box substitutions, P-Box permutation, and final XOR.
|
||||||
|
* The S-Box substitutions return a 32-bit value, which is then
|
||||||
|
* run through the 32-bit to 32-bit P-Box permutation. The P-Box
|
||||||
|
* result is then XOR'd with the left-hand half of the key.
|
||||||
|
* (Rexp is used as a temporary variable between the P-Box & XOR).
|
||||||
|
*/
|
||||||
|
sbox( Rn, Rexp );
|
||||||
|
Permute( Rexp, Rn, PBox, 4 );
|
||||||
|
xor( Rn, L, Rexp, 4 );
|
||||||
|
|
||||||
|
/* The previous R becomes the new L,
|
||||||
|
* and Rn is moved into R ready for the next round.
|
||||||
|
*/
|
||||||
|
for( j = 0; j < 4; j++ )
|
||||||
|
{
|
||||||
|
L[j] = R[j];
|
||||||
|
R[j] = Rn[j];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* The encryption is complete.
|
||||||
|
* Now reverse-permute the ciphertext to produce the final result.
|
||||||
|
* We actually combine two steps here. The penultimate step is to
|
||||||
|
* swap the positions of L and R in the result of the 16 rounds,
|
||||||
|
* after which the reverse of the Initial Permutation is applied.
|
||||||
|
* To save a step, the FinalPermuteMap applies both the L/R swap
|
||||||
|
* and the inverse of the Initial Permutation.
|
||||||
|
*/
|
||||||
|
Permute( dst, D, FinalPermuteMap, 8 );
|
||||||
|
return( dst );
|
||||||
|
} /* auth_DEShash */
|
||||||
|
|
||||||
|
|
||||||
|
static const uchar SMB_LMhash_Magic[8] =
|
||||||
|
{ 'K', 'G', 'S', '!', '@', '#', '$', '%' };
|
||||||
|
|
||||||
|
uchar *auth_LMhash( uchar *dst, const uchar *pwd, const int pwdlen )
|
||||||
|
/* ------------------------------------------------------------------------ **
|
||||||
|
* Generate an LM Hash from the input password.
|
||||||
|
*
|
||||||
|
* Input: dst - Pointer to a location to which to write the LM Hash.
|
||||||
|
* Requires 16 bytes minimum.
|
||||||
|
* pwd - Source password. Should be in OEM charset (extended
|
||||||
|
* ASCII) format in all upper-case, but this
|
||||||
|
* implementation doesn't really care. See the notes
|
||||||
|
* below.
|
||||||
|
* pwdlen - Length, in bytes, of the password. Normally, this
|
||||||
|
* will be strlen( pwd ).
|
||||||
|
*
|
||||||
|
* Output: Pointer to the resulting LM hash (same as <dst>).
|
||||||
|
*
|
||||||
|
* Notes: This function does not convert the input password to upper
|
||||||
|
* case. The upper-case conversion should be done before the
|
||||||
|
* password gets this far. DOS codepage handling and such
|
||||||
|
* should be taken into consideration. Rather than attempt to
|
||||||
|
* work out all those details here, the function assumes that
|
||||||
|
* the password is in the correct form before it reaches this
|
||||||
|
* point.
|
||||||
|
*
|
||||||
|
* ------------------------------------------------------------------------ **
|
||||||
|
*/
|
||||||
|
{
|
||||||
|
int i,
|
||||||
|
max14;
|
||||||
|
uint8_t tmp_pwd[14] = { 0,0,0,0,0,0,0,0,0,0,0,0,0,0 };
|
||||||
|
|
||||||
|
/* Copy at most 14 bytes of <pwd> into <tmp_pwd>.
|
||||||
|
* If the password is less than 14 bytes long
|
||||||
|
* the rest will be nul padded.
|
||||||
|
*/
|
||||||
|
max14 = pwdlen > 14 ? 14 : pwdlen;
|
||||||
|
for( i = 0; i < max14; i++ )
|
||||||
|
tmp_pwd[i] = pwd[i];
|
||||||
|
|
||||||
|
/* The password is split into two 7-byte keys, each of which
|
||||||
|
* are used to DES-encrypt the magic string. The results are
|
||||||
|
* concatonated to produce the 16-byte LM Hash.
|
||||||
|
*/
|
||||||
|
(void)auth_DEShash( dst, tmp_pwd, SMB_LMhash_Magic );
|
||||||
|
(void)auth_DEShash( &dst[8], &tmp_pwd[7], SMB_LMhash_Magic );
|
||||||
|
|
||||||
|
/* Return a pointer to the result.
|
||||||
|
*/
|
||||||
|
return( dst );
|
||||||
|
} /* auth_LMhash */
|
|
@ -0,0 +1,95 @@
|
||||||
|
#ifndef AUTH_LMHASH_H
|
||||||
|
#define AUTH_LMHASH_H
|
||||||
|
|
||||||
|
typedef unsigned char uchar;
|
||||||
|
|
||||||
|
/* ========================================================================== **
|
||||||
|
*
|
||||||
|
* 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)
|
||||||
|
*
|
||||||
|
* ========================================================================== **
|
||||||
|
*/
|
||||||
|
|
||||||
|
/* -------------------------------------------------------------------------- **
|
||||||
|
* Functions:
|
||||||
|
*/
|
||||||
|
|
||||||
|
uchar *auth_LMhash( uchar *dst, const uchar *pwd, const int pwdlen );
|
||||||
|
/* ------------------------------------------------------------------------ **
|
||||||
|
* Generate an LM Hash from the input password.
|
||||||
|
*
|
||||||
|
* Input: dst - Pointer to a location to which to write the LM Hash.
|
||||||
|
* Requires 16 bytes minimum.
|
||||||
|
* pwd - Source password. Should be in OEM charset (extended
|
||||||
|
* ASCII) format in all upper-case, but this
|
||||||
|
* implementation doesn't really care. See the notes
|
||||||
|
* below.
|
||||||
|
* pwdlen - Length, in bytes, of the password. Normally, this
|
||||||
|
* will be strlen( pwd ).
|
||||||
|
*
|
||||||
|
* Output: Pointer to the resulting LM hash (same as <dst>).
|
||||||
|
*
|
||||||
|
* Notes: This function does not convert the input password to upper
|
||||||
|
* case. The upper-case conversion should be done before the
|
||||||
|
* password gets this far. DOS codepage handling and such
|
||||||
|
* should be taken into consideration. Rather than attempt to
|
||||||
|
* work out all those details here, the function assumes that
|
||||||
|
* the password is in the correct form before it reaches this
|
||||||
|
* point.
|
||||||
|
*
|
||||||
|
* ------------------------------------------------------------------------ **
|
||||||
|
*/
|
||||||
|
#endif
|
|
@ -1,8 +1,6 @@
|
||||||
/* SOGoTool.h - this file is part of SOGo
|
/* SOGoTool.h - this file is part of SOGo
|
||||||
*
|
*
|
||||||
* Copyright (C) 2009-2011 Inverse inc.
|
* Copyright (C) 2009-2015 Inverse inc.
|
||||||
*
|
|
||||||
* Author: Wolfgang Sourdeau <wsourdeau@inverse.ca>
|
|
||||||
*
|
*
|
||||||
* This file is free software; you can redistribute it and/or modify
|
* 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
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
|
|
@ -1,8 +1,6 @@
|
||||||
/* SOGoTool.m - this file is part of SOGo
|
/* SOGoTool.m - this file is part of SOGo
|
||||||
*
|
*
|
||||||
* Copyright (C) 2009-2011 Inverse inc.
|
* Copyright (C) 2009-2015 Inverse inc.
|
||||||
*
|
|
||||||
* Author: Wolfgang Sourdeau <wsourdeau@inverse.ca>
|
|
||||||
*
|
*
|
||||||
* This file is free software; you can redistribute it and/or modify
|
* 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
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
|
|
@ -254,7 +254,7 @@ typedef enum
|
||||||
//
|
//
|
||||||
// to achieve what we want.
|
// to achieve what we want.
|
||||||
//
|
//
|
||||||
if (o && [o count] == 1)
|
if (o && [o isKindOfClass: [NSDictionary class]] && [o count] == 1)
|
||||||
{
|
{
|
||||||
[source setObject: [[o allValues] lastObject] forKey: key];
|
[source setObject: [[o allValues] lastObject] forKey: key];
|
||||||
}
|
}
|
||||||
|
|
|
@ -79,7 +79,6 @@
|
||||||
"Remove this folder" = "مسح المجلد";
|
"Remove this folder" = "مسح المجلد";
|
||||||
"Erase mails from this folder" = "مسح الرسائل من هذا المجلد";
|
"Erase mails from this folder" = "مسح الرسائل من هذا المجلد";
|
||||||
"Expunge this folder" = "محو هذا المجلد";
|
"Expunge this folder" = "محو هذا المجلد";
|
||||||
"Archive This Folder" = "أرشفة هذا المجلد";
|
|
||||||
"Modify the acl of this folder" = "تعديل قائمة الصلاحيات لهذا المجلد";
|
"Modify the acl of this folder" = "تعديل قائمة الصلاحيات لهذا المجلد";
|
||||||
|
|
||||||
"Saved Messages.zip" = "حفظ Messages.zip";
|
"Saved Messages.zip" = "حفظ Messages.zip";
|
||||||
|
|
|
@ -79,7 +79,6 @@
|
||||||
"Remove this folder" = "Fjern denne mappe";
|
"Remove this folder" = "Fjern denne mappe";
|
||||||
"Erase mails from this folder" = "Slet beskeder fra denne mappe";
|
"Erase mails from this folder" = "Slet beskeder fra denne mappe";
|
||||||
"Expunge this folder" = "Slet denne mappe";
|
"Expunge this folder" = "Slet denne mappe";
|
||||||
"Archive This Folder" = "Gem denne mappe";
|
|
||||||
"Modify the acl of this folder" = "Rediger ACL i denne mappe";
|
"Modify the acl of this folder" = "Rediger ACL i denne mappe";
|
||||||
|
|
||||||
"Saved Messages.zip" = "Gemte beskeder.zip";
|
"Saved Messages.zip" = "Gemte beskeder.zip";
|
||||||
|
|
|
@ -76,7 +76,6 @@
|
||||||
"Remove this folder" = "Fjarlægja þessa möppu";
|
"Remove this folder" = "Fjarlægja þessa möppu";
|
||||||
"Erase mails from this folder" = "Eyða bréfum úr þessari möppu";
|
"Erase mails from this folder" = "Eyða bréfum úr þessari möppu";
|
||||||
"Expunge this folder" = "Afmá allt í þessari möppu";
|
"Expunge this folder" = "Afmá allt í þessari möppu";
|
||||||
"Archive This Folder" = "Setja þessa möppu í geymslu";
|
|
||||||
"Modify the acl of this folder" = "Breyta aðgangsstýringum fyrir þessa möppu";
|
"Modify the acl of this folder" = "Breyta aðgangsstýringum fyrir þessa möppu";
|
||||||
|
|
||||||
"Saved Messages.zip" = "vistud_skilabod.zip";
|
"Saved Messages.zip" = "vistud_skilabod.zip";
|
||||||
|
|
|
@ -79,7 +79,6 @@
|
||||||
"Remove this folder" = "Rimuovi questa cartella";
|
"Remove this folder" = "Rimuovi questa cartella";
|
||||||
"Erase mails from this folder" = "Elimina emails da questa cartella";
|
"Erase mails from this folder" = "Elimina emails da questa cartella";
|
||||||
"Expunge this folder" = "Pulisci questa cartella";
|
"Expunge this folder" = "Pulisci questa cartella";
|
||||||
"Archive This Folder" = "Archivia questa cartella";
|
|
||||||
"Modify the acl of this folder" = "Modifica i permessi per questa cartella";
|
"Modify the acl of this folder" = "Modifica i permessi per questa cartella";
|
||||||
|
|
||||||
"Saved Messages.zip" = "Salvato Messages.zip";
|
"Saved Messages.zip" = "Salvato Messages.zip";
|
||||||
|
|
|
@ -76,7 +76,6 @@
|
||||||
"Remove this folder" = "Ta bort mappen";
|
"Remove this folder" = "Ta bort mappen";
|
||||||
"Erase mails from this folder" = "Slett meldinger fra mappen";
|
"Erase mails from this folder" = "Slett meldinger fra mappen";
|
||||||
"Expunge this folder" = "Fjern mappen";
|
"Expunge this folder" = "Fjern mappen";
|
||||||
"Archive This Folder" = "Arkiver denne mappen";
|
|
||||||
"Modify the acl of this folder" = "Endre adgangsrettigheter til mappen";
|
"Modify the acl of this folder" = "Endre adgangsrettigheter til mappen";
|
||||||
|
|
||||||
"Saved Messages.zip" = "Lagrete Meldinger.zip";
|
"Saved Messages.zip" = "Lagrete Meldinger.zip";
|
||||||
|
|
|
@ -76,7 +76,6 @@
|
||||||
"Remove this folder" = "Ta bort mappen";
|
"Remove this folder" = "Ta bort mappen";
|
||||||
"Erase mails from this folder" = "Radera meddelanden från mappen";
|
"Erase mails from this folder" = "Radera meddelanden från mappen";
|
||||||
"Expunge this folder" = "Radera mappen";
|
"Expunge this folder" = "Radera mappen";
|
||||||
"Archive This Folder" = "Arkivera mappen";
|
|
||||||
"Modify the acl of this folder" = "Ändra åtkomsträttigheter på mappen";
|
"Modify the acl of this folder" = "Ändra åtkomsträttigheter på mappen";
|
||||||
|
|
||||||
"Saved Messages.zip" = "Sparat Messages.zip";
|
"Saved Messages.zip" = "Sparat Messages.zip";
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
/*
|
/*
|
||||||
Copyright (C) 2004-2005 SKYRIX Software AG
|
Copyright (C) 2004-2005 SKYRIX Software AG
|
||||||
Copyright (C) 2008-2014 Inverse inc.
|
Copyright (C) 2008-2015 Inverse inc.
|
||||||
|
|
||||||
This file is part of SOGo.
|
This file is part of SOGo.
|
||||||
|
|
||||||
|
@ -230,7 +230,15 @@ static NSArray *infoKeys = nil;
|
||||||
- (NSString *) localeCode
|
- (NSString *) localeCode
|
||||||
{
|
{
|
||||||
// WARNING : NSLocaleCode is not defined in <Foundation/NSUserDefaults.h>
|
// WARNING : NSLocaleCode is not defined in <Foundation/NSUserDefaults.h>
|
||||||
return [locale objectForKey: @"NSLocaleCode"];
|
// Region subtag must be separated by a dash
|
||||||
|
NSMutableString *s = [NSMutableString stringWithString: [locale objectForKey: @"NSLocaleCode"]];
|
||||||
|
|
||||||
|
[s replaceOccurrencesOfString: @"_"
|
||||||
|
withString: @"-"
|
||||||
|
options: 0
|
||||||
|
range: NSMakeRange(0, [s length])];
|
||||||
|
|
||||||
|
return s;
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void) setFrom: (NSString *) newFrom
|
- (void) setFrom: (NSString *) newFrom
|
||||||
|
|
|
@ -712,19 +712,26 @@
|
||||||
WORequest *request;
|
WORequest *request;
|
||||||
SOGoMailFolder *co;
|
SOGoMailFolder *co;
|
||||||
NSException *error;
|
NSException *error;
|
||||||
NSArray *msgUIDs, *flags;
|
NSArray *msgUIDs;
|
||||||
|
NSMutableArray *flags;
|
||||||
NSString *operation;
|
NSString *operation;
|
||||||
NSDictionary *content, *result;
|
NSDictionary *content, *result;
|
||||||
BOOL addOrRemove;
|
BOOL addOrRemove;
|
||||||
NGImap4Client *client;
|
NGImap4Client *client;
|
||||||
|
|
||||||
|
int i;
|
||||||
|
|
||||||
request = [context request];
|
request = [context request];
|
||||||
content = [[request contentAsString] objectFromJSONString];
|
content = [[request contentAsString] objectFromJSONString];
|
||||||
flags = [NSArray arrayWithObject:[content objectForKey:@"flags"]];
|
flags = [NSMutableArray arrayWithObject:[content objectForKey:@"flags"]];
|
||||||
msgUIDs = [NSArray arrayWithArray:[content objectForKey:@"msgUIDs"]];
|
msgUIDs = [NSArray arrayWithArray:[content objectForKey:@"msgUIDs"]];
|
||||||
operation = [content objectForKey:@"operation"];
|
operation = [content objectForKey:@"operation"];
|
||||||
addOrRemove = ([operation isEqualToString:@"add"]? YES: NO);
|
addOrRemove = ([operation isEqualToString:@"add"]? YES: NO);
|
||||||
|
|
||||||
|
// We unescape our flags
|
||||||
|
for (i = [flags count]-1; i >= 0; i--)
|
||||||
|
[flags replaceObjectAtIndex: i withObject: [[flags objectAtIndex: i] fromCSSIdentifier]];
|
||||||
|
|
||||||
co = [self clientObject];
|
co = [self clientObject];
|
||||||
client = [[co imap4Connection] client];
|
client = [[co imap4Connection] client];
|
||||||
[[co imap4Connection] selectFolder: [co imap4URL]];
|
[[co imap4Connection] selectFolder: [co imap4URL]];
|
||||||
|
|
|
@ -79,7 +79,6 @@
|
||||||
"Remove this folder" = "Вилучати цю теку";
|
"Remove this folder" = "Вилучати цю теку";
|
||||||
"Erase mails from this folder" = "Вилучати повідомлення з цієї теки";
|
"Erase mails from this folder" = "Вилучати повідомлення з цієї теки";
|
||||||
"Expunge this folder" = "Позначати листи як вилучені";
|
"Expunge this folder" = "Позначати листи як вилучені";
|
||||||
"Archive This Folder" = "Архівувати цю теку";
|
|
||||||
"Modify the acl of this folder" = "Керувати правами доступу до цієї теки";
|
"Modify the acl of this folder" = "Керувати правами доступу до цієї теки";
|
||||||
|
|
||||||
"Saved Messages.zip" = "Збережено Messages.zip";
|
"Saved Messages.zip" = "Збережено Messages.zip";
|
||||||
|
|
|
@ -76,7 +76,6 @@
|
||||||
"Remove this folder" = "Dileu'r ffolder hwn";
|
"Remove this folder" = "Dileu'r ffolder hwn";
|
||||||
"Erase mails from this folder" = "Dileu ebost o'r ffolder hwn";
|
"Erase mails from this folder" = "Dileu ebost o'r ffolder hwn";
|
||||||
"Expunge this folder" = "Dileu'r ffolder hwn";
|
"Expunge this folder" = "Dileu'r ffolder hwn";
|
||||||
"Archive This Folder" = "Archive This Folder";
|
|
||||||
"Modify the acl of this folder" = "Newid acl y ffolder";
|
"Modify the acl of this folder" = "Newid acl y ffolder";
|
||||||
|
|
||||||
"Saved Messages.zip" = "Saved Messages.zip";
|
"Saved Messages.zip" = "Saved Messages.zip";
|
||||||
|
|
|
@ -155,6 +155,7 @@
|
||||||
|
|
||||||
delta = [[currentDate timeZoneDetail] timeZoneSecondsFromGMT] - [[startDate timeZoneDetail] timeZoneSecondsFromGMT];
|
delta = [[currentDate timeZoneDetail] timeZoneSecondsFromGMT] - [[startDate timeZoneDetail] timeZoneSecondsFromGMT];
|
||||||
startInterval += (delta/60/15);
|
startInterval += (delta/60/15);
|
||||||
|
startInterval = (startInterval < -4 ? -4 : startInterval);
|
||||||
|
|
||||||
currentDate = [record objectForKey: @"endDate"];
|
currentDate = [record objectForKey: @"endDate"];
|
||||||
if ([currentDate earlierDate: endDate] == endDate)
|
if ([currentDate earlierDate: endDate] == endDate)
|
||||||
|
@ -165,6 +166,8 @@
|
||||||
|
|
||||||
delta = [[currentDate timeZoneDetail] timeZoneSecondsFromGMT] - [[startDate timeZoneDetail] timeZoneSecondsFromGMT];
|
delta = [[currentDate timeZoneDetail] timeZoneSecondsFromGMT] - [[startDate timeZoneDetail] timeZoneSecondsFromGMT];
|
||||||
endInterval += (delta/60/15);
|
endInterval += (delta/60/15);
|
||||||
|
endInterval = (endInterval < 0 ? 0 : endInterval);
|
||||||
|
endInterval = (endInterval > itemCount+4 ? itemCount+4 : endInterval);
|
||||||
|
|
||||||
// Update bit string representation
|
// Update bit string representation
|
||||||
// If the user is a resource with restristed amount of bookings, keep the sum of overlapping events
|
// If the user is a resource with restristed amount of bookings, keep the sum of overlapping events
|
||||||
|
@ -230,7 +233,7 @@
|
||||||
intervals = interval / intervalSeconds + 8;
|
intervals = interval / intervalSeconds + 8;
|
||||||
|
|
||||||
// Build a bit string representation of the freebusy data for the period
|
// Build a bit string representation of the freebusy data for the period
|
||||||
freeBusyItems = NSZoneCalloc (NULL, intervals, sizeof (int));
|
freeBusyItems = calloc(intervals, sizeof (unsigned int));
|
||||||
[self _fillFreeBusyItems: (freeBusyItems+4)
|
[self _fillFreeBusyItems: (freeBusyItems+4)
|
||||||
count: (intervals-4)
|
count: (intervals-4)
|
||||||
withRecords: [fb fetchFreeBusyInfosFrom: start to: end forContact: uid]
|
withRecords: [fb fetchFreeBusyInfosFrom: start to: end forContact: uid]
|
||||||
|
@ -243,7 +246,7 @@
|
||||||
{
|
{
|
||||||
[freeBusy addObject: [NSString stringWithFormat: @"%d", *(freeBusyItems + count)]];
|
[freeBusy addObject: [NSString stringWithFormat: @"%d", *(freeBusyItems + count)]];
|
||||||
}
|
}
|
||||||
NSZoneFree (NULL, freeBusyItems);
|
free(freeBusyItems);
|
||||||
|
|
||||||
// Return a NSString representation
|
// Return a NSString representation
|
||||||
return [freeBusy componentsJoinedByString: @","];
|
return [freeBusy componentsJoinedByString: @","];
|
||||||
|
|
|
@ -29,6 +29,7 @@
|
||||||
"Days between responses :" = "Počet dnů mezi odpověďmi :";
|
"Days between responses :" = "Počet dnů mezi odpověďmi :";
|
||||||
"Do not send responses to mailing lists" = "Neposílat odpovědi do konferencí";
|
"Do not send responses to mailing lists" = "Neposílat odpovědi do konferencí";
|
||||||
"Disable auto reply on" = "Automatické odpovědi vypnout dne";
|
"Disable auto reply on" = "Automatické odpovědi vypnout dne";
|
||||||
|
"Always send vacation message response" = "Vždy posílat zprávu v nepřítomnosti";
|
||||||
"Please specify your message and your email addresses for which you want to enable auto reply."
|
"Please specify your message and your email addresses for which you want to enable auto reply."
|
||||||
= "Prosím zadejte text zprávy a své emailové adresy, pro které chcete zasílání v nepřítomnosti použít.";
|
= "Prosím zadejte text zprávy a své emailové adresy, pro které chcete zasílání v nepřítomnosti použít.";
|
||||||
"Your vacation message must not end with a single dot on a line." = "Vaše zpráva v nepřítomnosti nesmí končit samotnou tečkou na řádce.";
|
"Your vacation message must not end with a single dot on a line." = "Vaše zpráva v nepřítomnosti nesmí končit samotnou tečkou na řádce.";
|
||||||
|
@ -40,6 +41,9 @@
|
||||||
"Keep a copy" = "Ponechat kopii";
|
"Keep a copy" = "Ponechat kopii";
|
||||||
"Please specify an address to which you want to forward your messages."
|
"Please specify an address to which you want to forward your messages."
|
||||||
= "Prosím zadejte adresu, na kterou chcete své zprávy přeposílat.";
|
= "Prosím zadejte adresu, na kterou chcete své zprávy přeposílat.";
|
||||||
|
"You are not allowed to forward your messages to an external email address." = "Nemáte povoleno přeposílání Vašich zpráv na externí emailovou adresu.";
|
||||||
|
"You are not allowed to forward your messages to an internal email address." = "Nemáte povoleno přeposílání Vašich zpráv na interní emailovou adresu.";
|
||||||
|
|
||||||
|
|
||||||
/* d & t */
|
/* d & t */
|
||||||
"Current Time Zone :" = "Současné časové pásmo :";
|
"Current Time Zone :" = "Současné časové pásmo :";
|
||||||
|
|
|
@ -29,6 +29,7 @@
|
||||||
"Days between responses :" = "Days between responses:";
|
"Days between responses :" = "Days between responses:";
|
||||||
"Do not send responses to mailing lists" = "Do not send responses to mailing lists";
|
"Do not send responses to mailing lists" = "Do not send responses to mailing lists";
|
||||||
"Disable auto reply on" = "Disable auto reply on";
|
"Disable auto reply on" = "Disable auto reply on";
|
||||||
|
"Always send vacation message response" = "Always send vacation message response";
|
||||||
"Please specify your message and your email addresses for which you want to enable auto reply."
|
"Please specify your message and your email addresses for which you want to enable auto reply."
|
||||||
= "Please specify your message and your email addresses for which you want to enable auto reply.";
|
= "Please specify your message and your email addresses for which you want to enable auto reply.";
|
||||||
"Your vacation message must not end with a single dot on a line." = "Your vacation message must not end with a single dot on a line.";
|
"Your vacation message must not end with a single dot on a line." = "Your vacation message must not end with a single dot on a line.";
|
||||||
|
@ -40,6 +41,9 @@
|
||||||
"Keep a copy" = "Keep a copy";
|
"Keep a copy" = "Keep a copy";
|
||||||
"Please specify an address to which you want to forward your messages."
|
"Please specify an address to which you want to forward your messages."
|
||||||
= "Please specify an address to which you want to forward your messages.";
|
= "Please specify an address to which you want to forward your messages.";
|
||||||
|
"You are not allowed to forward your messages to an external email address." = "You are not allowed to forward your messages to an external email address.";
|
||||||
|
"You are not allowed to forward your messages to an internal email address." = "You are not allowed to forward your messages to an internal email address.";
|
||||||
|
|
||||||
|
|
||||||
/* d & t */
|
/* d & t */
|
||||||
"Current Time Zone :" = "Current Time Zone:";
|
"Current Time Zone :" = "Current Time Zone:";
|
||||||
|
|
|
@ -29,6 +29,7 @@
|
||||||
"Days between responses :" = "Päivät vastausten välillä:";
|
"Days between responses :" = "Päivät vastausten välillä:";
|
||||||
"Do not send responses to mailing lists" = "Älä lähetä vastauksia postituslistoille";
|
"Do not send responses to mailing lists" = "Älä lähetä vastauksia postituslistoille";
|
||||||
"Disable auto reply on" = "Poista automaattivastaus käytöstä";
|
"Disable auto reply on" = "Poista automaattivastaus käytöstä";
|
||||||
|
"Always send vacation message response" = "Lähetä lomaviestivastaus aina";
|
||||||
"Please specify your message and your email addresses for which you want to enable auto reply."
|
"Please specify your message and your email addresses for which you want to enable auto reply."
|
||||||
= "Ole hyvä ja määritä viesti sekä sähköpostiosoitteet joille haluat aktivoida automaattivastauksen.";
|
= "Ole hyvä ja määritä viesti sekä sähköpostiosoitteet joille haluat aktivoida automaattivastauksen.";
|
||||||
"Your vacation message must not end with a single dot on a line." = "Lomaviestisi ei saa päättyä yksittäiseen pisteeseen viimeisellä rivillä.";
|
"Your vacation message must not end with a single dot on a line." = "Lomaviestisi ei saa päättyä yksittäiseen pisteeseen viimeisellä rivillä.";
|
||||||
|
@ -40,6 +41,9 @@
|
||||||
"Keep a copy" = "Pidä kopio";
|
"Keep a copy" = "Pidä kopio";
|
||||||
"Please specify an address to which you want to forward your messages."
|
"Please specify an address to which you want to forward your messages."
|
||||||
= "Ole hyvä ja määritä sähköpostiosoite johon haluat edelleenlähettää viestisi.";
|
= "Ole hyvä ja määritä sähköpostiosoite johon haluat edelleenlähettää viestisi.";
|
||||||
|
"You are not allowed to forward your messages to an external email address." = "Sinulla ei ole lupaa välittää viestejä ulkoiseen sähköpostiosoitteeseen.";
|
||||||
|
"You are not allowed to forward your messages to an internal email address." = "Sinulla ei ole lupaa välittää viestejä sisäiseen sähköpostiosoitteeseen.";
|
||||||
|
|
||||||
|
|
||||||
/* d & t */
|
/* d & t */
|
||||||
"Current Time Zone :" = "Nykyinen aikavyöhyke:";
|
"Current Time Zone :" = "Nykyinen aikavyöhyke:";
|
||||||
|
|
|
@ -29,6 +29,7 @@
|
||||||
"Days between responses :" = "Nombre de jours entre réponses :";
|
"Days between responses :" = "Nombre de jours entre réponses :";
|
||||||
"Do not send responses to mailing lists" = "Ne pas envoyer les réponses aux listes de diffusion";
|
"Do not send responses to mailing lists" = "Ne pas envoyer les réponses aux listes de diffusion";
|
||||||
"Disable auto reply on" = "Désactiver la réponse automatique le";
|
"Disable auto reply on" = "Désactiver la réponse automatique le";
|
||||||
|
"Always send vacation message response" = "Toujours envoyer le message de vacances prolongées";
|
||||||
"Please specify your message and your email addresses for which you want to enable auto reply."
|
"Please specify your message and your email addresses for which you want to enable auto reply."
|
||||||
= "Veuillez définir un message et vos adresses pour lesquelles pour désirez activer une réponse automatique.";
|
= "Veuillez définir un message et vos adresses pour lesquelles pour désirez activer une réponse automatique.";
|
||||||
"Your vacation message must not end with a single dot on a line." = "Le message de vacances ne doit pas se terminer par une ligne ne contenant qu'un point.";
|
"Your vacation message must not end with a single dot on a line." = "Le message de vacances ne doit pas se terminer par une ligne ne contenant qu'un point.";
|
||||||
|
@ -40,6 +41,9 @@
|
||||||
"Keep a copy" = "Garder une copie";
|
"Keep a copy" = "Garder une copie";
|
||||||
"Please specify an address to which you want to forward your messages."
|
"Please specify an address to which you want to forward your messages."
|
||||||
= "Veuillez définir une adresse à laquelle vous désirez transférer automatiquement vos nouveaux messages.";
|
= "Veuillez définir une adresse à laquelle vous désirez transférer automatiquement vos nouveaux messages.";
|
||||||
|
"You are not allowed to forward your messages to an external email address." = "Il est interdit de renvoyer vos messages vers une adresse externe.";
|
||||||
|
"You are not allowed to forward your messages to an internal email address." = "Il est interdit de renvoyer vos messages vers une adresse interne.";
|
||||||
|
|
||||||
|
|
||||||
/* d & t */
|
/* d & t */
|
||||||
"Current Time Zone :" = "Fuseau horaire en cours :";
|
"Current Time Zone :" = "Fuseau horaire en cours :";
|
||||||
|
|
|
@ -29,6 +29,7 @@
|
||||||
"Days between responses :" = "Tage zwischen automatischen Abwesenheitsnachrichten:";
|
"Days between responses :" = "Tage zwischen automatischen Abwesenheitsnachrichten:";
|
||||||
"Do not send responses to mailing lists" = "Keine automatischen Abwesenheitsnachrichten an Mailinglisten senden";
|
"Do not send responses to mailing lists" = "Keine automatischen Abwesenheitsnachrichten an Mailinglisten senden";
|
||||||
"Disable auto reply on" = "Automatische Abwesenheitsnachricht abschalten am";
|
"Disable auto reply on" = "Automatische Abwesenheitsnachricht abschalten am";
|
||||||
|
"Always send vacation message response" = "Immer Abwesenheitsmeldung senden";
|
||||||
"Please specify your message and your email addresses for which you want to enable auto reply."
|
"Please specify your message and your email addresses for which you want to enable auto reply."
|
||||||
= "Bitte ihren Nachrichtentext und ihre E-Mail-Adressen angeben, für die automatische Abwesenheitsnachrichten aktiviert werden sollen.";
|
= "Bitte ihren Nachrichtentext und ihre E-Mail-Adressen angeben, für die automatische Abwesenheitsnachrichten aktiviert werden sollen.";
|
||||||
"Your vacation message must not end with a single dot on a line." = "Ihre Abwesenheitsnachricht darf nicht mit einem alleinstehenden Punkt in einer Zeile enden.";
|
"Your vacation message must not end with a single dot on a line." = "Ihre Abwesenheitsnachricht darf nicht mit einem alleinstehenden Punkt in einer Zeile enden.";
|
||||||
|
@ -40,6 +41,9 @@
|
||||||
"Keep a copy" = "Eine Kopie behalten";
|
"Keep a copy" = "Eine Kopie behalten";
|
||||||
"Please specify an address to which you want to forward your messages."
|
"Please specify an address to which you want to forward your messages."
|
||||||
= "Bitte Adresse angeben, an die Ihre Nachrichten weitergeleitet werden sollen.";
|
= "Bitte Adresse angeben, an die Ihre Nachrichten weitergeleitet werden sollen.";
|
||||||
|
"You are not allowed to forward your messages to an external email address." = "Es ist Ihnen nicht erlaubt, Ihre Nachrichten an externe E-Mail-Adressen weiterzuleiten.";
|
||||||
|
"You are not allowed to forward your messages to an internal email address." = "Es ist Ihnen nicht erlaubt, Ihre Nachrichten an interne E-Mail-Adressen weiterzuleiten.";
|
||||||
|
|
||||||
|
|
||||||
/* d & t */
|
/* d & t */
|
||||||
"Current Time Zone :" = "Aktuelle Zeitzone:";
|
"Current Time Zone :" = "Aktuelle Zeitzone:";
|
||||||
|
|
|
@ -29,6 +29,7 @@
|
||||||
"Days between responses :" = "Válaszok közötti napok száma:";
|
"Days between responses :" = "Válaszok közötti napok száma:";
|
||||||
"Do not send responses to mailing lists" = "Levelező listákra válasz mellőzése";
|
"Do not send responses to mailing lists" = "Levelező listákra válasz mellőzése";
|
||||||
"Disable auto reply on" = "Automatikus válasz tiltása";
|
"Disable auto reply on" = "Automatikus válasz tiltása";
|
||||||
|
"Always send vacation message response" = "Mindig küldjön távollét üzenet választ";
|
||||||
"Please specify your message and your email addresses for which you want to enable auto reply."
|
"Please specify your message and your email addresses for which you want to enable auto reply."
|
||||||
= "Kérem adja meg az üzenetét valamint a címeket, amelyekre engedélyezi az automatikus válasz küldését.";
|
= "Kérem adja meg az üzenetét valamint a címeket, amelyekre engedélyezi az automatikus válasz küldését.";
|
||||||
"Your vacation message must not end with a single dot on a line." = "A távollét üzenet utolsó sora nem tartalmazhat egy egyedülálló . karatert.";
|
"Your vacation message must not end with a single dot on a line." = "A távollét üzenet utolsó sora nem tartalmazhat egy egyedülálló . karatert.";
|
||||||
|
@ -40,6 +41,9 @@
|
||||||
"Keep a copy" = "Másolat megtartása";
|
"Keep a copy" = "Másolat megtartása";
|
||||||
"Please specify an address to which you want to forward your messages."
|
"Please specify an address to which you want to forward your messages."
|
||||||
= "Kérem adjon meg egy címet, amelyre a leveleit továbbítani kívánja.";
|
= "Kérem adjon meg egy címet, amelyre a leveleit továbbítani kívánja.";
|
||||||
|
"You are not allowed to forward your messages to an external email address." = "Ön számára nem endélyezett az üzenetek továbbítása külső email címre.";
|
||||||
|
"You are not allowed to forward your messages to an internal email address." = "Ön számára nem endélyezett az üzenetek továbbítása belső email címre.";
|
||||||
|
|
||||||
|
|
||||||
/* d & t */
|
/* d & t */
|
||||||
"Current Time Zone :" = "Időzóna :";
|
"Current Time Zone :" = "Időzóna :";
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
/* UIxPreferences.m - this file is part of SOGo
|
/* UIxPreferences.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
|
* 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
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
@ -1127,6 +1127,30 @@ static NSArray *reminderValues = nil;
|
||||||
return ignore;
|
return ignore;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// See http://sogo.nu/bugs/view.php?id=2332 for details
|
||||||
|
//
|
||||||
|
- (void) setAlwaysSend: (BOOL) ignoreLists
|
||||||
|
{
|
||||||
|
[vacationOptions setObject: [NSNumber numberWithBool: ignoreLists]
|
||||||
|
forKey: @"alwaysSend"];
|
||||||
|
}
|
||||||
|
|
||||||
|
- (BOOL) alwaysSend
|
||||||
|
{
|
||||||
|
NSNumber *obj;
|
||||||
|
BOOL ignore;
|
||||||
|
|
||||||
|
obj = [vacationOptions objectForKey: @"alwaysSend"];
|
||||||
|
|
||||||
|
if (obj == nil)
|
||||||
|
ignore = NO; // defaults to NO
|
||||||
|
else
|
||||||
|
ignore = [obj boolValue];
|
||||||
|
|
||||||
|
return ignore;
|
||||||
|
}
|
||||||
|
|
||||||
- (BOOL) enableVacationEndDate
|
- (BOOL) enableVacationEndDate
|
||||||
{
|
{
|
||||||
return [[vacationOptions objectForKey: @"endDateEnabled"] boolValue];
|
return [[vacationOptions objectForKey: @"endDateEnabled"] boolValue];
|
||||||
|
@ -1214,6 +1238,15 @@ static NSArray *reminderValues = nil;
|
||||||
return [[forwardOptions objectForKey: @"keepCopy"] boolValue];
|
return [[forwardOptions objectForKey: @"keepCopy"] boolValue];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
- (NSString *) forwardConstraints
|
||||||
|
{
|
||||||
|
SOGoDomainDefaults *dd;
|
||||||
|
|
||||||
|
dd = [[context activeUser] domainDefaults];
|
||||||
|
|
||||||
|
return [NSString stringWithFormat: @"%d", [dd forwardConstraints]];
|
||||||
|
}
|
||||||
|
|
||||||
/* main */
|
/* main */
|
||||||
|
|
||||||
- (NSArray *) availableModules
|
- (NSArray *) availableModules
|
||||||
|
|
|
@ -1,8 +1,6 @@
|
||||||
/* UIxAttendeesEditor.h - this file is part of SOGo
|
/* UIxAttendeesEditor.h - this file is part of SOGo
|
||||||
*
|
*
|
||||||
* Copyright (C) 2007-2012 Inverse inc.
|
* Copyright (C) 2007-2015 Inverse inc.
|
||||||
*
|
|
||||||
* Author: Wolfgang Sourdeau <wsourdeau@inverse.ca>
|
|
||||||
*
|
*
|
||||||
* This file is free software; you can redistribute it and/or modify
|
* 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
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
|
|
@ -1,8 +1,6 @@
|
||||||
/* UIxAttendeesEditor.m - this file is part of SOGo
|
/* UIxAttendeesEditor.m - this file is part of SOGo
|
||||||
*
|
*
|
||||||
* Copyright (C) 2007-2012 Inverse inc.
|
* Copyright (C) 2007-2015 Inverse inc.
|
||||||
*
|
|
||||||
* Author: Wolfgang Sourdeau <wsourdeau@inverse.ca>
|
|
||||||
*
|
*
|
||||||
* This file is free software; you can redistribute it and/or modify
|
* 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
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
|
|
@ -1,7 +1,6 @@
|
||||||
/* UIxCalMainView.m - this file is part of SOGo
|
/* UIxCalMainView.m - this file is part of SOGo
|
||||||
*
|
*
|
||||||
* Copyright (C) 2006-2014 Inverse inc.
|
* Copyright (C) 2006-2015 Inverse inc.
|
||||||
*
|
|
||||||
*
|
*
|
||||||
* This file is free software; you can redistribute it and/or modify
|
* 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
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
@ -82,7 +81,15 @@
|
||||||
- (NSString *) localeCode
|
- (NSString *) localeCode
|
||||||
{
|
{
|
||||||
// WARNING : NSLocaleCode is not defined in <Foundation/NSUserDefaults.h>
|
// WARNING : NSLocaleCode is not defined in <Foundation/NSUserDefaults.h>
|
||||||
return [locale objectForKey: @"NSLocaleCode"];
|
// Region subtag must be separated by a dash
|
||||||
|
NSMutableString *s = [NSMutableString stringWithString: [locale objectForKey: @"NSLocaleCode"]];
|
||||||
|
|
||||||
|
[s replaceOccurrencesOfString: @"_"
|
||||||
|
withString: @"-"
|
||||||
|
options: 0
|
||||||
|
range: NSMakeRange(0, [s length])];
|
||||||
|
|
||||||
|
return s;
|
||||||
}
|
}
|
||||||
|
|
||||||
- (NSArray *) monthMenuItems
|
- (NSArray *) monthMenuItems
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
/* UIxComponentEditor.h - this file is part of SOGo
|
/* UIxComponentEditor.h - this file is part of SOGo
|
||||||
*
|
*
|
||||||
* Copyright (C) 2006-2014 Inverse inc.
|
* Copyright (C) 2006-2015 Inverse inc.
|
||||||
*
|
*
|
||||||
* This file is free software; you can redistribute it and/or modify
|
* 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
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
|
|
@ -176,7 +176,6 @@ iRANGE(2);
|
||||||
[self setIsCycleEndNever];
|
[self setIsCycleEndNever];
|
||||||
componentOwner = @"";
|
componentOwner = @"";
|
||||||
organizer = nil;
|
organizer = nil;
|
||||||
//organizerIdentity = nil;
|
|
||||||
organizerProfile = nil;
|
organizerProfile = nil;
|
||||||
ownerAsAttendee = nil;
|
ownerAsAttendee = nil;
|
||||||
attendee = nil;
|
attendee = nil;
|
||||||
|
@ -213,7 +212,6 @@ iRANGE(2);
|
||||||
[title release];
|
[title release];
|
||||||
[location release];
|
[location release];
|
||||||
[organizer release];
|
[organizer release];
|
||||||
//[organizerIdentity release];
|
|
||||||
[organizerProfile release];
|
[organizerProfile release];
|
||||||
[ownerAsAttendee release];
|
[ownerAsAttendee release];
|
||||||
[comment release];
|
[comment release];
|
||||||
|
@ -827,11 +825,13 @@ iRANGE(2);
|
||||||
|
|
||||||
if (partstat == nil || ![partstat length])
|
if (partstat == nil || ![partstat length])
|
||||||
partstat = @"accepted";
|
partstat = @"accepted";
|
||||||
|
|
||||||
[profile setObject: partstat
|
[profile setObject: partstat
|
||||||
forKey: @"partstat"];
|
forKey: @"partstat"];
|
||||||
|
|
||||||
if (role == nil || ![role length])
|
if (role == nil || ![role length])
|
||||||
role = @"chair";
|
role = @"chair";
|
||||||
|
|
||||||
[profile setObject: role
|
[profile setObject: role
|
||||||
forKey: @"role"];
|
forKey: @"role"];
|
||||||
|
|
||||||
|
@ -897,30 +897,6 @@ iRANGE(2);
|
||||||
return [[[[self organizerProfile] allValues] lastObject] jsonRepresentation];
|
return [[[[self organizerProfile] allValues] lastObject] jsonRepresentation];
|
||||||
}
|
}
|
||||||
|
|
||||||
// - (BOOL) canBeOrganizer
|
|
||||||
// {
|
|
||||||
// NSString *owner;
|
|
||||||
// SOGoObject <SOGoComponentOccurence> *co;
|
|
||||||
// SOGoUser *currentUser;
|
|
||||||
// BOOL hasOrganizer;
|
|
||||||
// SoSecurityManager *sm;
|
|
||||||
|
|
||||||
// co = [self clientObject];
|
|
||||||
// owner = [co ownerInContext: context];
|
|
||||||
// currentUser = [context activeUser];
|
|
||||||
|
|
||||||
// hasOrganizer = ([[organizer value: 0] length] > 0);
|
|
||||||
|
|
||||||
// sm = [SoSecurityManager sharedSecurityManager];
|
|
||||||
|
|
||||||
// return ([co isNew]
|
|
||||||
// || (([owner isEqualToString: [currentUser login]]
|
|
||||||
// || ![sm validatePermission: SOGoCalendarPerm_ModifyComponent
|
|
||||||
// onObject: co
|
|
||||||
// inContext: context])
|
|
||||||
// && (!hasOrganizer || [component userIsOrganizer: currentUser])));
|
|
||||||
// }
|
|
||||||
|
|
||||||
- (BOOL) hasOrganizer
|
- (BOOL) hasOrganizer
|
||||||
{
|
{
|
||||||
// We check if there's an organizer and if it's not ourself
|
// We check if there's an organizer and if it's not ourself
|
||||||
|
@ -948,51 +924,8 @@ iRANGE(2);
|
||||||
}
|
}
|
||||||
|
|
||||||
return NO;
|
return NO;
|
||||||
|
|
||||||
//return ([[organizer value: 0] length] && ![self canBeOrganizer]);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
//- (void) setOrganizerIdentity: (NSDictionary *) newOrganizerIdentity
|
|
||||||
//{
|
|
||||||
// ASSIGN (organizerIdentity, newOrganizerIdentity);
|
|
||||||
//}
|
|
||||||
|
|
||||||
// - (NSDictionary *) organizerIdentity
|
|
||||||
// {
|
|
||||||
// NSArray *allIdentities;
|
|
||||||
// NSEnumerator *identities;
|
|
||||||
// NSDictionary *currentIdentity;
|
|
||||||
// NSString *orgEmail;
|
|
||||||
|
|
||||||
// orgEmail = [organizer rfc822Email];
|
|
||||||
// if (!organizerIdentity)
|
|
||||||
// {
|
|
||||||
// if ([orgEmail length])
|
|
||||||
// {
|
|
||||||
// allIdentities = [[context activeUser] allIdentities];
|
|
||||||
// identities = [allIdentities objectEnumerator];
|
|
||||||
// while (!organizerIdentity
|
|
||||||
// && ((currentIdentity = [identities nextObject])))
|
|
||||||
// if ([[currentIdentity objectForKey: @"email"]
|
|
||||||
// caseInsensitiveCompare: orgEmail]
|
|
||||||
// == NSOrderedSame)
|
|
||||||
// ASSIGN (organizerIdentity, currentIdentity);
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
|
|
||||||
// return organizerIdentity;
|
|
||||||
// }
|
|
||||||
|
|
||||||
//- (NSArray *) organizerList
|
|
||||||
//{
|
|
||||||
// return [[context activeUser] allIdentities];
|
|
||||||
//}
|
|
||||||
|
|
||||||
//- (NSString *) itemOrganizerText
|
|
||||||
//{
|
|
||||||
// return [item keysWithFormat: @"%{fullName} <%{email}>"];
|
|
||||||
//}
|
|
||||||
|
|
||||||
- (BOOL) hasAttendees
|
- (BOOL) hasAttendees
|
||||||
{
|
{
|
||||||
return ([[component attendees] count] > 0);
|
return ([[component attendees] count] > 0);
|
||||||
|
|
|
@ -85,7 +85,7 @@
|
||||||
<li><var:string label:value="Mark Folder Read" /></li>
|
<li><var:string label:value="Mark Folder Read" /></li>
|
||||||
<li><var:string label:value="New Subfolder..." /></li>
|
<li><var:string label:value="New Subfolder..." /></li>
|
||||||
<li><var:string label:value="Compact This Folder" /></li>
|
<li><var:string label:value="Compact This Folder" /></li>
|
||||||
<li><var:string label:value="Archive This Folder" /></li>
|
<li><var:string label:value="Export This Folder" /></li>
|
||||||
<li><var:string label:value="Empty Trash" /></li>
|
<li><var:string label:value="Empty Trash" /></li>
|
||||||
<li><!-- separator --></li>
|
<li><!-- separator --></li>
|
||||||
<li><var:string label:value="Search Messages..." /></li>
|
<li><var:string label:value="Search Messages..." /></li>
|
||||||
|
@ -103,7 +103,7 @@
|
||||||
<li><var:string label:value="New Subfolder..." /></li>
|
<li><var:string label:value="New Subfolder..." /></li>
|
||||||
<li><var:string label:value="Rename Folder..." /></li>
|
<li><var:string label:value="Rename Folder..." /></li>
|
||||||
<li><var:string label:value="Compact This Folder" /></li>
|
<li><var:string label:value="Compact This Folder" /></li>
|
||||||
<li><var:string label:value="Archive This Folder" /></li>
|
<li><var:string label:value="Export This Folder" /></li>
|
||||||
<li><var:string label:value="Delete Folder" /></li>
|
<li><var:string label:value="Delete Folder" /></li>
|
||||||
<li><var:string label:value="Use This Folder For" /></li>
|
<li><var:string label:value="Use This Folder For" /></li>
|
||||||
<li><!-- separator --></li>
|
<li><!-- separator --></li>
|
||||||
|
|
|
@ -623,7 +623,13 @@
|
||||||
date="vacationEndDate"
|
date="vacationEndDate"
|
||||||
const:dayStartHour="0"
|
const:dayStartHour="0"
|
||||||
const:dayEndHour="23"
|
const:dayEndHour="23"
|
||||||
/>
|
/><br/>
|
||||||
|
|
||||||
|
<label><input type="checkbox"
|
||||||
|
const:name="alwaysSend"
|
||||||
|
const:id="alwaysSend"
|
||||||
|
var:checked="alwaysSend" />
|
||||||
|
<var:string label:value="Always send vacation message response" /></label><br/>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -648,6 +654,9 @@
|
||||||
const:id="forwardKeepCopy"
|
const:id="forwardKeepCopy"
|
||||||
var:checked="forwardKeepCopy" />
|
var:checked="forwardKeepCopy" />
|
||||||
<var:string label:value="Keep a copy" /></label><br/>
|
<var:string label:value="Keep a copy" /></label><br/>
|
||||||
|
<script type="text/javascript">
|
||||||
|
var forwardConstraints = <var:string value="forwardConstraints" const:escapeHTML="NO"/>;
|
||||||
|
</script>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</var:if>
|
</var:if>
|
||||||
|
|
|
@ -218,7 +218,7 @@ Date.prototype.stringWithSeparator = function(separator) {
|
||||||
|
|
||||||
Date.prototype.addDays = function(nbrDays) {
|
Date.prototype.addDays = function(nbrDays) {
|
||||||
var milliSeconds = this.getTime();
|
var milliSeconds = this.getTime();
|
||||||
milliSeconds += 86400000 * nbrDays;
|
milliSeconds += 86400000 * Math.round(nbrDays);
|
||||||
this.setTime(milliSeconds);
|
this.setTime(milliSeconds);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -896,9 +896,8 @@ _freeBusyCacheEntry.prototype = {
|
||||||
if (this.entries.length > offset) {
|
if (this.entries.length > offset) {
|
||||||
var adjustedEd = ed.beginOfDay();
|
var adjustedEd = ed.beginOfDay();
|
||||||
var nbrDays = adjustedSd.deltaDays(adjustedEd) + 1;
|
var nbrDays = adjustedSd.deltaDays(adjustedEd) + 1;
|
||||||
var nbrQu = nbrDays * 96;
|
var offsetEnd = offset + (nbrDays * 96);
|
||||||
var offsetEnd = offset + nbrQu;
|
if (Math.round(this.entries.length/96) >= nbrDays) {
|
||||||
if (this.entries.length >= offsetEnd) {
|
|
||||||
entries = this.entries.slice(offset, offsetEnd);
|
entries = this.entries.slice(offset, offsetEnd);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -912,15 +911,19 @@ _freeBusyCacheEntry.prototype = {
|
||||||
|
|
||||||
var adjustedSd = sd.beginOfDay();
|
var adjustedSd = sd.beginOfDay();
|
||||||
var adjustedEd = ed.beginOfDay();
|
var adjustedEd = ed.beginOfDay();
|
||||||
var nbrDays = adjustedSd.deltaDays(adjustedEd) + 1;
|
var start = adjustedSd.clone();
|
||||||
|
start.addDays(-7);
|
||||||
|
var end = adjustedEd.clone();
|
||||||
|
end.addDays(7);
|
||||||
if (this.startDate) {
|
if (this.startDate) {
|
||||||
fetchDates = [];
|
fetchDates = [];
|
||||||
|
|
||||||
if (adjustedSd.getTime() < this.startDate.getTime()) {
|
if (adjustedSd.getTime() < this.startDate.getTime()) {
|
||||||
var start = adjustedSd.clone();
|
// Period extends to before current start
|
||||||
start.addDays(-7);
|
if (end.getTime() >= this.startDate.getTime()) {
|
||||||
var end = this.startDate.beginOfDay();
|
end = this.startDate.beginOfDay();
|
||||||
end.addDays(-1);
|
end.addDays(-1);
|
||||||
|
}
|
||||||
fetchDates.push({ start: start, end: end });
|
fetchDates.push({ start: start, end: end });
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -928,16 +931,16 @@ _freeBusyCacheEntry.prototype = {
|
||||||
var nextDate = this.startDate.clone();
|
var nextDate = this.startDate.clone();
|
||||||
nextDate.addDays(currentNbrDays);
|
nextDate.addDays(currentNbrDays);
|
||||||
if (adjustedEd.getTime() >= nextDate.getTime()) {
|
if (adjustedEd.getTime() >= nextDate.getTime()) {
|
||||||
var end = nextDate.clone();
|
// Period extends to after current end
|
||||||
end.addDays(7);
|
if (start.getTime() <= nextDate.getTime()) {
|
||||||
fetchDates.push({ start: nextDate, end: end });
|
start = nextDate.beginOfDay();
|
||||||
|
start.addDays(1);
|
||||||
|
}
|
||||||
|
fetchDates.push({ start: start, end: end });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
var start = adjustedSd.clone();
|
// Initial range
|
||||||
start.addDays(-7);
|
|
||||||
var end = adjustedEd.clone();
|
|
||||||
end.addDays(7);
|
|
||||||
fetchDates = [ { start: start, end: end } ];
|
fetchDates = [ { start: start, end: end } ];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -945,18 +948,27 @@ _freeBusyCacheEntry.prototype = {
|
||||||
},
|
},
|
||||||
|
|
||||||
integrateEntries: function fBCE_integrateEntries(entries, start, end) {
|
integrateEntries: function fBCE_integrateEntries(entries, start, end) {
|
||||||
|
var days, merged = false;
|
||||||
if (this.startDate) {
|
if (this.startDate) {
|
||||||
if (start.getTime() < this.startDate) {
|
if (start.getTime() < this.startDate) {
|
||||||
var days = start.deltaDays(this.startDate);
|
days = start.deltaDays(this.startDate);
|
||||||
if (entries.length == (days * 96)) {
|
if (entries.length == (days * 96)) {
|
||||||
|
// New period is just before previous period
|
||||||
this.startDate = start;
|
this.startDate = start;
|
||||||
this.entries = entries.concat(this.entries);
|
this.entries = entries.concat(this.entries);
|
||||||
|
merged = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
|
days = this.startDate.deltaDays(start);
|
||||||
|
if (Math.round(this.entries.length/96) == days) {
|
||||||
|
// New period is just after previous period
|
||||||
this.entries = this.entries.concat(entries);
|
this.entries = this.entries.concat(entries);
|
||||||
|
merged = true;
|
||||||
}
|
}
|
||||||
} else {
|
}
|
||||||
|
}
|
||||||
|
if (!merged) {
|
||||||
this.startDate = start;
|
this.startDate = start;
|
||||||
this.entries = entries;
|
this.entries = entries;
|
||||||
}
|
}
|
||||||
|
@ -1193,7 +1205,7 @@ function drawFbData(input, slots) {
|
||||||
else {
|
else {
|
||||||
log("inconsistency between freebusy results and"
|
log("inconsistency between freebusy results and"
|
||||||
+ " the number of cells");
|
+ " the number of cells");
|
||||||
log(" expecting: " + tds.length + " received: " + slots.length);
|
log(" expecting: " + (tds.length * 4) + " received: " + slots.length);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1674,10 +1686,10 @@ function initTimeWidgets(widgets) {
|
||||||
function onAdjustTime(event) {
|
function onAdjustTime(event) {
|
||||||
var endDate = window.getEndDate();
|
var endDate = window.getEndDate();
|
||||||
var startDate = window.getStartDate();
|
var startDate = window.getStartDate();
|
||||||
|
var delta = 0;
|
||||||
if (this.id.startsWith("start")) {
|
if (this.id.startsWith("start")) {
|
||||||
// Start date was changed
|
// Start date was changed
|
||||||
var delta = window.getShadowStartDate().valueOf() -
|
delta = window.getShadowStartDate().valueOf() - startDate.valueOf();
|
||||||
startDate.valueOf();
|
|
||||||
var newEndDate = new Date(endDate.valueOf() - delta);
|
var newEndDate = new Date(endDate.valueOf() - delta);
|
||||||
window.setEndDate(newEndDate);
|
window.setEndDate(newEndDate);
|
||||||
window.timeWidgets['end']['date'].updateShadowValue();
|
window.timeWidgets['end']['date'].updateShadowValue();
|
||||||
|
@ -1687,19 +1699,26 @@ function onAdjustTime(event) {
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
// End date was changed
|
// End date was changed
|
||||||
var delta = endDate.valueOf() - startDate.valueOf();
|
delta = window.getShadowEndDate().valueOf() - endDate.valueOf();
|
||||||
if (delta < 0) {
|
if (delta != 0) {
|
||||||
|
var startEndDelta = endDate.valueOf() - startDate.valueOf();
|
||||||
|
if (startEndDelta < 0) {
|
||||||
showAlertDialog(labels.validate_endbeforestart);
|
showAlertDialog(labels.validate_endbeforestart);
|
||||||
var oldEndDate = window.getShadowEndDate();
|
var oldEndDate = window.getShadowEndDate();
|
||||||
window.setEndDate(oldEndDate);
|
window.setEndDate(oldEndDate);
|
||||||
|
|
||||||
window.timeWidgets['end']['date'].updateShadowValue();
|
window.timeWidgets['end']['date'].updateShadowValue();
|
||||||
window.timeWidgets['end']['time'].updateShadowValue();
|
window.timeWidgets['end']['time'].updateShadowValue();
|
||||||
window.timeWidgets['end']['time'].onChange(); // method from SOGoTimePicker
|
window.timeWidgets['end']['time'].onChange(); // method from SOGoTimePicker
|
||||||
}
|
}
|
||||||
|
else {
|
||||||
|
window.timeWidgets['end']['date'].updateShadowValue();
|
||||||
|
window.timeWidgets['end']['time'].updateShadowValue();
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Specific function for the attendees editor
|
if ($("freeBusyHeader").getElementsByTagName("th").length == 0 || delta != 0)
|
||||||
|
// Update freebusy data
|
||||||
onTimeDateWidgetChange();
|
onTimeDateWidgetChange();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1739,6 +1758,7 @@ function getShadowEndDate() {
|
||||||
|
|
||||||
function _setDate(which, newDate) {
|
function _setDate(which, newDate) {
|
||||||
window.timeWidgets[which]['date'].setInputAsDate(newDate);
|
window.timeWidgets[which]['date'].setInputAsDate(newDate);
|
||||||
|
jQuery(window.timeWidgets[which]['date']).closest('.date').datepicker('update');
|
||||||
if (!isAllDay) {
|
if (!isAllDay) {
|
||||||
window.timeWidgets[which]['time'].value = newDate.getDisplayHoursString();
|
window.timeWidgets[which]['time'].value = newDate.getDisplayHoursString();
|
||||||
if (window.timeWidgets[which]['time'].onChange) window.timeWidgets[which]['time'].onChange(); // method from SOGoTimePicker
|
if (window.timeWidgets[which]['time'].onChange) window.timeWidgets[which]['time'].onChange(); // method from SOGoTimePicker
|
||||||
|
|
|
@ -14,7 +14,7 @@ var MailEditor = {
|
||||||
var autoSaveTimer;
|
var autoSaveTimer;
|
||||||
|
|
||||||
function refreshDraftsFolder() {
|
function refreshDraftsFolder() {
|
||||||
if (window.opener && window.opener.open && !window.opener.closed) {
|
if (window.opener && window.opener.getUnseenCountForFolder) {
|
||||||
var nodes = window.opener.$("mailboxTree").select("DIV[datatype=draft]");
|
var nodes = window.opener.$("mailboxTree").select("DIV[datatype=draft]");
|
||||||
window.opener.getUnseenCountForFolder(nodes[0].readAttribute("dataname"));
|
window.opener.getUnseenCountForFolder(nodes[0].readAttribute("dataname"));
|
||||||
}
|
}
|
||||||
|
|
|
@ -70,11 +70,42 @@ function savePreferences(sender) {
|
||||||
|
|
||||||
if ($("enableForward") && $("enableForward").checked) {
|
if ($("enableForward") && $("enableForward").checked) {
|
||||||
var addresses = $("forwardAddress").value.split(",");
|
var addresses = $("forwardAddress").value.split(",");
|
||||||
|
|
||||||
|
// We check if all addresses are valid
|
||||||
for (var i = 0; i < addresses.length && sendForm; i++)
|
for (var i = 0; i < addresses.length && sendForm; i++)
|
||||||
if (!emailRE.test(addresses[i].strip())) {
|
if (!emailRE.test(addresses[i].strip())) {
|
||||||
showAlertDialog(_("Please specify an address to which you want to forward your messages."));
|
showAlertDialog(_("Please specify an address to which you want to forward your messages."));
|
||||||
sendForm = false;
|
sendForm = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// We check if we can only to internal/external addresses.
|
||||||
|
var constraints = parseInt(forwardConstraints);
|
||||||
|
|
||||||
|
if (constraints > 0) {
|
||||||
|
// We first extract the list of 'known domains' to SOGo
|
||||||
|
var defaultAddresses = $("defaultEmailAddresses").value.split(/, */);
|
||||||
|
var domains = new Array();
|
||||||
|
|
||||||
|
defaultAddresses.each(function(adr) {
|
||||||
|
var domain = adr.split("@")[1];
|
||||||
|
if (domain) {
|
||||||
|
domains.push(domain.toLowerCase());
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// We check if we're allowed or not to forward based on the domain defaults
|
||||||
|
for (var i = 0; i < addresses.length && sendForm; i++) {
|
||||||
|
var domain = addresses[i].split("@")[1].toLowerCase();
|
||||||
|
if (domains.indexOf(domain) < 0 && constraints == 1) {
|
||||||
|
showAlertDialog(_("You are not allowed to forward your messages to an external email address."));
|
||||||
|
sendForm = false;
|
||||||
|
}
|
||||||
|
else if (domains.indexOf(domain) >= 0 && constraints == 2) {
|
||||||
|
showAlertDialog(_("You are not allowed to forward your messages to an internal email address."));
|
||||||
|
sendForm = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (typeof sieveCapabilities != "undefined") {
|
if (typeof sieveCapabilities != "undefined") {
|
||||||
|
|
Loading…
Reference in New Issue