Merge tag 'SOGo-2.2.17a' into jjgarcia/merge-from-upstream

SOGo 2.2.17a

Conflicts:
	NEWS
pull/65/head
Julio García 2015-04-10 13:12:46 +02:00
commit 321e16c382
70 changed files with 2541 additions and 389 deletions

View File

@ -1,6 +1,6 @@
/*
Copyright (c) 2014, Inverse inc.
Copyright (c) 2015, Inverse inc.
All rights reserved.
Redistribution and use in source and binary forms, with or without
@ -40,35 +40,38 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
@implementation NGMimeMessage (ActiveSync)
- (NSArray *) allRecipients
- (void) _addRecipients: (NSEnumerator *) enumerator
toArray: (NSMutableArray *) recipients
{
NSEnumerator *enumerator, *addressList;
NSMutableArray *recipients;
NGMailAddressParser *parser;
NSEnumerator *addressList;
NGMailAddress *address;
NSString *s;
while ((s = [enumerator nextObject]))
{
parser = [NGMailAddressParser mailAddressParserWithString: s];
addressList = [[parser parseAddressList] objectEnumerator];
while ((address = [addressList nextObject]))
[recipients addObject: [address address]];
}
}
- (NSArray *) allRecipients
{
NSMutableArray *recipients;
recipients = [NSMutableArray array];
enumerator = [[self headersForKey: @"to"] objectEnumerator];
while ((s = [enumerator nextObject]))
{
parser = [NGMailAddressParser mailAddressParserWithString: s];
addressList = [[parser parseAddressList] objectEnumerator];
while ((address = [addressList nextObject]))
[recipients addObject: [address address]];
}
[self _addRecipients: [[self headersForKey: @"to"] objectEnumerator]
toArray: recipients];
enumerator = [[self headersForKey: @"cc"] objectEnumerator];
while ((s = [enumerator nextObject]))
{
parser = [NGMailAddressParser mailAddressParserWithString: s];
addressList = [[parser parseAddressList] objectEnumerator];
while ((address = [addressList nextObject]))
[recipients addObject: [address address]];
}
[self _addRecipients: [[self headersForKey: @"cc"] objectEnumerator]
toArray: recipients];
[self _addRecipients: [[self headersForKey: @"bcc"] objectEnumerator]
toArray: recipients];
return recipients;
}

View File

@ -49,6 +49,8 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- (NSString *) deviceType;
- (NSString *) attachmentName;
- (NSString *) command;
- (NSString *) collectionid;
- (NSString *) itemid;
- (NSData *) convertHexStringToBytes;
@end

View File

@ -298,6 +298,31 @@ static NSArray *easCommandParameters = nil;
return s;
}
- (NSString *) itemid
{
NSString *s;
s = [self _valueForParameter: @"ITEMID="];
if (!s)
s = @"Unknown";
return s;
}
- (NSString *) collectionid
{
NSString *s;
s = [self _valueForParameter: @"COLLECTIONID="];
if (!s)
s = @"Unknown";
return s;
}
//
// FIXME: combine with our OpenChange code.
//

View File

@ -540,6 +540,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
{
NSMutableDictionary *folderMetadata, *dateCache, *syncCache;
NSString *davCollectionTagToStore;
NSAutoreleasePool *pool;
NSMutableString *s;
@ -618,6 +619,8 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
more_available = NO;
davCollectionTagToStore = [theCollection davCollectionTag];
switch (theFolderType)
{
// Handle all the GCS components
@ -788,7 +791,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
else
{
[folderMetadata removeObjectForKey: @"MoreAvailable"];
[folderMetadata setObject: [theCollection davCollectionTag] forKey: @"SyncKey"];
[folderMetadata setObject: davCollectionTagToStore forKey: @"SyncKey"];
}
[self _setFolderMetadata: folderMetadata
@ -963,7 +966,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
else
{
[folderMetadata removeObjectForKey: @"MoreAvailable"];
[folderMetadata setObject: [theCollection davCollectionTag] forKey: @"SyncKey"];
[folderMetadata setObject: davCollectionTagToStore forKey: @"SyncKey"];
}
[self _setFolderMetadata: folderMetadata forKey: [self _getNameInCache: theCollection withType: theFolderType]];
@ -1211,7 +1214,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
{
// Make sure that client is updated with the right syncKey. - This keeps vtodo's and vevent's syncKey in sync.
syncKeyInCache = [folderMetadata objectForKey: @"SyncKey"];
if (syncKeyInCache && !([davCollectionTag isEqualToString:syncKeyInCache]))
if (syncKeyInCache && !([davCollectionTag isEqualToString:syncKeyInCache]) && ![davCollectionTag isEqualToString: @"-1"])
{
davCollectionTag = syncKeyInCache;
*changeDetected = YES;
@ -1431,8 +1434,9 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
}
}
// Only send a response if there are changes otherwise send an empty response.
if (changeDetected)
// Only send a response if there are changes or MS-ASProtocolVersion is either 2.5 or 12.0 oterwise send an empty response.
if (changeDetected || [[[context request] headerForKey: @"MS-ASProtocolVersion"] isEqualToString: @"2.5"] || [[[context request] headerForKey: @"MS-ASProtocolVersion"] isEqualToString: @"12.0"])
{
// We always return the last generated response.
// If we only return <Sync><Collections/></Sync>,

View File

@ -1127,6 +1127,8 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
id currentCollection;
NSMutableString *s;
NSData *d;
NSArray *allCollections;
int j;
SOGoMicrosoftActiveSyncFolderType folderType;
int status, count;
@ -1135,66 +1137,72 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
status = 1;
count = 0;
collectionId = [[(id)[theDocumentElement getElementsByTagName: @"CollectionId"] lastObject] textValue];
realCollectionId = [collectionId realCollectionIdWithFolderType: &folderType];
if (folderType == ActiveSyncMailFolder)
nameInCache = [NSString stringWithFormat: @"folder%@", realCollectionId];
else
nameInCache = collectionId;
realCollectionId = [self globallyUniqueIDToIMAPFolderName: realCollectionId type: folderType];
currentCollection = [self collectionFromId: realCollectionId type: folderType];
//
// For IMAP, we simply build a request like this:
//
// . UID SORT (SUBJECT) UTF-8 SINCE 1-Jan-2014 NOT DELETED
// * SORT 124576 124577 124579 124578
// . OK Completed (4 msgs in 0.000 secs)
//
if (folderType == ActiveSyncMailFolder)
{
EOQualifier *notDeletedQualifier, *sinceDateQualifier;
EOAndQualifier *qualifier;
NSCalendarDate *filter;
NSArray *uids;
filter = [NSCalendarDate dateFromFilterType: [[(id)[theDocumentElement getElementsByTagName: @"FilterType"] lastObject] textValue]];
notDeletedQualifier = [EOQualifier qualifierWithQualifierFormat:
@"(not (flags = %@))",
@"deleted"];
sinceDateQualifier = [EOQualifier qualifierWithQualifierFormat:
@"(DATE >= %@)", filter];
qualifier = [[EOAndQualifier alloc] initWithQualifiers: notDeletedQualifier, sinceDateQualifier,
nil];
AUTORELEASE(qualifier);
uids = [currentCollection fetchUIDsMatchingQualifier: qualifier
sortOrdering: @"REVERSE ARRIVAL"
threaded: NO];
count = [uids count];
// Add the number of UIDs expected to "soft delete"
count += [self _softDeleteCountWithFilter: filter collectionId: nameInCache];
}
else
{
count = [[currentCollection toOneRelationshipKeys] count];
}
[s appendString: @"<?xml version=\"1.0\" encoding=\"utf-8\"?>"];
[s appendString: @"<!DOCTYPE ActiveSync PUBLIC \"-//MICROSOFT//DTD ActiveSync//EN\" \"http://www.microsoft.com/\">"];
[s appendFormat: @"<GetItemEstimate xmlns=\"GetItemEstimate:\"><Response><Status>%d</Status><Collection>", status];
[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];
if (folderType == ActiveSyncMailFolder)
nameInCache = [NSString stringWithFormat: @"folder%@", realCollectionId];
else
nameInCache = collectionId;
realCollectionId = [self globallyUniqueIDToIMAPFolderName: realCollectionId type: folderType];
currentCollection = [self collectionFromId: realCollectionId type: folderType];
[s appendFormat: @"<CollectionId>%@</CollectionId>", collectionId];
[s appendFormat: @"<Estimate>%d</Estimate>", count];
//
// For IMAP, we simply build a request like this:
//
// . UID SORT (SUBJECT) UTF-8 SINCE 1-Jan-2014 NOT DELETED
// * SORT 124576 124577 124579 124578
// . OK Completed (4 msgs in 0.000 secs)
//
if (folderType == ActiveSyncMailFolder)
{
NSCalendarDate *filter;
NSString *syncKey;
NSArray *allMessages;
filter = [NSCalendarDate dateFromFilterType: [[(id)[[allCollections objectAtIndex: j] getElementsByTagName: @"FilterType"] lastObject] textValue]];
syncKey = [[(id)[[allCollections objectAtIndex: j] getElementsByTagName: @"SyncKey"] lastObject] textValue];
allMessages = [currentCollection syncTokenFieldsWithProperties: nil matchingSyncToken: syncKey fromDate: filter];
count = [allMessages count];
// Add the number of UIDs expected to "soft delete"
count += [self _softDeleteCountWithFilter: filter collectionId: nameInCache];
}
else
{
count = [[currentCollection toOneRelationshipKeys] count];
}
[s appendString: @"<Response>"];
[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: @"<Estimate>%d</Estimate></Collection></Response>", count];
}
[s appendString: @"</Collection></Response></GetItemEstimate>"];
[s appendString: @"</GetItemEstimate>"];
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: @"<!DOCTYPE ActiveSync PUBLIC \"-//MICROSOFT//DTD ActiveSync//EN\" \"http://www.microsoft.com/\">"];
[s appendString: @"<Provision xmlns=\"Provision:\">"];
[s appendString: @"<AllowHTMLEmail>1</AllowHTMLEmail>"];
[s appendString: @"</Provision>"];
d = [[s dataUsingEncoding: NSUTF8StringEncoding] xml2wbxml];
@ -2254,7 +2263,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
error = [self _sendMail: data
recipients: [message allRecipients]
saveInSentItems: ([(id)[theDocumentElement getElementsByTagName: @"SaveInSentItems"] count] ? YES : NO)];
saveInSentItems: ([(id)[theDocumentElement getElementsByTagName: @"SaveInSentItems"] count] ? YES : NO)];
if (error)
{
@ -2345,7 +2354,21 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
id value;
folderId = [[(id)[theDocumentElement getElementsByTagName: @"FolderId"] lastObject] textValue];
// if folderId is not there try to get it from URL
if (!folderId)
{
folderId = [[[context request] uri] collectionid];
}
itemId = [[(id)[theDocumentElement getElementsByTagName: @"ItemId"] lastObject] textValue];
// if itemId is not there try to get it from URL
if (!itemId)
{
itemId = [[[context request] uri] itemid];
}
realCollectionId = [folderId realCollectionIdWithFolderType: &folderType];
realCollectionId = [self globallyUniqueIDToIMAPFolderName: realCollectionId type: folderType];
@ -2543,7 +2566,9 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
// If the MS-ASProtocolVersion header is set to "12.1", the body of the SendMail request is
// is a "message/rfc822" payload - otherwise, it's a WBXML blob.
//
if ([cmdName caseInsensitiveCompare: @"SendMail"] == NSOrderedSame &&
if (([cmdName caseInsensitiveCompare: @"SendMail"] == NSOrderedSame ||
[cmdName caseInsensitiveCompare: @"SmartReply"] == NSOrderedSame ||
[cmdName caseInsensitiveCompare: @"SmartForward"] == NSOrderedSame) &&
[[theRequest headerForKey: @"content-type"] caseInsensitiveCompare: @"message/rfc822"] == NSOrderedSame)
{
NSString *s, *xml;
@ -2561,7 +2586,9 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
s = [theRequest contentAsString];
}
xml = [NSString stringWithFormat: @"<?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];
}

View File

@ -47,6 +47,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#import <NGExtensions/NSString+Encoding.h>
#import <NGImap4/NGImap4Envelope.h>
#import <NGImap4/NGImap4EnvelopeAddress.h>
#import <NGImap4/NSString+Imap4.h>
#import <NGObjWeb/WOContext+SoObjects.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/NGMimeMessageGenerator.h>
#import <Mailer/SOGoMailLabel.h>
#import <SOGo/SOGoUserDefaults.h>
#include "iCalTimeZone+ActiveSync.h"
#include "NSData+ActiveSync.h"
#include "NSDate+ActiveSync.h"
@ -204,6 +209,7 @@ struct GlobalObjectId {
//
//
- (NSData *) _preferredBodyDataInMultipartUsingType: (int) theType
nativeTypeFound: (int *) theNativeTypeFound
{
NSString *encoding, *key, *plainKey, *htmlKey, *type, *subtype;
NSDictionary *textParts, *part;
@ -222,6 +228,10 @@ struct GlobalObjectId {
type = [part valueForKey: @"type"];
subtype = [part valueForKey: @"subtype"];
// Don't select an attachment as body
if ([[[part valueForKey: @"disposition"] valueForKey: @"type"] isEqualToString: @"attachment"])
continue;
if ([type isEqualToString: @"text"] && [subtype isEqualToString: @"html"])
htmlKey = key;
else if ([type isEqualToString: @"text"] && [subtype isEqualToString: @"plain"])
@ -229,11 +239,22 @@ struct GlobalObjectId {
}
key = nil;
*theNativeTypeFound = 1;
if (theType == 2)
key = htmlKey;
else if (theType == 1)
if (theType == 2 && htmlKey)
{
key = htmlKey;
*theNativeTypeFound = 2;
}
else if (theType == 1 && plainKey)
key = plainKey;
else if (theType == 2 && plainKey)
key = plainKey;
else if (theType == 1 && htmlKey)
{
key = htmlKey;
*theNativeTypeFound = 2;
}
if (key)
{
@ -251,9 +272,17 @@ struct GlobalObjectId {
charset = [[[self lookupInfoForBodyPart: key] objectForKey: @"parameterList"] objectForKey: @"charset"];
if (![charset length])
charset = @"us-ascii";
charset = @"utf-8";
s = [NSString stringWithData: d usingEncodingNamed: charset];
// We fallback to ISO-8859-1 string encoding
if (!s)
s = [[[NSString alloc] initWithData: d encoding: NSISOLatin1StringEncoding] autorelease];
if (theType == 1 && *theNativeTypeFound == 2)
s = [s htmlToText];
d = [s dataUsingEncoding: NSUTF8StringEncoding];
}
@ -298,7 +327,7 @@ struct GlobalObjectId {
[[[thePart contentType] type] isEqualToString: @"text"] &&
([[[thePart contentType] subType] isEqualToString: @"plain"] || [[[thePart contentType] subType] isEqualToString: @"html"]))
{
// We make sure everything is encoded in UTF-8
// We make sure everything is encoded in UTF-8.
NGMimeType *mimeType;
NSString *s;
@ -309,9 +338,13 @@ struct GlobalObjectId {
charset = [[thePart contentType] valueOfParameter: @"charset"];
if (![charset length])
charset = @"us-ascii";
charset = @"utf-8";
s = [NSString stringWithData: body usingEncodingNamed: charset];
s = [NSString stringWithData: body usingEncodingNamed: charset];
// We fallback to ISO-8859-1 string encoding. We avoid #3103.
if (!s)
s = [[[NSString alloc] initWithData: body encoding: NSISOLatin1StringEncoding] autorelease];
}
else
{
@ -414,7 +447,7 @@ struct GlobalObjectId {
charset = [[[self lookupInfoForBodyPart: @""] objectForKey: @"parameterList"] objectForKey: @"charset"];
if (![charset length])
charset = @"us-ascii";
charset = @"utf-8";
d = [[self fetchPlainTextParts] objectForKey: @""];
@ -428,25 +461,30 @@ struct GlobalObjectId {
d = [d dataByDecodingQuotedPrintableTransferEncoding];
s = [NSString stringWithData: d usingEncodingNamed: charset];
// We fallback to ISO-8859-1 string encoding. We avoid #3103.
if (!s)
s = [[[NSString alloc] initWithData: d encoding: NSISOLatin1StringEncoding] autorelease];
// Check if we must convert html->plain
if (theType == 1 && [subtype isEqualToString: @"html"])
{
s = [s htmlToText];
}
d = [s dataUsingEncoding: NSUTF8StringEncoding];
}
else if ([type isEqualToString: @"multipart"])
{
d = [self _preferredBodyDataInMultipartUsingType: theType];
d = [self _preferredBodyDataInMultipartUsingType: theType nativeTypeFound: theNativeType];
}
}
else if (theType == 4)
{
// We sanitize the content *ONLY* for Outlook clients. Outlook has strange issues
// We sanitize the content *ONLY* for Outlook clients and if the content-transfer-encoding is 8bit. Outlook has strange issues
// with quoted-printable/base64 encoded text parts. It just doesn't decode them.
if ([[context objectForKey: @"DeviceType"] isEqualToString: @"WindowsOutlook15"])
encoding = [[self lookupInfoForBodyPart: @""] objectForKey: @"encoding"];
if ([[context objectForKey: @"DeviceType"] isEqualToString: @"WindowsOutlook15"] || ([encoding caseInsensitiveCompare: @"8bit"] == NSOrderedSame))
d = [self _sanitizedMIMEMessage];
else
d = [self content];
@ -512,6 +550,10 @@ struct GlobalObjectId {
NSData *d, *globalObjId;
NSArray *attachmentKeys;
NSMutableString *s;
uint32_t v;
NSString *p;
id value;
iCalCalendar *calendar;
@ -552,8 +594,25 @@ struct GlobalObjectId {
if (value)
[s appendFormat: @"<Cc xmlns=\"Email:\">%@</Cc>", [value activeSyncRepresentationInContext: context]];
// Importance - FIXME
[s appendFormat: @"<Importance xmlns=\"Email:\">%@</Importance>", @"1"];
// Importance
v = 0x1;
p = [[self mailHeaders] objectForKey: @"x-priority"];
if (p)
{
if ([p hasPrefix: @"1"]) v = 0x2;
else if ([p hasPrefix: @"2"]) v = 0x2;
else if ([p hasPrefix: @"4"]) v = 0x0;
else if ([p hasPrefix: @"5"]) v = 0x0;
}
else
{
p = [[self mailHeaders] objectForKey: @"importance"];
if ([p hasPrefix: @"High"]) v = 0x2;
else if ([p hasPrefix: @"Low"]) v = 0x0;
}
[s appendFormat: @"<Importance xmlns=\"Email:\">%d</Importance>", v];
// Read
[s appendFormat: @"<Read xmlns=\"Email:\">%d</Read>", ([self read] ? 1 : 0)];
@ -728,17 +787,31 @@ struct GlobalObjectId {
len = [content length];
[s appendString: @"<Body xmlns=\"AirSyncBase:\">"];
[s appendFormat: @"<Type>%d</Type>", preferredBodyType];
[s appendFormat: @"<Truncated>%d</Truncated>", truncated];
[s appendFormat: @"<Preview></Preview>"];
if (!truncated)
if ([[[context request] headerForKey: @"MS-ASProtocolVersion"] isEqualToString: @"2.5"])
{
[s appendFormat: @"<Data>%@</Data>", content];
[s appendFormat: @"<EstimatedDataSize>%d</EstimatedDataSize>", len];
[s appendFormat: @"<Body xmlns=\"Email:\">%@</Body>", content];
[s appendFormat: @"<BodyTruncated xmlns=\"Email:\">%d</BodyTruncated>", truncated];
}
[s appendString: @"</Body>"];
else
{
[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: @"<Truncated>%d</Truncated>", truncated];
[s appendFormat: @"<Preview></Preview>"];
if (!truncated)
{
[s appendFormat: @"<Data>%@</Data>", content];
[s appendFormat: @"<EstimatedDataSize>%d</EstimatedDataSize>", len];
}
[s appendString: @"</Body>"];
}
}
// Attachments -namespace 16
@ -746,9 +819,12 @@ struct GlobalObjectId {
if ([attachmentKeys count])
{
int i;
[s appendString: @"<Attachments xmlns=\"AirSyncBase:\">"];
if ([[[context request] headerForKey: @"MS-ASProtocolVersion"] isEqualToString: @"2.5"])
[s appendString: @"<Attachments xmlns=\"Email:\">"];
else
[s appendString: @"<Attachments xmlns=\"AirSyncBase:\">"];
for (i = 0; i < [attachmentKeys count]; i++)
{
value = [attachmentKeys objectAtIndex: i];
@ -759,11 +835,22 @@ struct GlobalObjectId {
// FileReference must be a unique identifier across the whole store. We use the following structure:
// mail/<foldername>/<message UID/<pathofpart>
// mail/INBOX/2
[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: @"<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: @"<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: @"<IsInline>%d</IsInline>", 1];
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: @"<EstimatedDataSize>%d</EstimatedDataSize>", [[value objectForKey: @"size"] intValue]];
//[s appendFormat: @"<IsInline>%d</IsInline>", 1];
}
[s appendString: @"</Attachment>"];
}
@ -774,6 +861,27 @@ struct GlobalObjectId {
[s appendString: @"<Flag xmlns=\"Email:\">"];
[s appendFormat: @"<FlagStatus>%d</FlagStatus>", ([self flagged] ? 2 : 0)];
[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
//[s appendString: @"<ConversationId xmlns=\"Email2:\">foobar</ConversationId>"];
@ -836,6 +944,65 @@ struct GlobalObjectId {
else
[self removeFlags: @"seen"];;
}
if ((o = [theValues objectForKey: @"Categories"]))
{
NSEnumerator *categories;
NSString *currentFlag;
NSDictionary *v;
v = [[[context activeUser] userDefaults] mailLabelsColors];
// add categories/labels sent from client
if ([o isKindOfClass: [NSArray class]])
{
NSEnumerator *enumerator;
NSMutableArray *labels;
NSEnumerator *flags;
id key;
labels = [NSMutableArray array];
enumerator = [v keyEnumerator];
flags = [o objectEnumerator];
while ((currentFlag = [flags nextObject]))
{
while ((key = [enumerator nextObject]))
{
if (([currentFlag isEqualToString:[[v objectForKey:key] objectAtIndex:0]]))
{
[labels addObject: key];
break;
}
}
}
[self addFlags: [labels componentsJoinedByString: @" "]];
}
categories = [[[self fetchCoreInfos] objectForKey: @"flags"] objectEnumerator];
// remove all categories/labels from server which were not it the list sent from client
if (categories)
{
while ((currentFlag = [categories nextObject]))
{
// only deal with lables and don't touch flags like seen and flagged
if (([v objectForKey: currentFlag]))
{
if (![o isKindOfClass: [NSArray class]])
{
[self removeFlags: currentFlag];
}
else if (([o indexOfObject: [[v objectForKey:currentFlag] objectAtIndex:0]] == NSNotFound))
{
[self removeFlags: currentFlag];
}
}
}
}
}
}
@end

View File

@ -40,6 +40,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#import <NGExtensions/NSString+misc.h>
#import <NGObjWeb/WOContext.h>
#import <NGObjWeb/WOContext+SoObjects.h>
#import <NGObjWeb/WORequest.h>
#import <NGCards/iCalCalendar.h>
#import <NGCards/iCalDateTime.h>
@ -274,11 +275,20 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
// 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.
o = [o activeSyncRepresentationInContext: context];
[s appendString: @"<Body xmlns=\"AirSyncBase:\">"];
[s appendFormat: @"<Type>%d</Type>", 1];
[s appendFormat: @"<EstimatedDataSize>%d</EstimatedDataSize>", [o length]];
[s appendFormat: @"<Data>%@</Data>", o];
[s appendString: @"</Body>"];
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 appendFormat: @"<Type>%d</Type>", 1];
[s appendFormat: @"<EstimatedDataSize>%d</EstimatedDataSize>", [o length]];
[s appendFormat: @"<Data>%@</Data>", o];
[s appendString: @"</Body>"];
}
}
[s appendFormat: @"<NativeBodyType xmlns=\"AirSyncBase:\">%d</NativeBodyType>", 1];
@ -394,9 +404,17 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
}
// FIXME: merge with iCalToDo
if ((o = [[theValues objectForKey: @"Body"] objectForKey: @"Data"]))
[self setComment: o];
if ([[[context request] headerForKey: @"MS-ASProtocolVersion"] isEqualToString: @"2.5"])
{
if ((o = [theValues objectForKey: @"Body"]))
[self setComment: o];
}
else
{
if ((o = [[theValues objectForKey: @"Body"] objectForKey: @"Data"]))
[self setComment: o];
}
if ((o = [theValues objectForKey: @"Location"]))
[self setLocation: o];

View File

@ -38,6 +38,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#import <NGExtensions/NSString+misc.h>
#import <NGObjWeb/WOContext.h>
#import <NGObjWeb/WOContext+SoObjects.h>
#import <NGObjWeb/WORequest.h>
#import <SOGo/SOGoUser.h>
#import <SOGo/SOGoUserDefaults.h>
@ -49,6 +50,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#include "NSDate+ActiveSync.h"
#include "NSString+ActiveSync.h"
@implementation iCalToDo (ActiveSync)
- (NSString *) activeSyncRepresentationInContext: (WOContext *) context
@ -62,11 +64,10 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
s = [NSMutableString string];
// Complete
o = [self completed];
[s appendFormat: @"<Complete xmlns=\"Tasks:\">%d</Complete>", (o ? 1 : 0)];
[s appendFormat: @"<Complete xmlns=\"Tasks:\">%d</Complete>", [[self status] isEqualToString: @"COMPLETED"] ? 1 : 0];
// DateCompleted
if (o)
if ((o = [self completed]))
[s appendFormat: @"<DateCompleted xmlns=\"Tasks:\">%@</DateCompleted>", [o activeSyncRepresentationInContext: context]];
// Start date
@ -97,6 +98,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
// Reminder - FIXME
[s appendFormat: @"<ReminderSet xmlns=\"Tasks:\">%d</ReminderSet>", 0];
// Sensitivity
if ([[self accessClass] isEqualToString: @"PRIVATE"])
v = 2;
else if ([[self accessClass] isEqualToString: @"CONFIDENTIAL"])
@ -104,6 +106,8 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
else
v = 0;
[s appendFormat: @"<Sensitivity xmlns=\"Tasks:\">%d</Sensitivity>", v];
categories = [self categories];
if ([categories count])
@ -126,11 +130,20 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
// It is very important here to NOT set <Truncated>0</Truncated> in the response,
// otherwise it'll prevent WP8 phones from sync'ing. See #3028 for details.
o = [o activeSyncRepresentationInContext: context];
[s appendString: @"<Body xmlns=\"AirSyncBase:\">"];
[s appendFormat: @"<Type>%d</Type>", 1];
[s appendFormat: @"<EstimatedDataSize>%d</EstimatedDataSize>", [o length]];
[s appendFormat: @"<Data>%@</Data>", o];
[s appendString: @"</Body>"];
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 appendFormat: @"<Type>%d</Type>", 1];
[s appendFormat: @"<EstimatedDataSize>%d</EstimatedDataSize>", [o length]];
[s appendFormat: @"<Data>%@</Data>", o];
[s appendString: @"</Body>"];
}
}
return s;
@ -152,9 +165,16 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
[self setSummary: o];
// FIXME: merge with iCalEvent
if ((o = [[theValues objectForKey: @"Body"] objectForKey: @"Data"]))
[self setComment: o];
if ([[[context request] headerForKey: @"MS-ASProtocolVersion"] isEqualToString: @"2.5"])
{
if ((o = [theValues objectForKey: @"Body"]))
[self setComment: o];
}
else
{
if ((o = [[theValues objectForKey: @"Body"] objectForKey: @"Data"]))
[self setComment: o];
}
if ([[theValues objectForKey: @"Complete"] intValue] &&
((o = [theValues objectForKey: @"DateCompleted"])) )
@ -166,6 +186,11 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
[completed setDate: o];
[self setStatus: @"COMPLETED"];
}
else
{
[self setStatus: @"IN-PROCESS"];
[self setCompleted: nil];
}
if ((o = [theValues objectForKey: @"DueDate"]))
{
@ -177,6 +202,16 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
[due setDateTime: o];
}
if ((o = [theValues objectForKey: @"StartDate"]))
{
iCalDateTime *due;
o = [o calendarDate];
due = (iCalDateTime *) [self uniqueChildWithTag: @"dtstart"];
[due setTimeZone: tz];
[due setDateTime: o];
}
// 2 == high, 1 == normal, 0 == low
if ((o = [theValues objectForKey: @"Importance"]))
{

589
ChangeLog
View File

@ -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
Author: Francis Lachapelle <flachapelle@inverse.ca>
Date: Fri Jan 30 11:03:38 2015 -0500

View File

@ -1003,6 +1003,14 @@ this feature, every LDAP requests will fail. Note that some LDAP servers
require LDAP/SSL for password policies to work. This is the case for
example with 389 Directory Server.
|updateSambaNTLMPasswords
|If set to `YES`, SOGo will automatically update the sambaNTPassword
and sambaLMPassword attributes when changing passwords. The attributes
must be called sambaNTPassword and sambaLMPassword. You must also make
sure the correct ACL is set in your LDAP server to allow users to change
their own sambaNTPassword and sambaLMPassword password attributes.
Defaults to `NO` when unset.
|isAddressBook
|If set to `YES`, this LDAP source is used as a shared address book
(with read-only access). Note that if set to `NO`, autocompletion will
@ -1040,6 +1048,11 @@ of supported attributes.
user addressbooks (see _abOU_ below), this list of object classes will
be applied to new records as they are created.
|GroupObjectClasses
|A list (array) of names identifying groups within the LDAP source. If not
set, SOGo will use `group`, `groupofnames`, `groupofuniquenames`
and `posixgroup`.
|modifiers
|A list (array) of usernames that are authorized to perform
modifications to the address book defined by this LDAP source.
@ -1329,27 +1342,27 @@ The following table describes the parameters that were set:
[cols="3,47,50a"]
|=======================================================================
|D |SOGoProfileURL
|S |SOGoProfileURL
|Parameter used to set the database URL so that SOGo can retrieve user
profiles.
For MySQL, set the database URL to something like:
`mysql://sogo:sogo@localhost:3306/sogo/sogo_user_profile`.
|D |OCSFolderInfoURL
|S |OCSFolderInfoURL
|Parameter used to set the database URL so that SOGo can retrieve the
location of user folders (address books and calendars).
For Oracle, set the database URL to something like:
`oracle://sogo:sogo@localhost:1526/sogo/sogo_folder_info`.
|D |OCSSessionsFolderURL
|S |OCSSessionsFolderURL
|Parameter used to set the database URL so that SOGo can store and
retrieve secured user sessions information. For PostgreSQL, the database
URL could be set to something like:
`postgresql://sogo:sogo@localhost:5432/sogo/sogo_sessions_folder`.
|D |OCSEMailAlarmsFolderURL
|S |OCSEMailAlarmsFolderURL
|Parameter used to set the database URL for email-based alarms (that can
be set on events and tasks). This parameter is relevant only if
_SOGoEnableEMailAlarms_ is set to `YES`. For PostgreSQL, the database
@ -1681,6 +1694,13 @@ cronjob `sogo-tmpwatch`.
Defaults to `/var/spool/sogo`.
|S |NGMimeBuildMimeTempDirectory
|Parameter used to set the path where temporary files will be stored
by SOPE when dealing with MIME messages.
Defaults to `/tmp`.
|S |NGImap4DisableIMAP4Pooling
|Disables IMAP pooling when set to `YES`. Enable pooling by setting to
`NO` or using a caching proxy like imapproxy.
@ -1784,6 +1804,12 @@ host.
Defaults to `NO` when unset.
|D |SOGoForwardConstraints
|Parameter used to set constraints on possible addresses used when
automatically forwarding mails. When set to `0` (default), no constraint
is enforced. When set to `1`, only internal domains can be used. When
set to `2`, only external domains can be used.
|D |SOGoSieveScriptsEnabled
|Parameter used to activate the edition from the preferences windows of
server-side mail filters. Requires Sieve script support on the IMAP
@ -2346,8 +2372,8 @@ easier. These GUI utilities can make use of templates to create and
pre-configure typical user accounts or any standardized LDAP record,
along with the correct object classes, fields and default values.
Microsoft ActiveSync
--------------------
Microsoft Enterprise ActiveSync
-------------------------------
SOGo supports the Microsoft ActiveSync protocol.
@ -2421,6 +2447,10 @@ Please be aware of the following limitations:
solution is to configure Outlook to use a LDAP server (over SSL) with
authentication. Outlook 2013 also does not seem to support multiple
address books over ActiveSync.
* To successfully synchronize Outlook email categories, a corresponding
mail label (Preferences->Mail Options) has to be created manually in SOGo
for each label defined in Outlook. The name in SOGo and in Outlook must be
identical.
* Make sure you do not use a self-signed certificate. While this will
work, Outlook will work intermittently as it will raise popups for
certificate validation, sometimes in background, preventing the user to
@ -2457,6 +2487,83 @@ and send an email to iplicreq@microsoft.com
Inverse inc. provides this software for free, but is not responsible for
anything related to its usage.
Microsoft Enterprise ActiveSync Tuning
--------------------------------------
First of all, it is important to know that most EAS devices will keep
HTTP connections open to SOGo (and thus, Apache) for a long time. This
is required for "push" to work properly. Connections can stay open for
up to one hour, or 3600 seconds.
The first parameter to check is related to Apache's proxying to
SOGo:
----
ProxyPass /Microsoft-Server-ActiveSync \
http://127.0.0.1:20000/SOGo/Microsoft-Server-ActiveSync \
retry=60 connectiontimeout=5 timeout=360
----
The above line sets a timeout for up to 360 seconds, or 6 minutes. If
you want to let EAS clients keep their HTTP connections open for up
to an hour, you must change the timeout parameter and set it to 3600.
If you change this value, the WOWatchDogRequestTimeout parameter must be changed
accordingly in SOGo's configuration file (/etc/sogo/sogo.conf). By default,
a SOGo child process is allowed to handle a request that can take up
to 10 minutes before it gets killed by its parent process. When using
EAS "push", the client expects to keep its connection open for up to one
hour - so the WOWatchDogRequestTimeout, which is set in minutes,
must be adjusted accordingly.
EAS clients will keep HTTP connections open for a long time
during these two EAS commands: Ping and Sync. By default, SOGo will prevent
EAS clients from keeping connections for a long time. This is to avoid the
situation where all SOGo child processes would be monopolized by EAS clients -
rendering the SOGo web interface or DAV interface unavailable. The
default SOGo behavior is thus similar to disable EAS push entirely.
Two SOGo configuration parameters are available to modify this behavior:
SOGoMaximumPingInterval (set by default to 10 seconds) and
SOGoMaximumSyncInterval (set by default to 30 seconds). If you want
connection to stay open for up to one hour, you should set these
slightly under 3600 seconds (say 3540 - or 59 minutes). During a
long-lived HTTP connection, the SOGo child process will perform
internal polling to detect changes and return them to the EAS client
if any changes are found. The parameter used to control this
is SOGoInternalSyncInterval. By default, polling is done every 10
seconds. This might generate too much load on large-scale system.
The last configuration parameter to adjust is WOWorkersCount - which sets the
number of SOGo child process that will be used to handle requests.
You should have at least one child per EAS device configured to use
"push". You must also have more children than you have EAS devices
configured to use "push" - in order to handle normal SOGo requests to
its Web or DAV interfaces.
Here are some usage examples for EAS devices using "push". In all
cases, the Apache timeout is set to 3600 and the
WOWatchDogRequestTimeout parameter is set to 60.
Example 1 - 100 users, 10 EAS devices:
----
WOWorkersCount = 15;
SOGoMaximumPingInterval = 3540;
SOGoMaximumSyncInterval = 3540;
SOGoInternalSyncInterval = 30;
----
Example 2 - 1000 users, 100 EAS devices:
----
WOWorkersCount = 120;
SOGoMaximumPingInterval = 3540;
SOGoMaximumSyncInterval = 3540;
SOGoInternalSyncInterval = 60;
----
Using SOGo
----------

View File

@ -1,7 +1,7 @@
<!-- TODO have the build system take care of this -->
<releaseinfo>Version 2.2.15 - January 2015</releaseinfo>
<subtitle>for version 2.2.15</subtitle>
<date>2015-01-30</date>
<releaseinfo>Version 2.2.17a - March 2015</releaseinfo>
<subtitle>for version 2.2.17a</subtitle>
<date>2015-03-26</date>
<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>

View File

@ -13,6 +13,6 @@
// TODO have the build system take care of this
:release_version: 2.2.15
:release_version: 2.2.17a
// vim: set syntax=asciidoc tabstop=2 shiftwidth=2 expandtab:

59
NEWS
View File

@ -20,6 +20,65 @@ Bug fixes
- Fix exception modifications import in recurrence series
- Fix server side crash parsing rtf emails with images (with word97 format)
2.2.17a (2014-03-15)
--------------------
Bug fixes
- avoid calling -stringByReplacingOccurrencesOfString:... for old GNUstep runtime
2.2.17 (2015-03-24)
-------------------
Enhancements
- support for mail prority using EAS
- immediately delete mails from EAS clients when they are marked as deleted on the IMAP server
- now favor login@domain as the default email address if multiple mail: fields are specified
- enable by default HTML mails support using EAS on Windows and BB phones
- now possible to configure objectClass names for LDAP groups using GroupObjectClasses (#1499)
Bug fixes
- fixed login issue after password change (#2601)
- fixed potential encoding issue using EAS and 8-bit mails (#3116)
- multiple collections support for GetItemEstimate using EAS
- fixed empty sync responses for EAS 2.5 and 12.0 clients
- use the correct mail body element for EAS 2.5 clients
- fixed tasks disappearing issue with RoadSync
- use the correct body element for events for EAS 2.5 clients
- SmartReply improvements for missing body attributes
- do not use syncKey from cache when davCollectionTag = -1
- use correct mail attachment elements for EAS 2.5 clients
- fixed contacts lookup by UID in freebusy
- reduced telephone number to a single value in JSON response of contacts list
- fixed freebusy data when 'busy off hours' is enabled and period starts during the weekend
- fixed fetching of freebusy data from the Web interface
- fixed EAS handling of Bcc in emails (#3138)
- fixed Language-Region tags in Web interface (#3121)
- properly fallback over EAS to UTF-8 and then Latin1 for messages w/o charset (#3103)
- prevent potential freebusy lookup crashes during timezone changes with repetitive events
- improved GetItemEstimate to count all vasnished/deleted mails too
- improvements to EAS SyncKey handling to avoid missing mails (#3048, #3058)
2.2.16 (2015-02-12)
-------------------
New features
- now possible for SOGo to change the sambaNTPassword/sambaLMPassword
- now possible to limit automatic forwards to internal/external domains
Enhancements
- added support for email categories using EAS (#2995)
- now possible to always send vacation messages (#2332)
- added EAS best practices to the documentation
- improved fetching of text parts over EAS
- updated Czech, Finnish, French, German and Hungarian translations
Bug fixes
- (regression) fixed sending a message when mail module is not active (#3088)
- mail labels with blanks are not handled correctly (#3078)
- fixed BlackBerry issues sending multiple mails over EAS (#3095)
- fixed plain/text mails showing on one line on Android/EAS (#3055)
- fixed exception in sogo-tool when parsing arguments of a set operation
2.2.15-zentyal2 (2015-03-16)
----------------------------

View File

@ -240,7 +240,7 @@ static NSTimeInterval ChannelCollectionTimer = 5 * 60;
handle = currentHandle;
else if (debugPools)
[self logWithFormat: @"DBPOOL: cannot use handle (%@ vs %@) ",
[_url absoluteString], [handle->url absoluteString]];
[_url absoluteString], [currentHandle->url absoluteString]];
return handle;
}

View File

@ -62,17 +62,14 @@
SOGoUserManager *um;
NSString *domain;
NSDictionary *contactInfos;
NSArray *contacts;
um = [SOGoUserManager sharedUserManager];
contactInfos = [um contactInfosForUserWithUIDorEmail: uid];
if (contactInfos == nil)
{
// Search among global addressbooks
domain = [[context activeUser] domain];
[um fetchContactsMatching: uid inDomain: domain];
contacts = [um fetchContactsMatching: uid inDomain: domain];
if ([contacts count] == 1)
contactInfos = [contacts lastObject];
contactInfos = [um fetchContactWithUID: uid inDomain: domain];
}
/* iCal.app compatibility:
@ -279,7 +276,6 @@
if ([uid length])
{
SOGoUserManager *um;
NSArray *contacts;
NSString *domain, *email;
NSDictionary *contact;
MSExchangeFreeBusy *exchangeFreeBusy;
@ -287,10 +283,9 @@
um = [SOGoUserManager sharedUserManager];
domain = [[context activeUser] domain];
contacts = [um fetchContactsMatching: uid inDomain: domain];
if ([contacts count] == 1)
contact = [um fetchContactWithUID: uid inDomain: domain];
if (contact)
{
contact = [contacts lastObject];
email = [contact valueForKey: @"c_email"];
source = [contact objectForKey: @"source"];
if ([email length]
@ -386,8 +381,7 @@
([currentStartDate compare: startDate] == NSOrderedAscending)? startDate : currentStartDate, @"startDate",
currentEndDate, @"endDate", nil]];
if (!firstRange
&& currentEndDate != endDate
if (currentEndDate != endDate
&& ([currentEndDate dayOfWeek] == 6 || [currentEndDate dayOfWeek] == 0))
{
// Fill weekend days

View File

@ -1,6 +1,6 @@
/* SOGoWebAppointmentFolder.m - this file is part of SOGo
*
* Copyright (C) 2009-2014 Inverse inc.
* Copyright (C) 2009-2015 Inverse inc.
*
* This file is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@ -137,6 +137,7 @@ size_t curl_body_function(void *ptr, size_t size, size_t nmemb, void *buffer)
curl_easy_setopt (curl, CURLOPT_URL, [location UTF8String]);
curl_easy_setopt (curl, CURLOPT_SSL_VERIFYPEER, 0L);
curl_easy_setopt (curl, CURLOPT_SSL_VERIFYHOST, 0L);
curl_easy_setopt (curl, CURLOPT_TIMEOUT, 60L);
authInfos = [self _loadAuthData];
if (authInfos)

View File

@ -267,6 +267,13 @@
data = [oldRecord objectForKey: @"homephone"];
if (![data length])
data = @"";
else if ([data isKindOfClass: [NSArray class]])
{
if ([data count] > 0)
data = [data objectAtIndex: 0];
else
data = @"";
}
[newRecord setObject: data forKey: @"c_telephonenumber"];
// Custom attribute for group-lookups. See LDAPSource.m where

View File

@ -2129,13 +2129,13 @@ _compareFetchResultsByMODSEQ (id entry1, id entry2, void *data)
value: nextModseq];
searchQualifier = [[EOAndQualifier alloc]
initWithQualifiers:
kvQualifier, [self _nonDeletedQualifier], nil];
kvQualifier, nil];
[kvQualifier release];
[searchQualifier autorelease];
}
else
{
searchQualifier = [self _nonDeletedQualifier];
searchQualifier = nil;
}
if (theStartDate)
@ -2144,7 +2144,7 @@ _compareFetchResultsByMODSEQ (id entry1, id entry2, void *data)
sinceDateQualifier = [EOQualifier qualifierWithQualifierFormat:
@"(DATE >= %@)", theStartDate];
searchQualifier = [[EOAndQualifier alloc] initWithQualifiers: searchQualifier, sinceDateQualifier,
searchQualifier = [[EOAndQualifier alloc] initWithQualifiers: sinceDateQualifier, searchQualifier,
nil];
[searchQualifier autorelease];
}
@ -2155,7 +2155,7 @@ _compareFetchResultsByMODSEQ (id entry1, id entry2, void *data)
sortOrdering: nil];
fetchResults = [(NSDictionary *)[self fetchUIDs: uids
parts: [NSArray arrayWithObject: @"modseq"]]
parts: [NSArray arrayWithObjects: @"modseq", @"flags", nil]]
objectForKey: @"fetch"];
/* NOTE: we sort items manually because Cyrus does not properly sort
@ -2166,7 +2166,7 @@ _compareFetchResultsByMODSEQ (id entry1, id entry2, void *data)
for (i = 0; i < [fetchResults count]; i++)
{
d = [NSDictionary dictionaryWithObject: [[fetchResults objectAtIndex: i] objectForKey: @"modseq"]
d = [NSDictionary dictionaryWithObject: ([[[fetchResults objectAtIndex: i] objectForKey: @"flags"] containsObject: @"deleted"]) ? [NSNull null] : [[fetchResults objectAtIndex: i] objectForKey: @"modseq"]
forKey: [[[fetchResults objectAtIndex: i] objectForKey: @"uid"] stringValue]];
[allTokens addObject: d];
}

View File

@ -77,11 +77,10 @@ SOGo_HEADER_FILES = \
WORequest+SOGo.h \
WOResourceManager+SOGo.h \
WOResponse+SOGo.h \
WOContext+SOGo.h \
WOContext+SOGo.h \
\
SOGoCredentialsFile.h
# daemon tool
all::
@touch SOGoBuild.m
@ -156,10 +155,11 @@ SOGo_OBJC_FILES = \
WORequest+SOGo.m \
WOResourceManager+SOGo.m \
WOResponse+SOGo.m \
WOContext+SOGo.m \
WOContext+SOGo.m \
\
SOGoCredentialsFile.m
SOGo_C_FILES = lmhash.c
SOGo_RESOURCE_FILES = \
SOGoDefaults.plist \

View File

@ -1,6 +1,6 @@
/* LDAPSource.h - this file is part of SOGo
*
* Copyright (C) 2007-2014 Inverse inc.
* Copyright (C) 2007-2015 Inverse inc.
*
* This file is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@ -72,12 +72,14 @@
NSDictionary *contactMapping;
NSArray *contactObjectClasses;
NSArray *groupObjectClasses;
NSDictionary *modulesConstraints;
NSMutableArray *searchAttributes;
BOOL passwordPolicy;
BOOL updateSambaNTLMPasswords;
/* resources handling */
NSString *kindField;
@ -105,6 +107,7 @@
UIDField: (NSString *) newUIDField
mailFields: (NSArray *) newMailFields
searchFields: (NSArray *) newSearchFields
groupObjectClasses: (NSArray *) newGroupObjectClasses
IMAPHostField: (NSString *) newIMAPHostField
IMAPLoginField: (NSString *) newIMAPLoginField
SieveHostField: (NSString *) newSieveHostField

View File

@ -1,6 +1,6 @@
/* LDAPSource.m - this file is part of SOGo
*
* Copyright (C) 2007-2014 Inverse inc.
* Copyright (C) 2007-2015 Inverse inc.
*
* This file is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@ -50,7 +50,6 @@ static Class NSStringK;
stringByReplacingString: @"'" withString: @"\\'"] \
stringByReplacingString: @"%" withString: @"%%"]
@implementation LDAPSource
+ (void) initialize
@ -102,6 +101,8 @@ static Class NSStringK;
contactMapping = nil;
searchFields = [NSArray arrayWithObjects: @"sn", @"displayname", @"telephonenumber", nil];
[searchFields retain];
groupObjectClasses = [NSArray arrayWithObjects: @"group", @"groupofnames", @"groupofuniquenames", @"posixgroup", nil];
[groupObjectClasses retain];
IMAPHostField = nil;
IMAPLoginField = nil;
SieveHostField = nil;
@ -113,6 +114,7 @@ static Class NSStringK;
searchAttributes = nil;
passwordPolicy = NO;
updateSambaNTLMPasswords = NO;
kindField = nil;
multipleBookingsField = nil;
@ -144,6 +146,7 @@ static Class NSStringK;
[contactMapping release];
[mailFields release];
[searchFields release];
[groupObjectClasses release];
[IMAPHostField release];
[IMAPLoginField release];
[SieveHostField release];
@ -189,6 +192,7 @@ static Class NSStringK;
UIDField: [udSource objectForKey: @"UIDFieldName"]
mailFields: [udSource objectForKey: @"MailFieldNames"]
searchFields: [udSource objectForKey: @"SearchFieldNames"]
groupObjectClasses: [udSource objectForKey: @"GroupObjectClasses"]
IMAPHostField: [udSource objectForKey: @"IMAPHostFieldName"]
IMAPLoginField: [udSource objectForKey: @"IMAPLoginFieldName"]
SieveHostField: [udSource objectForKey: @"SieveHostFieldName"]
@ -245,6 +249,9 @@ static Class NSStringK;
if ([udSource objectForKey: @"passwordPolicy"])
passwordPolicy = [[udSource objectForKey: @"passwordPolicy"] boolValue];
if ([udSource objectForKey: @"updateSambaNTLMPasswords"])
updateSambaNTLMPasswords = [[udSource objectForKey: @"updateSambaNTLMPasswords"] boolValue];
ASSIGN(MSExchangeHostname, [udSource objectForKey: @"MSExchangeHostname"]);
}
@ -307,6 +314,7 @@ static Class NSStringK;
UIDField: (NSString *) newUIDField
mailFields: (NSArray *) newMailFields
searchFields: (NSArray *) newSearchFields
groupObjectClasses: (NSArray *) newGroupObjectClasses
IMAPHostField: (NSString *) newIMAPHostField
IMAPLoginField: (NSString *) newIMAPLoginField
SieveHostField: (NSString *) newSieveHostField
@ -331,6 +339,8 @@ static Class NSStringK;
ASSIGN(mailFields, newMailFields);
if (newSearchFields)
ASSIGN(searchFields, newSearchFields);
if (newGroupObjectClasses)
ASSIGN(groupObjectClasses, newGroupObjectClasses);
if (newBindFields)
{
// Before SOGo v1.2.0, bindFields was a comma-separated list
@ -598,6 +608,40 @@ static Class NSStringK;
return [NSString stringWithFormat: @"{%@}%@", _userPasswordAlgorithm, pass];
}
- (BOOL) _ldapModifyAttribute: (NSString *) theAttribute
withValue: (NSString *) theValue
userDN: (NSString *) theUserDN
password: (NSString *) theUserPassword
connection: (NGLdapConnection *) bindConnection
{
NGLdapModification *mod;
NGLdapAttribute *attr;
NSArray *changes;
BOOL didChange;
attr = [[NGLdapAttribute alloc] initWithAttributeName: theAttribute];
[attr addStringValue: theValue];
mod = [NGLdapModification replaceModification: attr];
changes = [NSArray arrayWithObject: mod];
if ([bindConnection bindWithMethod: @"simple"
binddn: theUserDN
credentials: theUserPassword])
{
didChange = [bindConnection modifyEntryWithDN: theUserDN
changes: changes];
}
else
didChange = NO;
RELEASE(attr);
return didChange;
}
//
//
//
@ -651,12 +695,8 @@ static Class NSStringK;
{
// We don't use a password policy - we simply use
// a modify-op to change the password
NGLdapModification *mod;
NGLdapAttribute *attr;
NSArray *changes;
NSString* encryptedPass;
attr = [[NGLdapAttribute alloc] initWithAttributeName: @"userPassword"];
if ([_userPasswordAlgorithm isEqualToString: @"none"])
{
encryptedPass = newPassword;
@ -665,24 +705,33 @@ static Class NSStringK;
{
encryptedPass = [self _encryptPassword: newPassword];
}
if(encryptedPass != nil)
if (encryptedPass != nil)
{
[attr addStringValue: encryptedPass];
mod = [NGLdapModification replaceModification: attr];
changes = [NSArray arrayWithObject: mod];
*perr = PolicyNoError;
if ([bindConnection bindWithMethod: @"simple"
binddn: userDN
credentials: oldPassword])
{
didChange = [bindConnection modifyEntryWithDN: userDN
changes: changes];
}
else
didChange = NO;
didChange = [self _ldapModifyAttribute: @"userPassword"
withValue: encryptedPass
userDN: userDN
password: oldPassword
connection: bindConnection];
}
}
// We must check if we must update the Samba NT/LM password hashes
if (didChange && updateSambaNTLMPasswords)
{
[self _ldapModifyAttribute: @"sambaNTPassword"
withValue: [newPassword asNTHash]
userDN: userDN
password: newPassword
connection: bindConnection];
[self _ldapModifyAttribute: @"sambaLMPassword"
withValue: [newPassword asLMHash]
userDN: userDN
password: newPassword
connection: bindConnection];
}
}
}
}
@ -989,6 +1038,8 @@ static Class NSStringK;
NSString *value;
static NSArray *resourceKinds = nil;
NSMutableArray *classes;
NSEnumerator *gclasses;
NSString *gclass;
id o;
if (!resourceKinds)
@ -1017,24 +1068,27 @@ static Class NSStringK;
if (classes)
{
// We check if our entry is a group. If so, we set the
// 'isGroup' custom attribute.
if ([classes containsObject: @"group"] ||
[classes containsObject: @"groupofnames"] ||
[classes containsObject: @"groupofuniquenames"] ||
[classes containsObject: @"posixgroup"])
{
[ldifRecord setObject: [NSNumber numberWithInt: 1]
forKey: @"isGroup"];
}
// We check if our entry is a resource. We also support
// determining resources based on the KindFieldName attribute
// value - see below.
else if ([classes containsObject: @"calendarresource"])
if ([classes containsObject: @"calendarresource"])
{
[ldifRecord setObject: [NSNumber numberWithInt: 1]
forKey: @"isResource"];
}
else
{
// We check if our entry is a group. If so, we set the
// 'isGroup' custom attribute.
gclasses = [groupObjectClasses objectEnumerator];
while (gclass = [gclasses nextObject])
if ([classes containsObject: [gclass lowercaseString]])
{
[ldifRecord setObject: [NSNumber numberWithInt: 1]
forKey: @"isGroup"];
break;
}
}
}
// We check if that entry corresponds to a resource. For this,
@ -1326,6 +1380,11 @@ static Class NSStringK;
return modifiers;
}
- (NSArray *) groupObjectClasses
{
return groupObjectClasses;
}
static NSArray *
_convertRecordToLDAPAttributes (LDAPSourceSchema *schema, NSDictionary *ldifRecord)
{
@ -1641,6 +1700,7 @@ _makeLDAPChanges (NGLdapConnection *ldapConnection,
UIDField: @"cn"
mailFields: nil
searchFields: nil
groupObjectClasses: nil
IMAPHostField: nil
IMAPLoginField: nil
SieveHostField: nil

View File

@ -1,7 +1,7 @@
/* NSData+Crypto.h - this file is part of SOGo
*
* Copyright (C) 2012 Nicolas Höft
* Copyright (C) 2012 Inverse inc.
* Copyright (C) 2012-2015 Inverse inc.
*
* Author: Nicolas Höft
* Inverse inc.
@ -32,8 +32,10 @@
@interface NSData (SOGoCryptoExtension)
- (NSData *) asCryptedPassUsingScheme: (NSString *) passwordScheme
withSalt: (NSData *) theSalt;
withSalt: (NSData *) theSalt;
- (NSData *) asLM;
- (NSData *) asMD4;
- (NSData *) asMD5;
- (NSData *) asSMD5UsingSalt: (NSData *) theSalt;
- (NSData *) asSHA1;

View File

@ -1,7 +1,7 @@
/* NSData+Crypto.m - this file is part of SOGo
*
* Copyright (C) 2012 Nicolas Höft
* Copyright (C) 2012 Inverse inc.
* Copyright (C) 2012-2015 Inverse inc.
* Copyright (C) 2012 Jeroen Dekkers
*
* Author: Nicolas Höft
@ -39,12 +39,14 @@
#include <stdint.h>
#include <gnutls/gnutls.h>
#include <gnutls/crypto.h>
#define MD4_DIGEST_LENGTH 16
#define MD5_DIGEST_LENGTH 16
#define SHA_DIGEST_LENGTH 20
#define SHA256_DIGEST_LENGTH 32
#define SHA512_DIGEST_LENGTH 64
#elif defined(HAVE_OPENSSL)
#include <openssl/evp.h>
#include <openssl/md4.h>
#include <openssl/md5.h>
#include <openssl/sha.h>
#else
@ -145,7 +147,7 @@ static void _nettle_md5_compress(uint32_t *digest, const uint8_t *input);
* @return Pseudo-random binary data with length theLength or nil, if an error occured
*/
+ (NSData *) generateSaltForLength: (unsigned int) theLength
withBase64: (BOOL) doBase64
withBase64: (BOOL) doBase64
{
char *buf;
int fd;
@ -177,7 +179,7 @@ static void _nettle_md5_compress(uint32_t *digest, const uint8_t *input);
* @return Binary data from the encryption by the specified scheme. On error the funciton returns nil.
*/
- (NSData *) asCryptedPassUsingScheme: (NSString *) passwordScheme
withSalt: (NSData *) theSalt
withSalt: (NSData *) theSalt
{
if ([passwordScheme caseInsensitiveCompare: @"none"] == NSOrderedSame ||
[passwordScheme caseInsensitiveCompare: @"plain"] == NSOrderedSame ||
@ -193,6 +195,10 @@ static void _nettle_md5_compress(uint32_t *digest, const uint8_t *input);
{
return [self asMD5CryptUsingSalt: theSalt];
}
else if ([passwordScheme caseInsensitiveCompare: @"md4"] == NSOrderedSame)
{
return [self asMD4];
}
else if ([passwordScheme caseInsensitiveCompare: @"md5"] == NSOrderedSame ||
[passwordScheme caseInsensitiveCompare: @"plain-md5"] == NSOrderedSame ||
[passwordScheme caseInsensitiveCompare: @"ldap-md5"] == NSOrderedSame)
@ -235,6 +241,49 @@ static void _nettle_md5_compress(uint32_t *digest, const uint8_t *input);
return nil;
}
- (NSData *) asLM
{
NSData *out;
unsigned char buf[14];
unsigned char *o;
unsigned int len;
memset(buf, 0, 14);
len = ([self length] >= 14 ? 14 : [self length]);
[self getBytes: buf length: len];
o = malloc(16*sizeof(unsigned char));
auth_LMhash(o, buf, len);
out = [NSData dataWithBytes: o length: 16];
free(o);
return out;
}
/**
* Hash the data with MD4. Uses openssl functions to generate it.
*
* @return Binary data from MD4 hashing. On error the funciton returns nil.
*/
- (NSData *) asMD4
{
unsigned char md4[MD4_DIGEST_LENGTH];
memset(md4, 0, MD4_DIGEST_LENGTH);
#if defined(HAVE_GNUTLS)
if (!check_gnutls_init())
return nil;
md4_buffer([self bytes], [self length], md4);
#elif defined(HAVE_OPENSSL)
MD4([self bytes], [self length], md4);
#endif
return [NSData dataWithBytes: md4 length: MD4_DIGEST_LENGTH];
}
/**
* Hash the data with MD5. Uses openssl functions to generate it

View File

@ -1,7 +1,7 @@
/* NSString+Crypto.h - this file is part of SOGo
*
* Copyright (C) 2012 Nicolas Höft
* Copyright (C) 2012 Inverse inc.
* Copyright (C) 2012-2015 Inverse inc.
*
* Author: Nicolas Höft
* Inverse inc.
@ -39,13 +39,12 @@ typedef enum {
@interface NSString (SOGoCryptoExtension)
- (BOOL) isEqualToCrypted: (NSString *) cryptedPassword
withDefaultScheme: (NSString *) theScheme;
withDefaultScheme: (NSString *) theScheme;
- (NSString *) asCryptedPassUsingScheme: (NSString *) passwordScheme
withSalt: (NSData *) theSalt
andEncoding: (keyEncoding) encoding;
andEncoding: (keyEncoding) encoding;
// this method uses the default encoding (base64, plain, hex)
// and generates a salt when necessary
@ -56,6 +55,9 @@ typedef enum {
- (NSString *) asSHA1String;
- (NSString *) asMD5String;
- (NSString *) asNTHash;
- (NSString *) asLMHash;
+ (NSArray *) getDefaultEncodingForScheme: (NSString *) passwordScheme;
@end

View File

@ -1,7 +1,7 @@
/* NSString+Crypto.m - this file is part of SOGo
*
* Copyright (C) 2012 Nicolas Höft
* Copyright (C) 2012 Inverse inc.
* Copyright (C) 2012-2015 Inverse inc.
*
* Author: Nicolas Höft
* Inverse inc.
@ -29,6 +29,8 @@
#import "NSData+Crypto.h"
#import <NGExtensions/NGBase64Coding.h>
#include "lmhash.h"
@implementation NSString (SOGoCryptoExtension)
/**
@ -90,7 +92,8 @@
encodingAndScheme = [NSString getDefaultEncodingForScheme: scheme];
pass = [self substringWithRange: range];
// return array with [scheme, password, encoding]
// Returns an array with [scheme, password, encoding]
return [NSArray arrayWithObjects: [encodingAndScheme objectAtIndex: 1], pass, [encodingAndScheme objectAtIndex: 0], nil];
}
@ -103,7 +106,7 @@
* @return YES if the passwords are identical using this encryption scheme
*/
- (BOOL) isEqualToCrypted: (NSString *) cryptedPassword
withDefaultScheme: (NSString *) theScheme
withDefaultScheme: (NSString *) theScheme
{
NSArray *passInfo;
NSString *selfCrypted;
@ -193,7 +196,7 @@
*/
- (NSString *) asCryptedPassUsingScheme: (NSString *) passwordScheme
withSalt: (NSData *) theSalt
andEncoding: (keyEncoding) userEncoding
andEncoding: (keyEncoding) userEncoding
{
keyEncoding dataEncoding;
NSData* cryptedData;
@ -229,7 +232,7 @@
{
// base64 encoding
NSString *s = [[NSString alloc] initWithData: [cryptedData dataByEncodingBase64WithLineLength: 1024]
encoding: NSASCIIStringEncoding];
encoding: NSASCIIStringEncoding];
return [s autorelease];
}
@ -277,7 +280,8 @@
}
// in order to keep backwards-compatibility, hex encoding is used for sha1 here
if ([passwordScheme caseInsensitiveCompare: @"md5"] == NSOrderedSame ||
if ([passwordScheme caseInsensitiveCompare: @"md4"] == NSOrderedSame ||
[passwordScheme caseInsensitiveCompare: @"md5"] == NSOrderedSame ||
[passwordScheme caseInsensitiveCompare: @"plain-md5"] == NSOrderedSame ||
[passwordScheme caseInsensitiveCompare: @"sha"] == NSOrderedSame ||
[passwordScheme caseInsensitiveCompare: @"cram-md5"] == NSOrderedSame)
@ -294,6 +298,7 @@
{
encoding = encBase64;
}
return [NSArray arrayWithObjects: [NSNumber numberWithInt: encoding], trueScheme, nil];
}
@ -306,7 +311,7 @@
{
NSData *cryptData;
cryptData = [self dataUsingEncoding: NSUTF8StringEncoding];
return [NSData encodeDataAsHexString: [cryptData asSHA1] ];
return [NSData encodeDataAsHexString: [cryptData asSHA1]];
}
/**
@ -318,7 +323,36 @@
{
NSData *cryptData;
cryptData = [self dataUsingEncoding: NSUTF8StringEncoding];
return [NSData encodeDataAsHexString: [cryptData asMD5] ];
return [NSData encodeDataAsHexString: [cryptData asMD5]];
}
/**
* Encrypts the data using the NT-hash password scheme.
*
* @return If successful, NT-hash encrypted and with hex encoded string
*/
- (NSString *) asNTHash
{
NSData *d;
d = [self dataUsingEncoding: NSUTF16LittleEndianStringEncoding];
return [[NSData encodeDataAsHexString: [d asMD4]] uppercaseString];
}
/**
* Encrypts the data using the LM-hash password scheme.
*
* @return If successful, LM-hash encrypted and with hex encoded string
*/
- (NSString *) asLMHash
{
NSData *d;
// See http://en.wikipedia.org/wiki/LM_hash#Algorithm
d = [[self uppercaseString] dataUsingEncoding: NSWindowsCP1252StringEncoding];
return [[NSData encodeDataAsHexString: [d asLM]] uppercaseString];
}
@end

View File

@ -113,7 +113,6 @@
+ (void) handleLogoutRequest: (NSString *) logoutRequest
{
CASLogoutRequest *rq;
SOGoCache *cache;
NSBundle *bundle;
NSString *mapFile, *sessionIndex;

View File

@ -1,6 +1,6 @@
/* SOGoDomainDefaults.h - this file is part of SOGo
*
* Copyright (C) 2009-2014 Inverse inc.
* Copyright (C) 2009-2015 Inverse inc.
*
* This file is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@ -49,6 +49,7 @@
- (BOOL) forceExternalLoginWithEmail;
- (BOOL) sieveScriptsEnabled;
- (BOOL) forwardEnabled;
- (int) forwardConstraints;
- (BOOL) vacationEnabled;
- (NSString *) mailingMechanism;
- (NSString *) smtpServer;

View File

@ -1,6 +1,6 @@
/* SOGoDomainDefaults.m - this file is part of SOGo
*
* Copyright (C) 2009-2014 Inverse inc.
* Copyright (C) 2009-2015 Inverse inc.
*
* This file is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@ -201,6 +201,15 @@
return [self boolForKey: @"SOGoForwardEnabled"];
}
- (int) forwardConstraints
{
unsigned int v;
v = [self integerForKey: @"SOGoForwardConstraints"];
return (v > 2 ? 0 : v);
}
- (BOOL) vacationEnabled
{
return [self boolForKey: @"SOGoVacationEnabled"];

View File

@ -137,6 +137,8 @@
NGLdapEntry *entry;
NSObject <SOGoSource> *source;
id o;
NSEnumerator *gclasses;
NSString *gclass;
int i;
@ -194,12 +196,11 @@
}
}
// Found a group, let's return it.
if ([classes containsObject: @"group"] ||
[classes containsObject: @"groupofnames"] ||
[classes containsObject: @"groupofuniquenames"] ||
[classes containsObject: @"posixgroup"])
{
gclasses = [[source groupObjectClasses] objectEnumerator];
while (gclass = [gclasses nextObject])
if ([classes containsObject: gclass])
{
// Found a group, let's return it.
o = [[self alloc] initWithIdentifier: theValue
domain: domain
source: source

View File

@ -835,13 +835,16 @@ static NSString *sieveScriptName = @"sogo";
if (values && [[values objectForKey: @"enabled"] boolValue])
{
NSMutableString *vacation_script;
NSArray *addresses;
NSString *text;
BOOL ignore;
BOOL ignore, alwaysSend;
int days, i;
days = [[values objectForKey: @"daysBetweenResponse"] intValue];
addresses = [values objectForKey: @"autoReplyEmailAddresses"];
alwaysSend = [[values objectForKey: @"alwaysSend"] boolValue];
ignore = [[values objectForKey: @"ignoreLists"] boolValue];
text = [values objectForKey: @"autoReplyText"];
b = YES;
@ -849,28 +852,38 @@ static NSString *sieveScriptName = @"sogo";
if (days == 0)
days = 7;
vacation_script = [NSMutableString string];
[req addObjectUniquely: @"vacation"];
// Skip mailing lists
if (ignore)
[script appendString: @"if allof ( not exists [\"list-help\", \"list-unsubscribe\", \"list-subscribe\", \"list-owner\", \"list-post\", \"list-archive\", \"list-id\", \"Mailing-List\"], not header :comparator \"i;ascii-casemap\" :is \"Precedence\" [\"list\", \"bulk\", \"junk\"], not header :comparator \"i;ascii-casemap\" :matches \"To\" \"Multiple recipients of*\" ) {"];
[vacation_script appendString: @"if allof ( not exists [\"list-help\", \"list-unsubscribe\", \"list-subscribe\", \"list-owner\", \"list-post\", \"list-archive\", \"list-id\", \"Mailing-List\"], not header :comparator \"i;ascii-casemap\" :is \"Precedence\" [\"list\", \"bulk\", \"junk\"], not header :comparator \"i;ascii-casemap\" :matches \"To\" \"Multiple recipients of*\" ) {"];
[script appendFormat: @"vacation :days %d :addresses [", days];
[vacation_script appendFormat: @"vacation :days %d :addresses [", days];
for (i = 0; i < [addresses count]; i++)
{
[script appendFormat: @"\"%@\"", [addresses objectAtIndex: i]];
[vacation_script appendFormat: @"\"%@\"", [addresses objectAtIndex: i]];
if (i == [addresses count]-1)
[script appendString: @"] "];
[vacation_script appendString: @"] "];
else
[script appendString: @", "];
[vacation_script appendString: @", "];
}
[script appendFormat: @"text:\r\n%@\r\n.\r\n;\r\n", text];
[vacation_script appendFormat: @"text:\r\n%@\r\n.\r\n;\r\n", text];
if (ignore)
[script appendString: @"}\r\n"];
[vacation_script appendString: @"}\r\n"];
//
// See http://sogo.nu/bugs/view.php?id=2332 for details
//
if (alwaysSend)
[script insertString: vacation_script atIndex: 0];
else
[script appendString: vacation_script];
}

View File

@ -110,6 +110,7 @@
- (NSString *) baseDN;
- (NSString *) MSExchangeHostname;
- (NSArray *) groupObjectClasses;
@end
#endif /* SOGOSOURCE_H */

View File

@ -1,5 +1,5 @@
/*
Copyright (C) 2006-2013 Inverse inc.
Copyright (C) 2006-2015 Inverse inc.
Copyright (C) 2005 SKYRIX Software AG
This file is part of SOGo.

View File

@ -37,6 +37,7 @@
#import <NGExtensions/NSObject+Logs.h>
#import <Appointments/SOGoAppointmentFolders.h>
#import <Contacts/SOGoContactFolders.h>
#import "NSArray+Utilities.h"
#import "SOGoCache.h"
@ -234,7 +235,6 @@
[currentPassword release];
[cn release];
[loginInDomain release];
//[language release];
[super dealloc];
}
@ -577,13 +577,13 @@
- (void) _appendSystemMailAccount
{
NSString *fullName, *replyTo, *imapLogin, *imapServer, *cImapServer, *signature,
*encryption, *scheme, *action, *query, *customEmail, *sieveServer;
*encryption, *scheme, *action, *query, *customEmail, *defaultEmail, *sieveServer;
NSMutableDictionary *mailAccount, *identity, *mailboxes, *receipts;
NSNumber *port;
NSMutableArray *identities;
NSArray *mails;
NSURL *url, *cUrl;
unsigned int count, max;
unsigned int count, max, default_identity;
NSInteger defaultPort;
[self userDefaults];
@ -661,8 +661,9 @@
[mailAccount setObject: sieveServer forKey: @"sieveServerName"];
}
/* identities */
// Identities
defaultEmail = [NSString stringWithFormat: @"%@@%@", [self loginInDomain], [self domain]];
default_identity = 0;
identities = [NSMutableArray new];
mails = [self allEmails];
[mailAccount setObject: [mails objectAtIndex: 0] forKey: @"name"];
@ -702,6 +703,10 @@
if (signature)
[identity setObject: signature forKey: @"signature"];
[identities addObject: identity];
if ([[identity objectForKey: @"email"] caseInsensitiveCompare: defaultEmail] == NSOrderedSame)
default_identity = [identities count]-1;
[identity release];
}
}
@ -723,10 +728,14 @@
if (signature)
[identity setObject: signature forKey: @"signature"];
[identities addObject: identity];
if ([[identity objectForKey: @"email"] caseInsensitiveCompare: defaultEmail] == NSOrderedSame)
default_identity = [identities count]-1;
[identity release];
}
[[identities objectAtIndex: 0] setObject: [NSNumber numberWithBool: YES]
forKey: @"isDefault"];
[[identities objectAtIndex: default_identity] setObject: [NSNumber numberWithBool: YES]
forKey: @"isDefault"];
[mailAccount setObject: identities forKey: @"identities"];
[identities release];

View File

@ -65,10 +65,13 @@
- (NSDictionary *) contactInfosForUserWithUIDorEmail: (NSString *) uid;
- (NSDictionary *) contactInfosForUserWithUIDorEmail: (NSString *) uid
inDomain: (NSString *) domain;
- (NSDictionary *) fetchContactWithUID: (NSString *) uid
inDomain: (NSString *) domain;
- (NSArray *) fetchContactsMatching: (NSString *) match
inDomain: (NSString *) domain;
- (NSArray *) fetchUsersMatching: (NSString *) filter
inDomain: (NSString *) domain;
- (NSArray *) _compactAndCompleteContacts: (NSEnumerator *) contacts;
- (NSString *) getCNForUID: (NSString *) uid;
- (NSString *) getEmailForUID: (NSString *) uid;

View File

@ -18,6 +18,7 @@
* Boston, MA 02111-1307, USA.
*/
#import <Foundation/NSArray.h>
#import <Foundation/NSCalendarDate.h>
#import <Foundation/NSDictionary.h>
#import <Foundation/NSEnumerator.h>
#import <Foundation/NSException.h>
@ -914,6 +915,37 @@ static Class NSNullK;
return currentUser;
}
/**
* Fetch the contact information identified by the specified UID in the global addressbooks.
*/
- (NSDictionary *) fetchContactWithUID: (NSString *) uid
inDomain: (NSString *) domain
{
NSDictionary *contact;
NSMutableArray *contacts;
NSEnumerator *sources;
NSString *sourceID;
id currentSource;
contacts = [NSMutableArray array];
contact = nil;
sources = [[self addressBookSourceIDsInDomain: domain] objectEnumerator];
while ((sourceID = [sources nextObject]))
{
currentSource = [_sources objectForKey: sourceID];
contact = [currentSource lookupContactEntry: uid];
if (contact)
[contacts addObject: contact];
}
if ([contacts count])
contact = [[self _compactAndCompleteContacts: [contacts objectEnumerator]] lastObject];
else
contact = nil;
return contact;
}
- (NSArray *) _compactAndCompleteContacts: (NSEnumerator *) contacts
{
NSMutableDictionary *compactContacts, *returnContact;

View File

@ -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 */

View File

@ -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

View File

@ -1,8 +1,6 @@
/* SOGoTool.h - this file is part of SOGo
*
* Copyright (C) 2009-2011 Inverse inc.
*
* Author: Wolfgang Sourdeau <wsourdeau@inverse.ca>
* Copyright (C) 2009-2015 Inverse inc.
*
* This file is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by

View File

@ -1,8 +1,6 @@
/* SOGoTool.m - this file is part of SOGo
*
* Copyright (C) 2009-2011 Inverse inc.
*
* Author: Wolfgang Sourdeau <wsourdeau@inverse.ca>
* Copyright (C) 2009-2015 Inverse inc.
*
* This file is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by

View File

@ -254,7 +254,7 @@ typedef enum
//
// 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];
}

View File

@ -79,7 +79,6 @@
"Remove this folder" = "مسح المجلد";
"Erase mails from this folder" = "مسح الرسائل من هذا المجلد";
"Expunge this folder" = "محو هذا المجلد";
"Archive This Folder" = "أرشفة هذا المجلد";
"Modify the acl of this folder" = "تعديل قائمة الصلاحيات لهذا المجلد";
"Saved Messages.zip" = "حفظ Messages.zip";

View File

@ -79,7 +79,6 @@
"Remove this folder" = "Fjern denne mappe";
"Erase mails from this folder" = "Slet beskeder fra 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";
"Saved Messages.zip" = "Gemte beskeder.zip";

View File

@ -76,7 +76,6 @@
"Remove this folder" = "Fjarlægja þessa möppu";
"Erase mails from this folder" = "Eyða bréfum úr þ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";
"Saved Messages.zip" = "vistud_skilabod.zip";

View File

@ -79,7 +79,6 @@
"Remove this folder" = "Rimuovi questa cartella";
"Erase mails from this folder" = "Elimina emails da 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";
"Saved Messages.zip" = "Salvato Messages.zip";

View File

@ -76,7 +76,6 @@
"Remove this folder" = "Ta bort mappen";
"Erase mails from this folder" = "Slett meldinger fra mappen";
"Expunge this folder" = "Fjern mappen";
"Archive This Folder" = "Arkiver denne mappen";
"Modify the acl of this folder" = "Endre adgangsrettigheter til mappen";
"Saved Messages.zip" = "Lagrete Meldinger.zip";

View File

@ -76,7 +76,6 @@
"Remove this folder" = "Ta bort mappen";
"Erase mails from this folder" = "Radera meddelanden från mappen";
"Expunge this folder" = "Radera mappen";
"Archive This Folder" = "Arkivera mappen";
"Modify the acl of this folder" = "Ändra åtkomsträttigheter på mappen";
"Saved Messages.zip" = "Sparat Messages.zip";

View File

@ -1,6 +1,6 @@
/*
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.
@ -230,7 +230,15 @@ static NSArray *infoKeys = nil;
- (NSString *) localeCode
{
// 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

View File

@ -712,19 +712,26 @@
WORequest *request;
SOGoMailFolder *co;
NSException *error;
NSArray *msgUIDs, *flags;
NSArray *msgUIDs;
NSMutableArray *flags;
NSString *operation;
NSDictionary *content, *result;
BOOL addOrRemove;
NGImap4Client *client;
int i;
request = [context request];
content = [[request contentAsString] objectFromJSONString];
flags = [NSArray arrayWithObject:[content objectForKey:@"flags"]];
flags = [NSMutableArray arrayWithObject:[content objectForKey:@"flags"]];
msgUIDs = [NSArray arrayWithArray:[content objectForKey:@"msgUIDs"]];
operation = [content objectForKey:@"operation"];
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];
client = [[co imap4Connection] client];
[[co imap4Connection] selectFolder: [co imap4URL]];

View File

@ -79,7 +79,6 @@
"Remove this folder" = "Вилучати цю теку";
"Erase mails from this folder" = "Вилучати повідомлення з цієї теки";
"Expunge this folder" = "Позначати листи як вилучені";
"Archive This Folder" = "Архівувати цю теку";
"Modify the acl of this folder" = "Керувати правами доступу до цієї теки";
"Saved Messages.zip" = "Збережено Messages.zip";

View File

@ -76,7 +76,6 @@
"Remove this folder" = "Dileu'r ffolder hwn";
"Erase mails from this folder" = "Dileu ebost o'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";
"Saved Messages.zip" = "Saved Messages.zip";

View File

@ -155,6 +155,7 @@
delta = [[currentDate timeZoneDetail] timeZoneSecondsFromGMT] - [[startDate timeZoneDetail] timeZoneSecondsFromGMT];
startInterval += (delta/60/15);
startInterval = (startInterval < -4 ? -4 : startInterval);
currentDate = [record objectForKey: @"endDate"];
if ([currentDate earlierDate: endDate] == endDate)
@ -165,6 +166,8 @@
delta = [[currentDate timeZoneDetail] timeZoneSecondsFromGMT] - [[startDate timeZoneDetail] timeZoneSecondsFromGMT];
endInterval += (delta/60/15);
endInterval = (endInterval < 0 ? 0 : endInterval);
endInterval = (endInterval > itemCount+4 ? itemCount+4 : endInterval);
// Update bit string representation
// 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;
// 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)
count: (intervals-4)
withRecords: [fb fetchFreeBusyInfosFrom: start to: end forContact: uid]
@ -243,7 +246,7 @@
{
[freeBusy addObject: [NSString stringWithFormat: @"%d", *(freeBusyItems + count)]];
}
NSZoneFree (NULL, freeBusyItems);
free(freeBusyItems);
// Return a NSString representation
return [freeBusy componentsJoinedByString: @","];

View File

@ -29,6 +29,7 @@
"Days between responses :" = "Počet dnů mezi odpověďmi :";
"Do not send responses to mailing lists" = "Neposílat odpovědi do konferencí";
"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."
= "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.";
@ -40,6 +41,9 @@
"Keep a copy" = "Ponechat kopii";
"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.";
"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 */
"Current Time Zone :" = "Současné časové pásmo :";

View File

@ -29,6 +29,7 @@
"Days between responses :" = "Days between responses:";
"Do not send responses to mailing lists" = "Do not send responses to mailing lists";
"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.";
"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";
"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 */
"Current Time Zone :" = "Current Time Zone:";

View File

@ -29,6 +29,7 @@
"Days between responses :" = "Päivät vastausten välillä:";
"Do not send responses to mailing lists" = "Älä lähetä vastauksia postituslistoille";
"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."
= "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ä.";
@ -40,6 +41,9 @@
"Keep a copy" = "Pidä kopio";
"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.";
"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 */
"Current Time Zone :" = "Nykyinen aikavyöhyke:";

View File

@ -29,6 +29,7 @@
"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";
"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."
= "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.";
@ -40,6 +41,9 @@
"Keep a copy" = "Garder une copie";
"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.";
"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 */
"Current Time Zone :" = "Fuseau horaire en cours :";

View File

@ -29,6 +29,7 @@
"Days between responses :" = "Tage zwischen automatischen Abwesenheitsnachrichten:";
"Do not send responses to mailing lists" = "Keine automatischen Abwesenheitsnachrichten an Mailinglisten senden";
"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."
= "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.";
@ -40,6 +41,9 @@
"Keep a copy" = "Eine Kopie behalten";
"Please specify an address to which you want to forward your messages."
= "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 */
"Current Time Zone :" = "Aktuelle Zeitzone:";

View File

@ -29,6 +29,7 @@
"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";
"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."
= "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.";
@ -40,6 +41,9 @@
"Keep a copy" = "Másolat megtartása";
"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.";
"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 */
"Current Time Zone :" = "Időzóna :";

View File

@ -1,6 +1,6 @@
/* 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
* it under the terms of the GNU General Public License as published by
@ -1127,6 +1127,30 @@ static NSArray *reminderValues = nil;
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
{
return [[vacationOptions objectForKey: @"endDateEnabled"] boolValue];
@ -1214,6 +1238,15 @@ static NSArray *reminderValues = nil;
return [[forwardOptions objectForKey: @"keepCopy"] boolValue];
}
- (NSString *) forwardConstraints
{
SOGoDomainDefaults *dd;
dd = [[context activeUser] domainDefaults];
return [NSString stringWithFormat: @"%d", [dd forwardConstraints]];
}
/* main */
- (NSArray *) availableModules

View File

@ -1,8 +1,6 @@
/* UIxAttendeesEditor.h - this file is part of SOGo
*
* Copyright (C) 2007-2012 Inverse inc.
*
* Author: Wolfgang Sourdeau <wsourdeau@inverse.ca>
* Copyright (C) 2007-2015 Inverse inc.
*
* This file is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by

View File

@ -1,8 +1,6 @@
/* UIxAttendeesEditor.m - this file is part of SOGo
*
* Copyright (C) 2007-2012 Inverse inc.
*
* Author: Wolfgang Sourdeau <wsourdeau@inverse.ca>
* Copyright (C) 2007-2015 Inverse inc.
*
* This file is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by

View File

@ -1,7 +1,6 @@
/* 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
* it under the terms of the GNU General Public License as published by
@ -82,7 +81,15 @@
- (NSString *) localeCode
{
// 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

View File

@ -1,6 +1,6 @@
/* 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
* it under the terms of the GNU General Public License as published by

View File

@ -176,7 +176,6 @@ iRANGE(2);
[self setIsCycleEndNever];
componentOwner = @"";
organizer = nil;
//organizerIdentity = nil;
organizerProfile = nil;
ownerAsAttendee = nil;
attendee = nil;
@ -213,7 +212,6 @@ iRANGE(2);
[title release];
[location release];
[organizer release];
//[organizerIdentity release];
[organizerProfile release];
[ownerAsAttendee release];
[comment release];
@ -820,25 +818,27 @@ iRANGE(2);
uid = email;
[profile setObject: name
forKey: @"name"];
forKey: @"name"];
[profile setObject: email
forKey: @"email"];
forKey: @"email"];
if (partstat == nil || ![partstat length])
partstat = @"accepted";
[profile setObject: partstat
forKey: @"partstat"];
forKey: @"partstat"];
if (role == nil || ![role length])
role = @"chair";
[profile setObject: role
forKey: @"role"];
forKey: @"role"];
organizerProfile = [NSDictionary dictionaryWithObject: profile forKey: uid];
[organizerProfile retain];
}
return organizerProfile;
}
@ -897,30 +897,6 @@ iRANGE(2);
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
{
// We check if there's an organizer and if it's not ourself
@ -948,51 +924,8 @@ iRANGE(2);
}
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
{
return ([[component attendees] count] > 0);

View File

@ -85,7 +85,7 @@
<li><var:string label:value="Mark Folder Read" /></li>
<li><var:string label:value="New Subfolder..." /></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><!-- separator --></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="Rename 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="Use This Folder For" /></li>
<li><!-- separator --></li>

View File

@ -600,7 +600,7 @@
<var:string label:value="Add default email addresses" /></span></a>
</span>
</label><br/>
<label><var:string label:value="Days between responses :"/>
<var:popup list="daysBetweenResponsesList" item="item"
const:id="daysBetweenResponsesList"
@ -623,8 +623,14 @@
date="vacationEndDate"
const:dayStartHour="0"
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>
</var:if
@ -647,7 +653,10 @@
const:name="forwardKeepCopy"
const:id="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>
</var:if>

View File

@ -218,7 +218,7 @@ Date.prototype.stringWithSeparator = function(separator) {
Date.prototype.addDays = function(nbrDays) {
var milliSeconds = this.getTime();
milliSeconds += 86400000 * nbrDays;
milliSeconds += 86400000 * Math.round(nbrDays);
this.setTime(milliSeconds);
};

View File

@ -896,9 +896,8 @@ _freeBusyCacheEntry.prototype = {
if (this.entries.length > offset) {
var adjustedEd = ed.beginOfDay();
var nbrDays = adjustedSd.deltaDays(adjustedEd) + 1;
var nbrQu = nbrDays * 96;
var offsetEnd = offset + nbrQu;
if (this.entries.length >= offsetEnd) {
var offsetEnd = offset + (nbrDays * 96);
if (Math.round(this.entries.length/96) >= nbrDays) {
entries = this.entries.slice(offset, offsetEnd);
}
}
@ -912,15 +911,19 @@ _freeBusyCacheEntry.prototype = {
var adjustedSd = sd.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) {
fetchDates = [];
if (adjustedSd.getTime() < this.startDate.getTime()) {
var start = adjustedSd.clone();
start.addDays(-7);
var end = this.startDate.beginOfDay();
end.addDays(-1);
// Period extends to before current start
if (end.getTime() >= this.startDate.getTime()) {
end = this.startDate.beginOfDay();
end.addDays(-1);
}
fetchDates.push({ start: start, end: end });
}
@ -928,16 +931,16 @@ _freeBusyCacheEntry.prototype = {
var nextDate = this.startDate.clone();
nextDate.addDays(currentNbrDays);
if (adjustedEd.getTime() >= nextDate.getTime()) {
var end = nextDate.clone();
end.addDays(7);
fetchDates.push({ start: nextDate, end: end });
// Period extends to after current end
if (start.getTime() <= nextDate.getTime()) {
start = nextDate.beginOfDay();
start.addDays(1);
}
fetchDates.push({ start: start, end: end });
}
}
else {
var start = adjustedSd.clone();
start.addDays(-7);
var end = adjustedEd.clone();
end.addDays(7);
// Initial range
fetchDates = [ { start: start, end: end } ];
}
@ -945,18 +948,27 @@ _freeBusyCacheEntry.prototype = {
},
integrateEntries: function fBCE_integrateEntries(entries, start, end) {
var days, merged = false;
if (this.startDate) {
if (start.getTime() < this.startDate) {
var days = start.deltaDays(this.startDate);
days = start.deltaDays(this.startDate);
if (entries.length == (days * 96)) {
// New period is just before previous period
this.startDate = start;
this.entries = entries.concat(this.entries);
merged = true;
}
}
else {
this.entries = this.entries.concat(entries);
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);
merged = true;
}
}
} else {
}
if (!merged) {
this.startDate = start;
this.entries = entries;
}
@ -1193,7 +1205,7 @@ function drawFbData(input, slots) {
else {
log("inconsistency between freebusy results and"
+ " 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) {
var endDate = window.getEndDate();
var startDate = window.getStartDate();
var delta = 0;
if (this.id.startsWith("start")) {
// Start date was changed
var delta = window.getShadowStartDate().valueOf() -
startDate.valueOf();
delta = window.getShadowStartDate().valueOf() - startDate.valueOf();
var newEndDate = new Date(endDate.valueOf() - delta);
window.setEndDate(newEndDate);
window.timeWidgets['end']['date'].updateShadowValue();
@ -1687,20 +1699,27 @@ function onAdjustTime(event) {
}
else {
// End date was changed
var delta = endDate.valueOf() - startDate.valueOf();
if (delta < 0) {
showAlertDialog(labels.validate_endbeforestart);
var oldEndDate = window.getShadowEndDate();
window.setEndDate(oldEndDate);
window.timeWidgets['end']['date'].updateShadowValue();
window.timeWidgets['end']['time'].updateShadowValue();
window.timeWidgets['end']['time'].onChange(); // method from SOGoTimePicker
delta = window.getShadowEndDate().valueOf() - endDate.valueOf();
if (delta != 0) {
var startEndDelta = endDate.valueOf() - startDate.valueOf();
if (startEndDelta < 0) {
showAlertDialog(labels.validate_endbeforestart);
var oldEndDate = window.getShadowEndDate();
window.setEndDate(oldEndDate);
window.timeWidgets['end']['date'].updateShadowValue();
window.timeWidgets['end']['time'].updateShadowValue();
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
onTimeDateWidgetChange();
if ($("freeBusyHeader").getElementsByTagName("th").length == 0 || delta != 0)
// Update freebusy data
onTimeDateWidgetChange();
}
function _getDate(which) {
@ -1739,6 +1758,7 @@ function getShadowEndDate() {
function _setDate(which, newDate) {
window.timeWidgets[which]['date'].setInputAsDate(newDate);
jQuery(window.timeWidgets[which]['date']).closest('.date').datepicker('update');
if (!isAllDay) {
window.timeWidgets[which]['time'].value = newDate.getDisplayHoursString();
if (window.timeWidgets[which]['time'].onChange) window.timeWidgets[which]['time'].onChange(); // method from SOGoTimePicker

View File

@ -14,7 +14,7 @@ var MailEditor = {
var autoSaveTimer;
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]");
window.opener.getUnseenCountForFolder(nodes[0].readAttribute("dataname"));
}

View File

@ -70,11 +70,42 @@ function savePreferences(sender) {
if ($("enableForward") && $("enableForward").checked) {
var addresses = $("forwardAddress").value.split(",");
// We check if all addresses are valid
for (var i = 0; i < addresses.length && sendForm; i++)
if (!emailRE.test(addresses[i].strip())) {
showAlertDialog(_("Please specify an address to which you want to forward your messages."));
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") {

View File

@ -4,4 +4,4 @@
MAJOR_VERSION=2
MINOR_VERSION=2
SUBMINOR_VERSION=15
SUBMINOR_VERSION=17a