Compare commits
75
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
d16eeb1a89 | ||
|
|
b26ea30b1b | ||
|
|
72b210cd2e | ||
|
|
a80d75d09c | ||
|
|
aae76dc4e6 | ||
|
|
c20ca0e33d | ||
|
|
601a9ca559 | ||
|
|
d7c283f751 | ||
|
|
f3325b8b0d | ||
|
|
b4ea4d7e9f | ||
|
|
9447437d6f | ||
|
|
322f72626a | ||
|
|
55ae4cb8c0 | ||
|
|
e5d01428ad | ||
|
|
82178fd6ce | ||
|
|
6cf1ec998e | ||
|
|
6c9b1a494f | ||
|
|
98e4c8ca55 | ||
|
|
3731819e15 | ||
|
|
eca22caefc | ||
|
|
923bcf8ae2 | ||
|
|
0b44fa81f4 | ||
|
|
776b17ff20 | ||
|
|
3ca94c60d0 | ||
|
|
2aa528dacc | ||
|
|
d1d3980919 | ||
|
|
cf35eec1f5 | ||
|
|
58a0b0c173 | ||
|
|
723a9d4e08 | ||
|
|
4cc158043e | ||
|
|
7fd1564a86 | ||
|
|
981c718d22 | ||
|
|
086361b79a | ||
|
|
ab12c84887 | ||
|
|
e2bee23021 | ||
|
|
1a10599369 | ||
|
|
d907d5d268 | ||
|
|
74b27a771a | ||
|
|
d98ff69fbe | ||
|
|
daa7ab87d7 | ||
|
|
3e59b1ad6e | ||
|
|
e45451a9f9 | ||
|
|
e9e3dd5646 | ||
|
|
16c863d89d | ||
|
|
d87056ebfa | ||
|
|
135b463e8f | ||
|
|
f4c4f5af62 | ||
|
|
70e45f7843 | ||
|
|
28e449b050 | ||
|
|
6b52e9c945 | ||
|
|
1cfbea69c1 | ||
|
|
cb9118d869 | ||
|
|
dd6dd7251c | ||
|
|
24c6c8c91d | ||
|
|
b1ac7a0cca | ||
|
|
59e6d6df4a | ||
|
|
2ae326c41f | ||
|
|
71ad5de910 | ||
|
|
ead665de85 | ||
|
|
a0c1ce8f3b | ||
|
|
204a62aa6a | ||
|
|
31cffdffd3 | ||
|
|
255bcbe92f | ||
|
|
72732879fa | ||
|
|
24a934275f | ||
|
|
58f634bffe | ||
|
|
ca4a754f2c | ||
|
|
8015688df3 | ||
|
|
b07913d66d | ||
|
|
6b49a8a341 | ||
|
|
562d3db1a6 | ||
|
|
df8a0d8715 | ||
|
|
e6cc56dca1 | ||
|
|
de3d38262b | ||
|
|
0e56527e05 |
No files matched your search
@@ -33,6 +33,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
#import <Foundation/NSCalendarDate.h>
|
||||
#import <Foundation/NSDictionary.h>
|
||||
#import <Foundation/NSString.h>
|
||||
#import <Foundation/NSTimeZone.h>
|
||||
|
||||
#import <NGExtensions/NSString+misc.h>
|
||||
|
||||
@@ -193,11 +194,12 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
if ((o = [self note]))
|
||||
{
|
||||
// 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: @"<Truncated>%d</Truncated>", 0];
|
||||
[s appendFormat: @"<Data>%@</Data>", o];
|
||||
[s appendString: @"</Body>"];
|
||||
}
|
||||
@@ -229,7 +231,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
if ((o = [theValues objectForKey: @"Birthday"]))
|
||||
{
|
||||
o = [o calendarDate];
|
||||
[self setBday: [o descriptionWithCalendarFormat: @"%Y-%m-%d"]];
|
||||
[self setBday: [o descriptionWithCalendarFormat: @"%Y-%m-%d" timeZone: nil locale: nil]];
|
||||
}
|
||||
|
||||
//
|
||||
|
||||
@@ -33,6 +33,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
#import <Foundation/NSString.h>
|
||||
|
||||
#import <NGExtensions/NGBase64Coding.h>
|
||||
#import <NGExtensions/NSObject+Logs.h>
|
||||
|
||||
#include <wbxml/wbxml.h>
|
||||
#include <wbxml/wbxml_conv.h>
|
||||
@@ -48,7 +49,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
path = [NSString stringWithFormat: @"/tmp/%@.data", [[NSProcessInfo processInfo] globallyUniqueString]];
|
||||
[self writeToFile: path atomically: YES];
|
||||
NSLog(@"Original data written to: %@", path);
|
||||
[self errorWithFormat: @"Original data written to: %@", path];
|
||||
}
|
||||
|
||||
//
|
||||
@@ -81,7 +82,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
if (ret != WBXML_OK)
|
||||
{
|
||||
NSLog(@"wbxml2xmlFromContent: failed: %s\n", wbxml_errors_string(ret));
|
||||
[self errorWithFormat: @"wbxml2xmlFromContent: failed: %s\n", wbxml_errors_string(ret)];
|
||||
[self _dumpToFile];
|
||||
return nil;
|
||||
}
|
||||
@@ -114,7 +115,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
if (ret != WBXML_OK)
|
||||
{
|
||||
NSLog(@"xml2wbxmlFromContent: failed: %s\n", wbxml_errors_string(ret));
|
||||
[self logWithFormat: @"xml2wbxmlFromContent: failed: %s\n", wbxml_errors_string(ret)];
|
||||
[self _dumpToFile];
|
||||
return nil;
|
||||
}
|
||||
@@ -129,7 +130,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
if (ret != WBXML_OK)
|
||||
{
|
||||
NSLog(@"xml2wbxmlFromContent: failed: %s\n", wbxml_errors_string(ret));
|
||||
[self errorWithFormat: @"xml2wbxmlFromContent: failed: %s\n", wbxml_errors_string(ret)];
|
||||
[self _dumpToFile];
|
||||
free(wbxml);
|
||||
wbxml_conv_xml2wbxml_destroy(conv);
|
||||
|
||||
@@ -33,6 +33,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
#include <Foundation/NSCalendarDate.h>
|
||||
#include <Foundation/NSData.h>
|
||||
#include <Foundation/NSDate.h>
|
||||
#include <Foundation/NSTimeZone.h>
|
||||
|
||||
#include <SOGo/NSString+Utilities.h>
|
||||
#include <SOGo/NSData+Crypto.h>
|
||||
@@ -127,13 +128,17 @@ static NSArray *easCommandParameters = nil;
|
||||
//
|
||||
- (NSCalendarDate *) calendarDate
|
||||
{
|
||||
NSString *s;
|
||||
id o;
|
||||
|
||||
o = [NSCalendarDate dateWithString: self calendarFormat: @"%Y%m%dT%H%M%SZ"];
|
||||
// We force parsing in the GMT timezone. If we don't do that, the date will be parsed
|
||||
// in the default timezone.
|
||||
s = [NSString stringWithFormat: @"%@ GMT", self];
|
||||
o = [NSCalendarDate dateWithString: s calendarFormat: @"%Y%m%dT%H%M%SZ %Z"];
|
||||
|
||||
if (!o)
|
||||
o = [NSCalendarDate dateWithString: self calendarFormat: @"%Y-%m-%dT%H:%M:%S.%FZ"];
|
||||
|
||||
o = [NSCalendarDate dateWithString: s calendarFormat: @"%Y-%m-%dT%H:%M:%S.%FZ %Z"];
|
||||
|
||||
return o;
|
||||
}
|
||||
|
||||
|
||||
+1
-1
@@ -4,7 +4,7 @@ to negotiate the fees associated to your user base.
|
||||
|
||||
To contact Microsoft, please visit:
|
||||
|
||||
http://www.microsoft.com/en-us/legal/intellectualproperty/IPLicensing/Programs/exchangeactivesyncprotocol.aspx
|
||||
http://www.microsoft.com/en-us/legal/intellectualproperty/
|
||||
|
||||
and send an email to iplicreq@microsoft.com
|
||||
|
||||
|
||||
@@ -57,6 +57,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
#import <NGCards/NGVCard.h>
|
||||
|
||||
#import <NGExtensions/NSCalendarDate+misc.h>
|
||||
#import <NGExtensions/NSObject+Logs.h>
|
||||
#import <NGExtensions/NSString+misc.h>
|
||||
|
||||
#import <NGImap4/NSString+Imap4.h>
|
||||
@@ -287,9 +288,8 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
case ActiveSyncMailFolder:
|
||||
default:
|
||||
{
|
||||
// FIXME
|
||||
//continue;
|
||||
NSLog(@"BLARG!");
|
||||
// FIXME - what to do?
|
||||
[self errorWithFormat: @"Fatal error occured - tried to call -processSyncAddCommand: ... on a mail folder. We abort."];
|
||||
abort();
|
||||
}
|
||||
}
|
||||
@@ -531,6 +531,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
- (void) processSyncGetChanges: (id <DOMElement>) theDocumentElement
|
||||
inCollection: (id) theCollection
|
||||
withWindowSize: (unsigned int) theWindowSize
|
||||
withMaxSyncResponseSize: (unsigned int) theMaxSyncResponseSize
|
||||
withSyncKey: (NSString *) theSyncKey
|
||||
withFolderType: (SOGoMicrosoftActiveSyncFolderType) theFolderType
|
||||
withFilterType: (NSCalendarDate *) theFilterType
|
||||
@@ -562,7 +563,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
dateCache = [folderMetadata objectForKey: @"DateCache"];
|
||||
|
||||
if ((theFolderType == ActiveSyncMailFolder || theFolderType == ActiveSyncEventFolder || theFolderType == ActiveSyncTaskFolder) &&
|
||||
!([folderMetadata objectForKey: @"MoreAvailable"]) && // previous sync operation reached the windowSize
|
||||
!([folderMetadata objectForKey: @"MoreAvailable"]) && // previous sync operation reached the windowSize or maximumSyncReponseSize
|
||||
!([theSyncKey isEqualToString: @"-1"]) && // new sync operation
|
||||
theFilterType)
|
||||
{
|
||||
@@ -590,7 +591,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
softdelete_count++;
|
||||
}
|
||||
|
||||
if (softdelete_count >= theWindowSize)
|
||||
if (softdelete_count >= theWindowSize || (theMaxSyncResponseSize > 0 && [s length] >= theMaxSyncResponseSize))
|
||||
{
|
||||
[folderMetadata setObject: [NSNumber numberWithBool: YES] forKey: @"MoreAvailable"];
|
||||
[self _setFolderMetadata: folderMetadata forKey: [self _getNameInCache: theCollection withType: theFolderType]];
|
||||
@@ -653,7 +654,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
pool = [[NSAutoreleasePool alloc] init];
|
||||
|
||||
// Check for the WindowSize and slice accordingly
|
||||
if (return_count >= theWindowSize)
|
||||
if (return_count >= theWindowSize || (theMaxSyncResponseSize > 0 && [s length] >= theMaxSyncResponseSize))
|
||||
{
|
||||
more_available = YES;
|
||||
|
||||
@@ -668,7 +669,10 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
deleted = [[component objectForKey: @"c_deleted"] intValue];
|
||||
|
||||
if (!deleted && ![[component objectForKey: @"c_component"] isEqualToString: component_name])
|
||||
continue;
|
||||
{
|
||||
DESTROY(pool);
|
||||
continue;
|
||||
}
|
||||
|
||||
uid = [[component objectForKey: @"c_name"] sanitizedServerIdWithType: theFolderType];
|
||||
|
||||
@@ -692,10 +696,13 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
if (![syncCache objectForKey: uid])
|
||||
updated = NO;
|
||||
else if ([[component objectForKey: @"c_lastmodified"] intValue] == [[syncCache objectForKey: uid] intValue])
|
||||
continue;
|
||||
{
|
||||
DESTROY(pool);
|
||||
continue;
|
||||
}
|
||||
|
||||
return_count++;
|
||||
|
||||
|
||||
sogoObject = [theCollection lookupName: [uid sanitizedServerIdWithType: theFolderType]
|
||||
inContext: context
|
||||
acquire: 0];
|
||||
@@ -705,7 +712,6 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
else
|
||||
componentObject = [sogoObject component: NO secure: NO];
|
||||
|
||||
|
||||
//
|
||||
// We do NOT synchronize NEW events that are in fact, invitations
|
||||
// to events. This is due to the fact that Outlook 2013 creates
|
||||
@@ -727,22 +733,34 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
partstat = [attendee participationStatus];
|
||||
|
||||
if (partstat == iCalPersonPartStatNeedsAction)
|
||||
continue;
|
||||
{
|
||||
DESTROY(pool);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
[syncCache setObject: [component objectForKey: @"c_lastmodified"] forKey: uid];
|
||||
|
||||
// No need to set dateCache for Contacts
|
||||
if ((theFolderType == ActiveSyncEventFolder || theFolderType == ActiveSyncTaskFolder))
|
||||
{
|
||||
NSCalendarDate *d;
|
||||
|
||||
if ([[component objectForKey: @"c_cycleenddate"] intValue])
|
||||
d = [NSCalendarDate dateWithTimeIntervalSince1970: [[component objectForKey: @"c_cycleenddate"] intValue]];
|
||||
else if ([[component objectForKey: @"c_enddate"] intValue])
|
||||
d = [NSCalendarDate dateWithTimeIntervalSince1970: [[component objectForKey: @"c_enddate"] intValue]];
|
||||
else
|
||||
d = [NSCalendarDate distantFuture];
|
||||
|
||||
[dateCache setObject: d forKey: uid];
|
||||
}
|
||||
|
||||
if (updated)
|
||||
[s appendString: @"<Change xmlns=\"AirSync:\">"];
|
||||
else
|
||||
{
|
||||
// no need to set dateCache for Contacts
|
||||
if ((theFolderType == ActiveSyncEventFolder || theFolderType == ActiveSyncTaskFolder))
|
||||
[dateCache setObject: [componentObject startDate] ? [componentObject startDate] : [NSCalendarDate date] forKey: uid]; // FIXME: need to set proper date for recurring events - softDelete
|
||||
|
||||
[s appendString: @"<Add xmlns=\"AirSync:\">"];
|
||||
}
|
||||
|
||||
[s appendFormat: @"<ServerId xmlns=\"AirSync:\">%@</ServerId>", uid];
|
||||
[s appendString: @"<ApplicationData xmlns=\"AirSync:\">"];
|
||||
@@ -757,10 +775,10 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
[s appendString: @"</Add>"];
|
||||
|
||||
return_count++;
|
||||
|
||||
DESTROY(pool);
|
||||
}
|
||||
} // for ...
|
||||
|
||||
DESTROY(pool);
|
||||
} // for (i = 0; i < max; i++) ...
|
||||
|
||||
if (more_available)
|
||||
{
|
||||
@@ -791,7 +809,6 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
int j, k, return_count;
|
||||
BOOL found_in_cache;
|
||||
|
||||
|
||||
allMessages = [theCollection syncTokenFieldsWithProperties: nil matchingSyncToken: theSyncKey fromDate: theFilterType];
|
||||
max = [allMessages count];
|
||||
|
||||
@@ -829,7 +846,6 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
else
|
||||
found_in_cache = NO;
|
||||
|
||||
|
||||
if (found_in_cache)
|
||||
k = j+1;
|
||||
else
|
||||
@@ -847,7 +863,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
pool = [[NSAutoreleasePool alloc] init];
|
||||
|
||||
// Check for the WindowSize and slice accordingly
|
||||
if (return_count >= theWindowSize)
|
||||
if (return_count >= theWindowSize || (theMaxSyncResponseSize > 0 && [s length] >= theMaxSyncResponseSize))
|
||||
{
|
||||
NSString *lastSequence;
|
||||
more_available = YES;
|
||||
@@ -1003,7 +1019,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
if ([[element tagName] isEqualToString: @"Add"])
|
||||
{
|
||||
// Add
|
||||
[self processSyncAddCommand: aCommand
|
||||
[self processSyncAddCommand: element
|
||||
inCollection: theCollection
|
||||
withType: theFolderType
|
||||
inBuffer: theBuffer];
|
||||
@@ -1012,7 +1028,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
else if ([[element tagName] isEqualToString: @"Change"])
|
||||
{
|
||||
// Change
|
||||
[self processSyncChangeCommand: aCommand
|
||||
[self processSyncChangeCommand: element
|
||||
inCollection: theCollection
|
||||
withType: theFolderType
|
||||
inBuffer: theBuffer];
|
||||
@@ -1021,7 +1037,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
else if ([[element tagName] isEqualToString: @"Delete"])
|
||||
{
|
||||
// Delete
|
||||
[self processSyncDeleteCommand: aCommand
|
||||
[self processSyncDeleteCommand: element
|
||||
inCollection: theCollection
|
||||
withType: theFolderType
|
||||
inBuffer: theBuffer];
|
||||
@@ -1030,7 +1046,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
else if ([[element tagName] isEqualToString: @"Fetch"])
|
||||
{
|
||||
// Fetch
|
||||
[self processSyncFetchCommand: aCommand
|
||||
[self processSyncFetchCommand: element
|
||||
inCollection: theCollection
|
||||
withType: theFolderType
|
||||
inBuffer: theBuffer];
|
||||
@@ -1047,14 +1063,16 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
- (void) processSyncCollection: (id <DOMElement>) theDocumentElement
|
||||
inBuffer: (NSMutableString *) theBuffer
|
||||
changeDetected: (BOOL *) changeDetected
|
||||
maxSyncResponseSize: (int) theMaxSyncResponseSize
|
||||
{
|
||||
NSString *collectionId, *realCollectionId, *syncKey, *davCollectionTag, *bodyPreferenceType, *lastServerKey;
|
||||
NSString *collectionId, *realCollectionId, *syncKey, *davCollectionTag, *bodyPreferenceType, *lastServerKey, *syncKeyInCache;
|
||||
SOGoMicrosoftActiveSyncFolderType folderType;
|
||||
id collection, value;
|
||||
|
||||
NSMutableString *changeBuffer, *commandsBuffer;
|
||||
BOOL getChanges, first_sync;
|
||||
unsigned int windowSize, v, status;
|
||||
NSMutableDictionary *folderMetadata;
|
||||
|
||||
changeBuffer = [NSMutableString string];
|
||||
commandsBuffer = [NSMutableString string];
|
||||
@@ -1076,7 +1094,6 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
//[theBuffer appendFormat: @"<CollectionId>%@</CollectionId>", collectionId];
|
||||
//[theBuffer appendFormat: @"<Status>%d</Status>", 8];
|
||||
//[theBuffer appendString: @"</Collection>"];
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -1139,6 +1156,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
[self processSyncGetChanges: theDocumentElement
|
||||
inCollection: collection
|
||||
withWindowSize: windowSize
|
||||
withMaxSyncResponseSize: theMaxSyncResponseSize
|
||||
withSyncKey: syncKey
|
||||
withFolderType: folderType
|
||||
withFilterType: [NSCalendarDate dateFromFilterType: [[(id)[theDocumentElement getElementsByTagName: @"FilterType"] lastObject] textValue]]
|
||||
@@ -1169,6 +1187,8 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
[commandsBuffer appendFormat: @"<Responses>%@</Responses>", s];
|
||||
}
|
||||
|
||||
folderMetadata = [self _folderMetadataForKey: [self _getNameInCache: collection withType: folderType]];
|
||||
|
||||
// If we got any changes or if we have applied any commands
|
||||
// let's regenerate our SyncKey based on the collection tag.
|
||||
if ([changeBuffer length] || [commandsBuffer length])
|
||||
@@ -1179,7 +1199,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
{
|
||||
// Use the SyncKey saved by processSyncGetChanges - if processSyncGetChanges is not called (because of getChanges=false)
|
||||
// SyncKey has the value of the previous sync operation.
|
||||
davCollectionTag = [[self _folderMetadataForKey: [self _getNameInCache: collection withType: folderType]] objectForKey: @"SyncKey"];
|
||||
davCollectionTag = [folderMetadata objectForKey: @"SyncKey"];
|
||||
|
||||
if (!davCollectionTag)
|
||||
davCollectionTag = [collection davCollectionTag];
|
||||
@@ -1189,8 +1209,13 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
}
|
||||
else
|
||||
{
|
||||
if (folderType == ActiveSyncMailFolder && [syncKey isEqualToString: @"-1"])
|
||||
davCollectionTag = [collection davCollectionTag];
|
||||
// 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]))
|
||||
{
|
||||
davCollectionTag = syncKeyInCache;
|
||||
*changeDetected = YES;
|
||||
}
|
||||
}
|
||||
|
||||
// Generate the response buffer
|
||||
@@ -1211,7 +1236,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
// MoreAvailable breaks Windows Mobile devices if not between <Status> and <Commands>
|
||||
// https://social.msdn.microsoft.com/Forums/en-US/040b254e-f47e-4cc1-a397-6d8393cdb819/airsyncmoreavailable-breaks-windows-mobile-devices-what-am-i-doing-wrong?forum=os_exchangeprotocols
|
||||
if ([[self _folderMetadataForKey: [self _getNameInCache: collection withType: folderType]] objectForKey: @"MoreAvailable"])
|
||||
if ([folderMetadata objectForKey: @"MoreAvailable"])
|
||||
[theBuffer appendString: @"<MoreAvailable/>"];
|
||||
|
||||
[theBuffer appendString: commandsBuffer];
|
||||
@@ -1324,21 +1349,42 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
NSArray *allCollections;
|
||||
NSData *d;
|
||||
|
||||
int i, j, defaultInterval, heartbeatInterval, internalInterval;
|
||||
int i, j, defaultInterval, heartbeatInterval, internalInterval, maxSyncResponseSize;
|
||||
BOOL changeDetected;
|
||||
|
||||
changeDetected = NO;
|
||||
|
||||
maxSyncResponseSize = [[SOGoSystemDefaults sharedSystemDefaults] maximumSyncResponseSize];
|
||||
|
||||
// We initialize our output buffer
|
||||
output = [[NSMutableString alloc] init];
|
||||
|
||||
[output appendString: @"<?xml version=\"1.0\" encoding=\"utf-8\"?>"];
|
||||
[output appendString: @"<!DOCTYPE ActiveSync PUBLIC \"-//MICROSOFT//DTD ActiveSync//EN\" \"http://www.microsoft.com/\">"];
|
||||
[output appendString: @"<Sync xmlns=\"AirSync:\">"];
|
||||
|
||||
//
|
||||
// We don't support yet empty Sync requests. See: http://msdn.microsoft.com/en-us/library/ee203280(v=exchg.80).aspx
|
||||
// We return '13' - see http://msdn.microsoft.com/en-us/library/gg675457(v=exchg.80).aspx
|
||||
//
|
||||
if (!theDocumentElement || [[(id)[theDocumentElement getElementsByTagName: @"Partial"] lastObject] textValue])
|
||||
{
|
||||
[output appendString: @"<Status>13</Status>"];
|
||||
[output appendString: @"</Sync>"];
|
||||
d = [[output dataUsingEncoding: NSUTF8StringEncoding] xml2wbxml];
|
||||
[theResponse setContent: d];
|
||||
return;
|
||||
}
|
||||
|
||||
defaults = [SOGoSystemDefaults sharedSystemDefaults];
|
||||
heartbeatInterval = [[[(id)[theDocumentElement getElementsByTagName: @"HeartbeatInterval"] lastObject] textValue] intValue];
|
||||
defaultInterval = [defaults maximumSyncInterval];
|
||||
internalInterval = [defaults internalSyncInterval];
|
||||
|
||||
// If the request doesn't contain "HeartbeatInterval" there is no reason to delay the response.
|
||||
if (heartbeatInterval == 0)
|
||||
heartbeatInterval = internalInterval = 1;
|
||||
|
||||
// We check to see if our heartbeat interval falls into the supported ranges.
|
||||
if (heartbeatInterval > defaultInterval || heartbeatInterval < 1)
|
||||
{
|
||||
@@ -1355,7 +1401,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
allCollections = (id)[theDocumentElement getElementsByTagName: @"Collection"];
|
||||
|
||||
// We enter our loop detection change
|
||||
for (i = 0; i < (defaultInterval/internalInterval); i++)
|
||||
for (i = 0; i < (heartbeatInterval/internalInterval); i++)
|
||||
{
|
||||
s = [NSMutableString string];
|
||||
|
||||
@@ -1363,36 +1409,46 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
{
|
||||
aCollection = [allCollections objectAtIndex: j];
|
||||
|
||||
[self processSyncCollection: aCollection inBuffer: s changeDetected: &changeDetected];
|
||||
[self processSyncCollection: aCollection
|
||||
inBuffer: s
|
||||
changeDetected: &changeDetected
|
||||
maxSyncResponseSize: maxSyncResponseSize];
|
||||
}
|
||||
|
||||
if (changeDetected)
|
||||
{
|
||||
NSLog(@"Change detected, we push the content.");
|
||||
[self logWithFormat: @"Change detected, we push the content."];
|
||||
break;
|
||||
}
|
||||
else if (heartbeatInterval > 1)
|
||||
{
|
||||
[self logWithFormat: @"Sleeping %d seconds while detecting changes...", internalInterval];
|
||||
sleep(internalInterval);
|
||||
}
|
||||
else
|
||||
{
|
||||
NSLog(@"Sleeping %d seconds while detecting changes...", internalInterval);
|
||||
sleep(internalInterval);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// We always return the last generated response.
|
||||
// If we only return <Sync><Collections/></Sync>,
|
||||
// iOS powered devices will simply crash.
|
||||
[output appendString: s];
|
||||
// Only send a response if there are changes otherwise send an empty response.
|
||||
if (changeDetected)
|
||||
{
|
||||
// We always return the last generated response.
|
||||
// If we only return <Sync><Collections/></Sync>,
|
||||
// iOS powered devices will simply crash.
|
||||
[output appendString: s];
|
||||
|
||||
[output appendString: @"</Collections></Sync>"];
|
||||
[output appendString: @"</Collections></Sync>"];
|
||||
|
||||
d = [output dataUsingEncoding: NSUTF8StringEncoding];
|
||||
d = [d xml2wbxml];
|
||||
[theResponse setContent: d];
|
||||
}
|
||||
|
||||
// Avoid overloading the autorelease pool here, as Sync command can
|
||||
// generate fairly large responses.
|
||||
d = [output dataUsingEncoding: NSUTF8StringEncoding];
|
||||
RELEASE(output);
|
||||
|
||||
d = [d xml2wbxml];
|
||||
|
||||
[theResponse setContent: d];
|
||||
}
|
||||
|
||||
@end
|
||||
@@ -35,6 +35,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
#import <Foundation/NSProcessInfo.h>
|
||||
#import <Foundation/NSTimeZone.h>
|
||||
#import <Foundation/NSURL.h>
|
||||
#import <Foundation/NSValue.h>
|
||||
|
||||
#import <NGObjWeb/NSException+HTTP.h>
|
||||
#import <NGObjWeb/SoPermissions.h>
|
||||
@@ -58,6 +59,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
#import <NGExtensions/NSCalendarDate+misc.h>
|
||||
#import <NGExtensions/NGCalendarDateRange.h>
|
||||
#import <NGExtensions/NGHashMap.h>
|
||||
#import <NGExtensions/NSObject+Logs.h>
|
||||
#import <NGExtensions/NSString+misc.h>
|
||||
|
||||
#import <NGImap4/NGImap4Client.h>
|
||||
@@ -793,10 +795,6 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
{
|
||||
cKey = [allKeys objectAtIndex: i];
|
||||
|
||||
// ignore invalid folder in cache caused by fs code bugs
|
||||
if ([cKey isEqualToString:@"(null)"])
|
||||
continue;
|
||||
|
||||
// if a cache entry is not found in imapGUIDs its either an imap which has been deleted or its an other folder type which can be checked via lookupName.
|
||||
if (![imapGUIDs allKeysForObject: cKey])
|
||||
{
|
||||
@@ -807,7 +805,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
[o setTableUrl: [self folderTableURL]];
|
||||
[o reloadIfNeeded];
|
||||
|
||||
if ([cKey hasPrefix: @"folder"])
|
||||
if ([cKey hasPrefix: @"folder"] || [cKey isEqualToString:@"(null)"])
|
||||
{
|
||||
[commands appendFormat: @"<Delete><ServerId>%@</ServerId></Delete>", [[NSString stringWithFormat: @"mail/%@", [cKey substringFromIndex: 6]] stringByEscapingURL]] ;
|
||||
command_count++;
|
||||
@@ -849,6 +847,11 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
folderMetadata = [allFoldersMetadata objectAtIndex: i];
|
||||
|
||||
nameInCache = [NSString stringWithFormat: @"folder%@", [[folderMetadata objectForKey: @"path"] substringFromIndex: 1]];
|
||||
|
||||
// we have no guid - ignore the folder
|
||||
if (![imapGUIDs objectForKey: nameInCache])
|
||||
continue;
|
||||
|
||||
serverId = [NSString stringWithFormat: @"mail/%@", [[imapGUIDs objectForKey: nameInCache] substringFromIndex: 6]];
|
||||
name = [folderMetadata objectForKey: @"displayName"];
|
||||
|
||||
@@ -1214,71 +1217,136 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
{
|
||||
NSString *fileReference, *realCollectionId;
|
||||
NSMutableString *s;
|
||||
NSArray *fetchRequests;
|
||||
id aFetch;
|
||||
int i;
|
||||
|
||||
SOGoMicrosoftActiveSyncFolderType folderType;
|
||||
|
||||
fileReference = [[[(id)[theDocumentElement getElementsByTagName: @"FileReference"] lastObject] textValue] stringByUnescapingURL];
|
||||
s = [NSMutableString string];
|
||||
|
||||
realCollectionId = [fileReference realCollectionIdWithFolderType: &folderType];
|
||||
[s appendString: @"<?xml version=\"1.0\" encoding=\"utf-8\"?>"];
|
||||
[s appendString: @"<!DOCTYPE ActiveSync PUBLIC \"-//MICROSOFT//DTD ActiveSync//EN\" \"http://www.microsoft.com/\">"];
|
||||
[s appendString: @"<ItemOperations xmlns=\"ItemOperations:\">"];
|
||||
[s appendString: @"<Status>1</Status>"];
|
||||
[s appendString: @"<Response>"];
|
||||
|
||||
fetchRequests = (id)[theDocumentElement getElementsByTagName: @"Fetch"];
|
||||
|
||||
if (folderType == ActiveSyncMailFolder)
|
||||
if ([fetchRequests count])
|
||||
{
|
||||
id currentFolder, currentCollection, currentBodyPart;
|
||||
NSString *folderName, *messageName, *pathToPart;
|
||||
SOGoMailAccounts *accountsFolder;
|
||||
SOGoUserFolder *userFolder;
|
||||
SOGoMailObject *mailObject;
|
||||
NSMutableData *bytes, *parts;
|
||||
NSMutableArray *partLength;
|
||||
NSData *d;
|
||||
|
||||
NSRange r1, r2;
|
||||
bytes = [NSMutableData data];
|
||||
parts = [NSMutableData data];
|
||||
partLength = [NSMutableArray array];
|
||||
|
||||
r1 = [realCollectionId rangeOfString: @"/"];
|
||||
r2 = [realCollectionId rangeOfString: @"/" options: 0 range: NSMakeRange(NSMaxRange(r1)+1, [realCollectionId length]-NSMaxRange(r1)-1)];
|
||||
for (i = 0; i < [fetchRequests count]; i++)
|
||||
{
|
||||
aFetch = [fetchRequests objectAtIndex: i];
|
||||
fileReference = [[[(id)[aFetch getElementsByTagName: @"FileReference"] lastObject] textValue] stringByUnescapingURL];
|
||||
realCollectionId = [fileReference realCollectionIdWithFolderType: &folderType];
|
||||
|
||||
if (folderType == ActiveSyncMailFolder)
|
||||
{
|
||||
id currentFolder, currentCollection, currentBodyPart;
|
||||
NSString *folderName, *messageName, *pathToPart;
|
||||
SOGoMailAccounts *accountsFolder;
|
||||
SOGoUserFolder *userFolder;
|
||||
SOGoMailObject *mailObject;
|
||||
|
||||
NSRange r1, r2;
|
||||
|
||||
r1 = [realCollectionId rangeOfString: @"/"];
|
||||
r2 = [realCollectionId rangeOfString: @"/" options: 0 range: NSMakeRange(NSMaxRange(r1)+1, [realCollectionId length]-NSMaxRange(r1)-1)];
|
||||
|
||||
folderName = [realCollectionId substringToIndex: r1.location];
|
||||
messageName = [realCollectionId substringWithRange: NSMakeRange(NSMaxRange(r1), r2.location-r1.location-1)];
|
||||
pathToPart = [realCollectionId substringFromIndex: r2.location+1];
|
||||
|
||||
userFolder = [[context activeUser] homeFolderInContext: context];
|
||||
accountsFolder = [userFolder lookupName: @"Mail" inContext: context acquire: NO];
|
||||
currentFolder = [accountsFolder lookupName: @"0" inContext: context acquire: NO];
|
||||
folderName = [realCollectionId substringToIndex: r1.location];
|
||||
messageName = [realCollectionId substringWithRange: NSMakeRange(NSMaxRange(r1), r2.location-r1.location-1)];
|
||||
pathToPart = [realCollectionId substringFromIndex: r2.location+1];
|
||||
|
||||
currentCollection = [currentFolder lookupName: [NSString stringWithFormat: @"folder%@", folderName]
|
||||
inContext: context
|
||||
acquire: NO];
|
||||
|
||||
mailObject = [currentCollection lookupName: messageName inContext: context acquire: NO];
|
||||
currentBodyPart = [mailObject lookupImap4BodyPartKey: pathToPart inContext: context];
|
||||
userFolder = [[context activeUser] homeFolderInContext: context];
|
||||
accountsFolder = [userFolder lookupName: @"Mail" inContext: context acquire: NO];
|
||||
currentFolder = [accountsFolder lookupName: @"0" inContext: context acquire: NO];
|
||||
|
||||
currentCollection = [currentFolder lookupName: [NSString stringWithFormat: @"folder%@", folderName]
|
||||
inContext: context
|
||||
acquire: NO];
|
||||
|
||||
s = [NSMutableString string];
|
||||
[s appendString: @"<?xml version=\"1.0\" encoding=\"utf-8\"?>"];
|
||||
[s appendString: @"<!DOCTYPE ActiveSync PUBLIC \"-//MICROSOFT//DTD ActiveSync//EN\" \"http://www.microsoft.com/\">"];
|
||||
[s appendString: @"<ItemOperations xmlns=\"ItemOperations:\">"];
|
||||
[s appendString: @"<Status>1</Status>"];
|
||||
[s appendString: @"<Response>"];
|
||||
mailObject = [currentCollection lookupName: messageName inContext: context acquire: NO];
|
||||
currentBodyPart = [mailObject lookupImap4BodyPartKey: pathToPart inContext: context];
|
||||
|
||||
[s appendString: @"<Fetch>"];
|
||||
[s appendString: @"<Status>1</Status>"];
|
||||
[s appendFormat: @"<FileReference xmlns=\"AirSyncBase:\">%@</FileReference>", [fileReference stringByEscapingURL]];
|
||||
[s appendString: @"<Properties>"];
|
||||
[s appendString: @"<Fetch>"];
|
||||
[s appendString: @"<Status>1</Status>"];
|
||||
[s appendFormat: @"<FileReference xmlns=\"AirSyncBase:\">%@</FileReference>", [fileReference stringByEscapingURL]];
|
||||
[s appendString: @"<Properties>"];
|
||||
|
||||
[s appendFormat: @"<ContentType xmlns=\"AirSyncBase:\">%@/%@</ContentType>", [[currentBodyPart partInfo] objectForKey: @"type"], [[currentBodyPart partInfo] objectForKey: @"subtype"]];
|
||||
[s appendFormat: @"<Data>%@</Data>", [[currentBodyPart fetchBLOB] activeSyncRepresentationInContext: context]];
|
||||
[s appendFormat: @"<ContentType xmlns=\"AirSyncBase:\">%@/%@</ContentType>", [[currentBodyPart partInfo] objectForKey: @"type"], [[currentBodyPart partInfo] objectForKey: @"subtype"]];
|
||||
|
||||
[s appendString: @"</Properties>"];
|
||||
[s appendString: @"</Fetch>"];
|
||||
if ([[theResponse headerForKey: @"Content-Type"] isEqualToString:@"application/vnd.ms-sync.multipart"])
|
||||
{
|
||||
[s appendFormat: @"<Part>%d</Part>", i+1];
|
||||
[partLength addObject: [NSNumber numberWithInteger: [[currentBodyPart fetchBLOB] length]]];
|
||||
[parts appendData:[currentBodyPart fetchBLOB]];
|
||||
}
|
||||
else
|
||||
{
|
||||
[s appendFormat: @"<Range>0-%d</Range>", [[[currentBodyPart fetchBLOB] activeSyncRepresentationInContext: context] length]-1];
|
||||
[s appendFormat: @"<Data>%@</Data>", [[currentBodyPart fetchBLOB] activeSyncRepresentationInContext: context]];
|
||||
}
|
||||
|
||||
[s appendString: @"</Properties>"];
|
||||
[s appendString: @"</Fetch>"];
|
||||
}
|
||||
else
|
||||
{
|
||||
[theResponse setStatus: 500];
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
[s appendString: @"</Response>"];
|
||||
[s appendString: @"</ItemOperations>"];
|
||||
|
||||
|
||||
d = [[s dataUsingEncoding: NSUTF8StringEncoding] xml2wbxml];
|
||||
[theResponse setContent: d];
|
||||
}
|
||||
else
|
||||
{
|
||||
[theResponse setStatus: 500];
|
||||
|
||||
if ([[theResponse headerForKey: @"Content-Type"] isEqualToString:@"application/vnd.ms-sync.multipart"])
|
||||
{
|
||||
uint32_t PartCount;
|
||||
uint32_t Offset;
|
||||
uint32_t Len;
|
||||
|
||||
// 2.2.2.9.1.1 - MultiPartResponse -- http://msdn.microsoft.com/en-us/library/jj663270%28v=exchg.80%29.aspx
|
||||
PartCount = [partLength count] + 1;
|
||||
Offset = ((PartCount) * 2) * 4 + 4;
|
||||
Len = [d length];
|
||||
|
||||
[bytes appendBytes: &PartCount length: 4];
|
||||
[bytes appendBytes: &Offset length: 4];
|
||||
[bytes appendBytes: &Len length: 4];
|
||||
|
||||
// 2.2.2.9.1.1.1 - PartMetaData -- http://msdn.microsoft.com/en-us/library/jj663267%28v=exchg.80%29.aspx
|
||||
for (i = 0; i < [fetchRequests count]; i++)
|
||||
{
|
||||
Offset = Offset + Len;
|
||||
Len = [[partLength objectAtIndex:i] intValue];
|
||||
[bytes appendBytes: &Offset length: 4];
|
||||
[bytes appendBytes: &Len length: 4];
|
||||
}
|
||||
|
||||
// First part - webxml
|
||||
[bytes appendData: d];
|
||||
|
||||
// Subsequent parts - requested data
|
||||
[bytes appendData: parts];
|
||||
|
||||
[theResponse setContent: bytes];
|
||||
}
|
||||
else
|
||||
{
|
||||
[theResponse setContent: d];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1738,30 +1806,24 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
// We build the list of folders to "ping". When the payload is empty, we use the list
|
||||
// of "cached" folders.
|
||||
allCollections = (id)[theDocumentElement getElementsByTagName: @"Folders"];
|
||||
allCollections = (id)[theDocumentElement getElementsByTagName: @"Folder"];
|
||||
allFoldersID = [NSMutableArray array];
|
||||
|
||||
if (![allCollections count])
|
||||
{
|
||||
SOGoMailAccounts *accountsFolder;
|
||||
SOGoMailAccount *accountFolder;
|
||||
SOGoUserFolder *userFolder;
|
||||
NSArray *allValues;
|
||||
// We received an empty Ping request. Return status '3' to ask client to resend the request with complete body.
|
||||
s = [NSMutableString string];
|
||||
[s appendString: @"<?xml version=\"1.0\" encoding=\"utf-8\"?>"];
|
||||
[s appendString: @"<!DOCTYPE ActiveSync PUBLIC \"-//MICROSOFT//DTD ActiveSync//EN\" \"http://www.microsoft.com/\">"];
|
||||
[s appendString: @"<Ping xmlns=\"Ping:\">"];
|
||||
[s appendString: @"<Status>3</Status>"];
|
||||
[s appendString: @"</Ping>"];
|
||||
|
||||
userFolder = [[context activeUser] homeFolderInContext: context];
|
||||
accountsFolder = [userFolder lookupName: @"Mail" inContext: context acquire: NO];
|
||||
accountFolder = [accountsFolder lookupName: @"0" inContext: context acquire: NO];
|
||||
d = [[s dataUsingEncoding: NSUTF8StringEncoding] xml2wbxml];
|
||||
|
||||
allValues = [[accountFolder imapFolderGUIDs] allValues];
|
||||
|
||||
for (i = 0; i < [allValues count]; i++)
|
||||
[allFoldersID addObject: [NSString stringWithFormat: @"mail/%@", [[allValues objectAtIndex: i] substringFromIndex: 6]]];
|
||||
[theResponse setContent: d];
|
||||
|
||||
|
||||
// FIXME: handle multiple GCS collecitons
|
||||
[allFoldersID addObject: @"vcard/personal"];
|
||||
[allFoldersID addObject: @"vevent/personal"];
|
||||
[allFoldersID addObject: @"vtodo/personal"];
|
||||
return;
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -1805,13 +1867,13 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
if ([foldersWithChanges count])
|
||||
{
|
||||
NSLog(@"Change detected, we push the content.");
|
||||
[self logWithFormat: @"Change detected, we push the content."];
|
||||
status = 2;
|
||||
break;
|
||||
}
|
||||
else
|
||||
{
|
||||
NSLog(@"Sleeping %d seconds while detecting changes...", internalInterval);
|
||||
[self logWithFormat: @"Sleeping %d seconds while detecting changes...", internalInterval];
|
||||
sleep(internalInterval);
|
||||
}
|
||||
}
|
||||
@@ -2158,6 +2220,11 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
NGMimeMessage *message;
|
||||
NSException *error;
|
||||
NSData *data;
|
||||
NGMutableHashMap *map;
|
||||
NGMimeMessage *messageToSend;
|
||||
NGMimeMessageGenerator *generator;
|
||||
NSDictionary *identity;
|
||||
NSString *fullName, *email;
|
||||
|
||||
// We get the mail's data
|
||||
data = [[[[(id)[theDocumentElement getElementsByTagName: @"MIME"] lastObject] textValue] stringByDecodingBase64] dataUsingEncoding: NSUTF8StringEncoding];
|
||||
@@ -2166,6 +2233,24 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
parser = [[NGMimeMessageParser alloc] init];
|
||||
message = [parser parsePartFromData: data];
|
||||
RELEASE(parser);
|
||||
|
||||
map = [NGHashMap hashMapWithDictionary: [message headers]];
|
||||
|
||||
identity = [[context activeUser] primaryIdentity];
|
||||
|
||||
fullName = [identity objectForKey: @"fullName"];
|
||||
email = [identity objectForKey: @"email"];
|
||||
if ([fullName length])
|
||||
[map setObject: [NSString stringWithFormat: @"%@ <%@>", fullName, email] forKey: @"from"];
|
||||
else
|
||||
[map setObject: email forKey: @"from"];
|
||||
|
||||
messageToSend = [[[NGMimeMessage alloc] initWithHeader: map] autorelease];
|
||||
|
||||
[messageToSend setBody: [message body]];
|
||||
|
||||
generator = [[[NGMimeMessageGenerator alloc] init] autorelease];
|
||||
data = [generator generateMimeFromPart: messageToSend];
|
||||
|
||||
error = [self _sendMail: data
|
||||
recipients: [message allRecipients]
|
||||
@@ -2294,6 +2379,8 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
NSException *error;
|
||||
|
||||
id body, bodyFromSmartForward;
|
||||
NSString *fullName, *email;
|
||||
NSDictionary *identity;
|
||||
|
||||
userFolder = [[context activeUser] homeFolderInContext: context];
|
||||
accountsFolder = [userFolder lookupName: @"Mail" inContext: context acquire: NO];
|
||||
@@ -2317,6 +2404,15 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
map = [NGHashMap hashMapWithDictionary: [messageFromSmartForward headers]];
|
||||
[map setObject: @"multipart/mixed" forKey: @"content-type"];
|
||||
|
||||
identity = [[context activeUser] primaryIdentity];
|
||||
|
||||
fullName = [identity objectForKey: @"fullName"];
|
||||
email = [identity objectForKey: @"email"];
|
||||
if ([fullName length])
|
||||
[map setObject: [NSString stringWithFormat: @"%@ <%@>", fullName, email] forKey: @"from"];
|
||||
else
|
||||
[map setObject: email forKey: @"from"];
|
||||
|
||||
messageToSend = [[[NGMimeMessage alloc] initWithHeader: map] autorelease];
|
||||
body = [[[NGMimeMultipartBody alloc] initWithPart: messageToSend] autorelease];
|
||||
|
||||
@@ -2480,8 +2576,12 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
{
|
||||
// We check if it's a Ping command with no body.
|
||||
// See http://msdn.microsoft.com/en-us/library/ee200913(v=exchg.80).aspx for details
|
||||
if ([cmdName caseInsensitiveCompare: @"Ping"] != NSOrderedSame && [cmdName caseInsensitiveCompare: @"GetAttachment"] != NSOrderedSame)
|
||||
return [NSException exceptionWithHTTPStatus: 500];
|
||||
if ([cmdName caseInsensitiveCompare: @"Ping"] != NSOrderedSame && [cmdName caseInsensitiveCompare: @"GetAttachment"] != NSOrderedSame && [cmdName caseInsensitiveCompare: @"Sync"] != NSOrderedSame)
|
||||
{
|
||||
RELEASE(context);
|
||||
RELEASE(pool);
|
||||
return [NSException exceptionWithHTTPStatus: 500];
|
||||
}
|
||||
}
|
||||
|
||||
if (d)
|
||||
@@ -2499,19 +2599,24 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
// Ping or Sync command with empty body
|
||||
cmdName = [NSString stringWithFormat: @"process%@:inResponse:", cmdName];
|
||||
}
|
||||
|
||||
|
||||
aSelector = NSSelectorFromString(cmdName);
|
||||
|
||||
// The -processItemOperations: method will generate a multipart response when Content-Type is application/vnd.ms-sync.multipart
|
||||
if ([[theRequest headerForKey: @"MS-ASAcceptMultiPart"] isEqualToString:@"T"])
|
||||
[theResponse setHeader: @"application/vnd.ms-sync.multipart" forKey: @"Content-Type"];
|
||||
else
|
||||
[theResponse setHeader: @"application/vnd.ms-sync.wbxml" forKey: @"Content-Type"];
|
||||
|
||||
[self performSelector: aSelector withObject: documentElement withObject: theResponse];
|
||||
|
||||
[theResponse setHeader: @"application/vnd.ms-sync.wbxml" forKey: @"Content-Type"];
|
||||
[theResponse setHeader: @"14.1" forKey: @"MS-Server-ActiveSync"];
|
||||
[theResponse setHeader: @"Sync,SendMail,SmartForward,SmartReply,GetAttachment,GetHierarchy,CreateCollection,DeleteCollection,MoveCollection,FolderSync,FolderCreate,FolderDelete,FolderUpdate,MoveItems,GetItemEstimate,MeetingResponse,Search,Settings,Ping,ItemOperations,ResolveRecipients,ValidateCert" forKey: @"MS-ASProtocolCommands"];
|
||||
[theResponse setHeader: @"2.0,2.1,2.5,12.0,12.1,14.0,14.1" forKey: @"MS-ASProtocolVersions"];
|
||||
[theResponse setHeader: @"2.5,12.0,12.1,14.0,14.1" forKey: @"MS-ASProtocolVersions"];
|
||||
|
||||
RELEASE(context);
|
||||
RELEASE(pool);
|
||||
|
||||
|
||||
return nil;
|
||||
}
|
||||
|
||||
|
||||
@@ -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)
|
||||
{
|
||||
@@ -254,6 +275,10 @@ struct GlobalObjectId {
|
||||
charset = @"us-ascii";
|
||||
|
||||
s = [NSString stringWithData: d usingEncodingNamed: charset];
|
||||
|
||||
if (theType == 1 && *theNativeTypeFound == 2)
|
||||
s = [s htmlToText];
|
||||
|
||||
d = [s dataUsingEncoding: NSUTF8StringEncoding];
|
||||
}
|
||||
|
||||
@@ -434,12 +459,12 @@ struct GlobalObjectId {
|
||||
{
|
||||
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)
|
||||
@@ -729,7 +754,13 @@ struct GlobalObjectId {
|
||||
len = [content length];
|
||||
|
||||
[s appendString: @"<Body xmlns=\"AirSyncBase:\">"];
|
||||
[s appendFormat: @"<Type>%d</Type>", preferredBodyType];
|
||||
|
||||
// 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>"];
|
||||
|
||||
@@ -774,6 +805,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 +888,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
|
||||
@@ -55,7 +55,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
[o setUID: theUID];
|
||||
[o setSequence: theSequence];
|
||||
|
||||
return o;
|
||||
return [o autorelease];
|
||||
}
|
||||
|
||||
- (void) dealloc
|
||||
|
||||
@@ -61,7 +61,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
[response setHeader: @"private" forKey: @"Cache-Control"];
|
||||
[response setHeader: @"OPTIONS, POST" forKey: @"Allow"];
|
||||
[response setHeader: @"14.1" forKey: @"MS-Server-ActiveSync"];
|
||||
[response setHeader: @"2.0,2.1,2.5,12.0,12.1,14.0,14.1" forKey: @"MS-ASProtocolVersions"];
|
||||
[response setHeader: @"2.5,12.0,12.1,14.0,14.1" forKey: @"MS-ASProtocolVersions"];
|
||||
[response setHeader: @"Sync,SendMail,SmartForward,SmartReply,GetAttachment,GetHierarchy,CreateCollection,DeleteCollection,MoveCollection,FolderSync,FolderCreate,FolderDelete,FolderUpdate,MoveItems,GetItemEstimate,MeetingResponse,Search,Settings,Ping,ItemOperations,ResolveRecipients,ValidateCert" forKey: @"MS-ASProtocolCommands"];
|
||||
[response setHeader: @"OPTIONS, POST" forKey: @"Public"];
|
||||
}
|
||||
|
||||
@@ -332,7 +332,6 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
iCalTimeZone *tz;
|
||||
id o;
|
||||
|
||||
NSInteger tzOffset;
|
||||
BOOL isAllDay;
|
||||
|
||||
if ((o = [theValues objectForKey: @"UID"]))
|
||||
@@ -384,10 +383,15 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
// Some Windows devices don't send during event updates.
|
||||
//if ((o = [theValues objectForKey: @"TimeZone"]))
|
||||
// {
|
||||
userTimeZone = [[[context activeUser] userDefaults] timeZone];
|
||||
tz = [iCalTimeZone timeZoneForName: [userTimeZone name]];
|
||||
[(iCalCalendar *) parent addTimeZone: tz];
|
||||
//}
|
||||
// }
|
||||
//else
|
||||
{
|
||||
// We haven't received a timezone, let's use the user's timezone
|
||||
// specified in SOGo for now.
|
||||
userTimeZone = [[[context activeUser] userDefaults] timeZone];
|
||||
tz = [iCalTimeZone timeZoneForName: [userTimeZone name]];
|
||||
[(iCalCalendar *) parent addTimeZone: tz];
|
||||
}
|
||||
|
||||
// FIXME: merge with iCalToDo
|
||||
if ((o = [[theValues objectForKey: @"Body"] objectForKey: @"Data"]))
|
||||
@@ -402,21 +406,13 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
start = (iCalDateTime *) [self uniqueChildWithTag: @"dtstart"];
|
||||
[start setTimeZone: tz];
|
||||
|
||||
if (isAllDay)
|
||||
{
|
||||
tzOffset = [userTimeZone secondsFromGMTForDate: o];
|
||||
o = [o dateByAddingYears: 0 months: 0 days: 0
|
||||
hours: 0 minutes: 0
|
||||
seconds: tzOffset];
|
||||
if (isAllDay)
|
||||
{
|
||||
[start setDate: o];
|
||||
[start setTimeZone: nil];
|
||||
}
|
||||
else
|
||||
{
|
||||
tzOffset = [userTimeZone secondsFromGMTForDate: o];
|
||||
o = [o dateByAddingYears: 0 months: 0 days: 0
|
||||
hours: 0 minutes: 0
|
||||
seconds: tzOffset];
|
||||
[start setDateTime: o];
|
||||
}
|
||||
}
|
||||
@@ -429,19 +425,11 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
if (isAllDay)
|
||||
{
|
||||
tzOffset = [userTimeZone secondsFromGMTForDate: o];
|
||||
o = [o dateByAddingYears: 0 months: 0 days: 0
|
||||
hours: 0 minutes: 0
|
||||
seconds: tzOffset];
|
||||
[end setDate: o];
|
||||
[end setTimeZone: nil];
|
||||
}
|
||||
else
|
||||
{
|
||||
tzOffset = [userTimeZone secondsFromGMTForDate: o];
|
||||
o = [o dateByAddingYears: 0 months: 0 days: 0
|
||||
hours: 0 minutes: 0
|
||||
seconds: tzOffset];
|
||||
[end setDateTime: o];
|
||||
}
|
||||
}
|
||||
|
||||
@@ -143,8 +143,6 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
iCalTimeZone *tz;
|
||||
id o;
|
||||
|
||||
NSInteger tzOffset;
|
||||
|
||||
userTimeZone = [[[context activeUser] userDefaults] timeZone];
|
||||
tz = [iCalTimeZone timeZoneForName: [userTimeZone name]];
|
||||
[(iCalCalendar *) parent addTimeZone: tz];
|
||||
@@ -165,10 +163,6 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
o = [o calendarDate];
|
||||
completed = (iCalDateTime *) [self uniqueChildWithTag: @"completed"];
|
||||
//tzOffset = [[o timeZone] secondsFromGMTForDate: o];
|
||||
//o = [o dateByAddingYears: 0 months: 0 days: 0
|
||||
// hours: 0 minutes: 0
|
||||
// seconds: -tzOffset];
|
||||
[completed setDate: o];
|
||||
[self setStatus: @"COMPLETED"];
|
||||
}
|
||||
@@ -177,15 +171,9 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
{
|
||||
iCalDateTime *due;
|
||||
|
||||
|
||||
o = [o calendarDate];
|
||||
due = (iCalDateTime *) [self uniqueChildWithTag: @"due"];
|
||||
[due setTimeZone: tz];
|
||||
|
||||
tzOffset = [userTimeZone secondsFromGMTForDate: o];
|
||||
o = [o dateByAddingYears: 0 months: 0 days: 0
|
||||
hours: 0 minutes: 0
|
||||
seconds: tzOffset];
|
||||
[due setDateTime: o];
|
||||
}
|
||||
|
||||
|
||||
@@ -1,3 +1,706 @@
|
||||
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
|
||||
|
||||
Update translations
|
||||
|
||||
M NEWS
|
||||
M UI/PreferencesUI/BrazilianPortuguese.lproj/Localizable.strings
|
||||
M UI/PreferencesUI/Hungarian.lproj/Localizable.strings
|
||||
|
||||
commit cf35eec1f5c56d8c7332bed34e2195a8ad9c9e9f
|
||||
Author: Ludovic Marcotte <lmarcotte@inverse.ca>
|
||||
Date: Fri Jan 30 10:28:45 2015 -0500
|
||||
|
||||
Improved the NEWS file for the release
|
||||
|
||||
M NEWS
|
||||
|
||||
commit 58a0b0c173616db9a3a1592b437dba7e40475321
|
||||
Author: Ludovic Marcotte <lmarcotte@inverse.ca>
|
||||
Date: Fri Jan 30 10:23:04 2015 -0500
|
||||
|
||||
Updated for the release
|
||||
|
||||
M NEWS
|
||||
|
||||
commit 723a9d4e080644a7a8cef60de9b7450621f0964e
|
||||
Author: Ludovic Marcotte <lmarcotte@inverse.ca>
|
||||
Date: Thu Jan 29 16:21:06 2015 -0500
|
||||
|
||||
Reverted bug fix from #3054 and added comment
|
||||
|
||||
M SoObjects/SOGo/SOGoParentFolder.m
|
||||
|
||||
commit 4cc158043eb6183ecefed7abd93b579afbdfd7f6
|
||||
Author: Francis Lachapelle <flachapelle@inverse.ca>
|
||||
Date: Wed Jan 28 16:38:27 2015 -0500
|
||||
|
||||
Improve CSS server-side cleaner
|
||||
|
||||
Fixes #3040
|
||||
|
||||
M UI/MailPartViewers/UIxMailPartHTMLViewer.m
|
||||
|
||||
commit 7fd1564a86d50cc96995bd3dec44939741312f4a
|
||||
Author: Ludovic Marcotte <lmarcotte@inverse.ca>
|
||||
Date: Wed Jan 28 15:16:21 2015 -0500
|
||||
|
||||
fix for #3076
|
||||
|
||||
M ActiveSync/SOGoActiveSyncDispatcher.m
|
||||
M ActiveSync/SoObjectWebDAVDispatcher+ActiveSync.m
|
||||
M Documentation/SOGoInstallationGuide.asciidoc
|
||||
M NEWS
|
||||
|
||||
commit 981c718d228f70a23e73d8db913234be7161166c
|
||||
Author: Ludovic Marcotte <lmarcotte@inverse.ca>
|
||||
Date: Wed Jan 28 15:03:49 2015 -0500
|
||||
|
||||
improved handling of EAS Push when no heartbeat is provided
|
||||
|
||||
M ActiveSync/SOGoActiveSyncDispatcher+Sync.m
|
||||
M ActiveSync/SOGoActiveSyncDispatcher.m
|
||||
M NEWS
|
||||
|
||||
commit 086361b79a8be7722d8ae476140858067bb06c2a
|
||||
Author: Ludovic Marcotte <lmarcotte@inverse.ca>
|
||||
Date: Fri Jan 23 16:16:36 2015 -0500
|
||||
|
||||
Avoid GNUstep warnings
|
||||
|
||||
M SOPE/GDLContentStore/GCSFolderType.m
|
||||
M UI/MailerUI/UIxMailEditor.m
|
||||
|
||||
commit ab12c84887a663c0ec442cfa22b2c9fd1f737f24
|
||||
Author: Ludovic Marcotte <lmarcotte@inverse.ca>
|
||||
Date: Fri Jan 23 15:32:22 2015 -0500
|
||||
|
||||
More NSLog usage cleanups
|
||||
|
||||
M SoObjects/Appointments/iCalCalendar+SOGo.m
|
||||
M SoObjects/Mailer/NSData+Mail.h
|
||||
M SoObjects/Mailer/NSData+Mail.m
|
||||
M SoObjects/SOGo/BSONCodec.m
|
||||
M SoObjects/SOGo/SOGoMailer.m
|
||||
M SoObjects/SOGo/SOGoSieveManager.m
|
||||
M SoObjects/SOGo/SOGoWebDAVAclManager.m
|
||||
|
||||
commit e2bee230217d16287883fb595d7bfc7ddec0ff77
|
||||
Author: Ludovic Marcotte <lmarcotte@inverse.ca>
|
||||
Date: Thu Jan 22 16:25:16 2015 -0500
|
||||
|
||||
Avoid unnecessary calls to NSLog
|
||||
|
||||
M UI/Contacts/UIxListView.m
|
||||
M UI/MailPartViewers/UIxMailPartHTMLViewer.m
|
||||
M UI/MailerUI/UIxMailView.m
|
||||
M UI/Scheduler/UIxCalView.m
|
||||
M UI/Scheduler/UIxComponentEditor.m
|
||||
|
||||
commit 1a10599369da87e64b77cf110624ca6745605585
|
||||
Author: Ludovic Marcotte <lmarcotte@inverse.ca>
|
||||
Date: Thu Jan 22 15:39:57 2015 -0500
|
||||
|
||||
Removed unnecessary NSLog calls
|
||||
|
||||
M SOPE/NGCards/CardGroup.m
|
||||
M SOPE/NGCards/IcalResponse.m
|
||||
M SOPE/NGCards/NSCalendarDate+ICal.m
|
||||
M SOPE/NGCards/NSString+NGCards.m
|
||||
M SOPE/NGCards/iCalObject.m
|
||||
|
||||
commit d907d5d268b470c7f461cb0b7951b894e4b77772
|
||||
Author: Ludovic Marcotte <lmarcotte@inverse.ca>
|
||||
Date: Thu Jan 22 13:31:31 2015 -0500
|
||||
|
||||
Avoid using NSLog() where we can
|
||||
|
||||
M ActiveSync/NSData+ActiveSync.m
|
||||
M ActiveSync/SOGoActiveSyncDispatcher+Sync.m
|
||||
M ActiveSync/SOGoActiveSyncDispatcher.m
|
||||
M Main/SOGo.m
|
||||
M Main/sogod.m
|
||||
|
||||
commit d98ff69fbee723ae268b0cb0836c8dca75394743
|
||||
Author: Ludovic Marcotte <lmarcotte@inverse.ca>
|
||||
Date: Tue Jan 20 08:35:10 2015 -0500
|
||||
|
||||
Version bumps for the release
|
||||
|
||||
M ChangeLog
|
||||
M Documentation/docinfo.xml
|
||||
M Documentation/includes/global-attributes.asciidoc
|
||||
M NEWS
|
||||
M Version
|
||||
|
||||
commit daa7ab87d7de8553c4df8790a10f18dead51e61f
|
||||
Author: Ludovic Marcotte <lmarcotte@inverse.ca>
|
||||
Date: Mon Jan 19 13:54:04 2015 -0500
|
||||
|
||||
Fix cosmetic change when fetching fb info
|
||||
|
||||
M Documentation/SOGoInstallationGuide.asciidoc
|
||||
M UI/MainUI/SOGoUserHomePage.m
|
||||
|
||||
commit 3e59b1ad6eb4dcca87b75fc478931bb9a404900e
|
||||
Author: Ludovic Marcotte <lmarcotte@inverse.ca>
|
||||
Date: Fri Jan 16 12:55:38 2015 -0500
|
||||
|
||||
Improved documentation regarding password policies over LDAP
|
||||
|
||||
M Documentation/SOGoInstallationGuide.asciidoc
|
||||
|
||||
commit e45451a9f9a38f869268f4db47e4e24045b63530
|
||||
Author: Ludovic Marcotte <lmarcotte@inverse.ca>
|
||||
Date: Fri Jan 16 09:18:37 2015 -0500
|
||||
|
||||
Improved comments over previous commit
|
||||
|
||||
M SOPE/NGCards/iCalTimeZonePeriod.m
|
||||
|
||||
commit e9e3dd5646cdf004896f350e35fddd661463f029
|
||||
Author: Ludovic Marcotte <lmarcotte@inverse.ca>
|
||||
Date: Thu Jan 15 15:29:02 2015 -0500
|
||||
|
||||
fixed timezone calculation on recurring event
|
||||
|
||||
M NEWS
|
||||
M SOPE/NGCards/iCalTimeZonePeriod.m
|
||||
M SoObjects/Appointments/SOGoAppointmentFolder.m
|
||||
|
||||
commit 16c863d89dc7555d3c1a490c3467c08c00bd421f
|
||||
Author: Ludovic Marcotte <lmarcotte@inverse.ca>
|
||||
Date: Thu Jan 15 11:55:04 2015 -0500
|
||||
|
||||
Fixed potential issue when handling multiple Add/Change/Delete/Fetch EAS commands (#3057)
|
||||
|
||||
M ActiveSync/SOGoActiveSyncDispatcher+Sync.m
|
||||
M NEWS
|
||||
|
||||
commit d87056ebfadff870518d032a0fbbd52d5c6d0a88
|
||||
Author: Ludovic Marcotte <lmarcotte@inverse.ca>
|
||||
Date: Mon Jan 12 14:38:55 2015 -0500
|
||||
|
||||
handle multipart for EAS/ItemOperations
|
||||
|
||||
M ActiveSync/SOGoActiveSyncDispatcher.m
|
||||
M NEWS
|
||||
|
||||
commit 135b463e8fa770f47aadb58ede53898ce6a8bfdc
|
||||
Author: Ludovic Marcotte <lmarcotte@inverse.ca>
|
||||
Date: Mon Jan 12 13:39:06 2015 -0500
|
||||
|
||||
Updated the EAS doc
|
||||
|
||||
M ActiveSync/README
|
||||
M Documentation/SOGoInstallationGuide.asciidoc
|
||||
|
||||
commit f4c4f5af6271956de6a9a339d8834b4f2f66ea45
|
||||
Author: Ludovic Marcotte <lmarcotte@inverse.ca>
|
||||
Date: Mon Jan 12 09:09:06 2015 -0500
|
||||
|
||||
fixed From's full name over EAS
|
||||
|
||||
M ActiveSync/SOGoActiveSyncDispatcher.m
|
||||
M NEWS
|
||||
|
||||
commit 70e45f78430e07f75e1fe094f8087902f572fab6
|
||||
Author: Ludovic Marcotte <lmarcotte@inverse.ca>
|
||||
Date: Mon Jan 12 08:40:06 2015 -0500
|
||||
|
||||
fixed birthday offset in EAS
|
||||
|
||||
M ActiveSync/NGVCard+ActiveSync.m
|
||||
M NEWS
|
||||
|
||||
commit 28e449b05077b2f0686fae645a84447af1b6d4e8
|
||||
Author: Ludovic Marcotte <lmarcotte@inverse.ca>
|
||||
Date: Sat Jan 10 07:55:55 2015 -0500
|
||||
|
||||
Updated NEWS file for bugs fixed
|
||||
|
||||
M NEWS
|
||||
|
||||
commit 6b52e9c945af28188b221f4cbf26e0ee28f58195
|
||||
Author: Ludovic Marcotte <lmarcotte@inverse.ca>
|
||||
Date: Fri Jan 9 09:04:15 2015 -0500
|
||||
|
||||
Fix for bug #2960
|
||||
|
||||
M NEWS
|
||||
M SoObjects/SOGo/WOContext+SOGo.h
|
||||
M SoObjects/SOGo/WOContext+SOGo.m
|
||||
M SoObjects/SOGo/WORequest+SOGo.h
|
||||
M SoObjects/SOGo/WORequest+SOGo.m
|
||||
|
||||
commit 1cfbea69c14468424f5eb713bcb5918a70b3d676
|
||||
Author: Ludovic Marcotte <lmarcotte@inverse.ca>
|
||||
Date: Thu Jan 8 15:56:16 2015 -0500
|
||||
|
||||
Make sure we always release local pool
|
||||
|
||||
M ActiveSync/SOGoActiveSyncDispatcher+Sync.m
|
||||
|
||||
commit cb9118d869d1d2535c50d46df5457038160de59e
|
||||
Author: Ludovic Marcotte <lmarcotte@inverse.ca>
|
||||
Date: Wed Jan 7 09:29:31 2015 -0500
|
||||
|
||||
Moved the pool destruction at the correct location
|
||||
|
||||
M ActiveSync/SOGoActiveSyncDispatcher+Sync.m
|
||||
|
||||
commit dd6dd7251ca15dc5e758e733e2529003e08e7c7c
|
||||
Author: Francis Lachapelle <flachapelle@inverse.ca>
|
||||
Date: Tue Jan 6 13:59:20 2015 -0500
|
||||
|
||||
Fix selection of calendar in event/task editors
|
||||
|
||||
Fixes #3049
|
||||
Fixes #3050
|
||||
|
||||
M UI/Scheduler/UIxComponentEditor.m
|
||||
M UI/Templates/SchedulerUI/UIxAppointmentEditor.wox
|
||||
M UI/Templates/SchedulerUI/UIxTaskEditor.wox
|
||||
|
||||
commit 24c6c8c91d421594bd51f07904d4cd3911cd187c
|
||||
Author: Ludovic Marcotte <lmarcotte@inverse.ca>
|
||||
Date: Tue Jan 6 10:59:56 2015 -0500
|
||||
|
||||
Fix for bug #3054
|
||||
|
||||
M NEWS
|
||||
M SoObjects/Appointments/SOGoAppointmentFolders.m
|
||||
M SoObjects/SOGo/SOGoFolder.h
|
||||
M SoObjects/SOGo/SOGoFolder.m
|
||||
M SoObjects/SOGo/SOGoGCSFolder.h
|
||||
M SoObjects/SOGo/SOGoObject.h
|
||||
M SoObjects/SOGo/SOGoObject.m
|
||||
M SoObjects/SOGo/SOGoParentFolder.h
|
||||
M SoObjects/SOGo/SOGoParentFolder.m
|
||||
|
||||
commit b1ac7a0cca0ace6aecd3ee650e3029ca16709831
|
||||
Author: Ludovic Marcotte <lmarcotte@inverse.ca>
|
||||
Date: Mon Jan 5 13:49:28 2015 -0500
|
||||
|
||||
MultipleBookingsFieldName can be set to -1
|
||||
|
||||
M Documentation/SOGoInstallationGuide.asciidoc
|
||||
M NEWS
|
||||
M SoObjects/Appointments/SOGoAppointmentObject.m
|
||||
M SoObjects/SOGo/SOGoUser.m
|
||||
M SoObjects/SOGo/SOGoUserManager.h
|
||||
M SoObjects/SOGo/SOGoUserManager.m
|
||||
M UI/MainUI/SOGoUserHomePage.m
|
||||
|
||||
commit 59e6d6df4aa131ca1ea658a212ec1c30e516b5f1
|
||||
Author: Francis Lachapelle <flachapelle@inverse.ca>
|
||||
Date: Mon Jan 5 10:12:57 2015 -0500
|
||||
|
||||
Fix selection of calendar in event/task editors
|
||||
|
||||
Fixes #3049
|
||||
Fixes #3050
|
||||
|
||||
M NEWS
|
||||
M UI/Templates/SchedulerUI/UIxAppointmentEditor.wox
|
||||
M UI/Templates/SchedulerUI/UIxTaskEditor.wox
|
||||
|
||||
commit 71ad5de910de96a293f75c7c0caba7a36b96c020
|
||||
Author: Ludovic Marcotte <lmarcotte@inverse.ca>
|
||||
Date: Tue Dec 30 08:00:41 2014 -0500
|
||||
|
||||
Bumped everything for the release
|
||||
|
||||
M ChangeLog
|
||||
M Documentation/docinfo.xml
|
||||
M Documentation/includes/global-attributes.asciidoc
|
||||
M NEWS
|
||||
M Version
|
||||
|
||||
commit ead665de85e2202dbde926c316cbba927d38dfa7
|
||||
Author: Ludovic Marcotte <lmarcotte@inverse.ca>
|
||||
Date: Mon Dec 29 16:19:10 2014 -0500
|
||||
|
||||
fix tz issue when the user one was different from the system one with EAS
|
||||
|
||||
M ActiveSync/NSString+ActiveSync.m
|
||||
M ActiveSync/iCalEvent+ActiveSync.m
|
||||
M ActiveSync/iCalToDo+ActiveSync.m
|
||||
M NEWS
|
||||
|
||||
commit a0c1ce8f3b7c22002661d40c24e95a1233b6a6e8
|
||||
Author: Ludovic Marcotte <lmarcotte@inverse.ca>
|
||||
Date: Mon Dec 29 12:43:20 2014 -0500
|
||||
|
||||
Improved handling of non-existant vs. subscribed folders over EAS
|
||||
|
||||
M ActiveSync/SOGoActiveSyncDispatcher.m
|
||||
M SoObjects/Mailer/SOGoMailAccount.m
|
||||
M SoObjects/Mailer/SOGoMailFolder.m
|
||||
|
||||
commit 204a62aa6ac8feb5a13d5a2ee162bb26cc495a50
|
||||
Author: Ludovic Marcotte <lmarcotte@inverse.ca>
|
||||
Date: Tue Dec 23 10:25:53 2014 -0500
|
||||
|
||||
Improved comments in the code
|
||||
|
||||
M SoObjects/Appointments/SOGoAppointmentFolder.m
|
||||
|
||||
commit 31cffdffd34429d1f4873d07d67e822bff82b7cf
|
||||
Author: Ludovic Marcotte <lmarcotte@inverse.ca>
|
||||
Date: Tue Dec 23 10:24:16 2014 -0500
|
||||
|
||||
Fix freebusy info not always returned
|
||||
|
||||
M NEWS
|
||||
M SoObjects/Appointments/SOGoAppointmentFolder.m
|
||||
M SoObjects/SOGo/SOGoFolder.h
|
||||
M SoObjects/SOGo/SOGoFolder.m
|
||||
|
||||
commit 255bcbe92fa6b4610edf5f4d05c97b74537ecb6e
|
||||
Author: Ludovic Marcotte <lmarcotte@inverse.ca>
|
||||
Date: Mon Dec 22 19:59:33 2014 -0500
|
||||
|
||||
Fixed memory leaks in SOGoSyncCacheObject and correctly kill the cache upon each EAS iteration
|
||||
|
||||
M ActiveSync/SOGoSyncCacheObject.m
|
||||
M SoObjects/Mailer/SOGoMailBaseObject.m
|
||||
M SoObjects/SOGo/NSObject+Utilities.m
|
||||
M UI/MainUI/SOGoMicrosoftActiveSyncActions.m
|
||||
|
||||
commit 72732879fafe8ef928593c0cf3e39d962b096ec9
|
||||
Author: Ludovic Marcotte <lmarcotte@inverse.ca>
|
||||
Date: Mon Dec 22 19:32:17 2014 -0500
|
||||
|
||||
Added memory statistics - set SOGoDebugLeaks = YES and call [[self class] memoryStatistics]
|
||||
|
||||
M SoObjects/SOGo/NSObject+Utilities.h
|
||||
M SoObjects/SOGo/NSObject+Utilities.m
|
||||
|
||||
commit 24a934275f5678f7ab86cb706aa817dfb5ef8991
|
||||
Author: Ludovic Marcotte <lmarcotte@inverse.ca>
|
||||
Date: Mon Dec 22 16:12:26 2014 -0500
|
||||
|
||||
Fix small memory leak incase of errors
|
||||
|
||||
M ActiveSync/SOGoActiveSyncDispatcher.m
|
||||
|
||||
commit 58f634bffe35b3a1f6a02072ba0103fea2155a45
|
||||
Author: Ludovic Marcotte <lmarcotte@inverse.ca>
|
||||
Date: Mon Dec 22 15:26:22 2014 -0500
|
||||
|
||||
Cosmetic improvements to the code
|
||||
|
||||
M SoObjects/Appointments/SOGoAppointmentObject.m
|
||||
M SoObjects/SOGo/SOGoMailer.h
|
||||
M SoObjects/SOGo/SOGoMailer.m
|
||||
|
||||
commit ca4a754f2c12784ff437df34c255d3dccbf5c6f2
|
||||
Author: Ludovic Marcotte <lmarcotte@inverse.ca>
|
||||
Date: Mon Dec 22 12:39:58 2014 -0500
|
||||
|
||||
Use the right cutoff date
|
||||
|
||||
M ActiveSync/SOGoActiveSyncDispatcher+Sync.m
|
||||
M NEWS
|
||||
M SoObjects/SOGo/SOGoGCSFolder.m
|
||||
|
||||
commit 8015688df31a3e3254093b260851d4664d725ea6
|
||||
Author: Ludovic Marcotte <lmarcotte@inverse.ca>
|
||||
Date: Mon Dec 22 11:50:51 2014 -0500
|
||||
|
||||
Added SOGoMaximumSyncResponseSize to support memory-limited EAS syncs
|
||||
|
||||
M ActiveSync/SOGoActiveSyncDispatcher+Sync.m
|
||||
M Documentation/SOGoInstallationGuide.asciidoc
|
||||
M NEWS
|
||||
M SoObjects/SOGo/SOGoSystemDefaults.h
|
||||
M SoObjects/SOGo/SOGoSystemDefaults.m
|
||||
|
||||
commit b07913d66d4a47919e09cbcd0c41d92bd97ea0a3
|
||||
Author: Ludovic Marcotte <lmarcotte@inverse.ca>
|
||||
Date: Mon Dec 22 08:36:55 2014 -0500
|
||||
|
||||
See NEWS file
|
||||
|
||||
M ActiveSync/NGVCard+ActiveSync.m
|
||||
M ActiveSync/SOGoActiveSyncDispatcher+Sync.m
|
||||
M ActiveSync/SOGoActiveSyncDispatcher.m
|
||||
M NEWS
|
||||
|
||||
commit df8a0d8715d97873e782ccfbfaab8b580c88c66e
|
||||
Author: Ludovic Marcotte <lmarcotte@inverse.ca>
|
||||
Date: Fri Dec 19 09:03:37 2014 -0500
|
||||
|
||||
Update ChangeLog
|
||||
|
||||
M ChangeLog
|
||||
|
||||
commit e6cc56dca1126e09cb8336bdbd4e5dabc7b83cf1
|
||||
Author: Ludovic Marcotte <lmarcotte@inverse.ca>
|
||||
Date: Fri Dec 19 09:03:19 2014 -0500
|
||||
|
||||
Bumped version to 2.2.12a
|
||||
|
||||
M Version
|
||||
|
||||
commit de3d38262b047a1b72ec9a49042059b90e2abc2c
|
||||
Author: Ludovic Marcotte <lmarcotte@inverse.ca>
|
||||
Date: Fri Dec 19 09:01:39 2014 -0500
|
||||
|
||||
Fix for bug #3034
|
||||
|
||||
M NEWS
|
||||
M SoObjects/Mailer/SOGoDraftObject.m
|
||||
|
||||
commit 0e56527e05566e78ac8fe4687d19423de78b0276
|
||||
Author: Chris Rosenhain <chris@rosenha.in>
|
||||
Date: Fri Dec 19 11:04:05 2014 +1030
|
||||
|
||||
Change ACL modification text to non-gender specific terms
|
||||
|
||||
M SoObjects/Appointments/SOGoAppointmentObject.m
|
||||
M SoObjects/Appointments/SOGoCalendarComponent.m
|
||||
M SoObjects/SOGo/SOGoGCSFolder.m
|
||||
M SoObjects/SOGo/SOGoUserFolder.h
|
||||
M UI/MainUI/SOGoRootPage.m
|
||||
M UI/Templates/SOGoACLDanishModificationAdvisory.wox
|
||||
M UI/Templates/SOGoACLDanishRemovalAdvisory.wox
|
||||
M UI/Templates/SOGoACLDutchModificationAdvisory.wox
|
||||
M UI/Templates/SOGoACLDutchRemovalAdvisory.wox
|
||||
M UI/Templates/SOGoACLEnglishModificationAdvisory.wox
|
||||
M UI/Templates/SOGoACLEnglishRemovalAdvisory.wox
|
||||
|
||||
commit db911f323d23f263f5fa9c5fb02d8234127687b3
|
||||
Author: Ludovic Marcotte <lmarcotte@inverse.ca>
|
||||
Date: Thu Dec 18 08:56:23 2014 -0500
|
||||
|
||||
Updated ChangeLog and version numbers for the release
|
||||
|
||||
M ChangeLog
|
||||
M Documentation/docinfo.xml
|
||||
M Documentation/includes/global-attributes.asciidoc
|
||||
M Version
|
||||
|
||||
commit fcddb37a0dd76f75bb16f9380e8b8a01865d0c4c
|
||||
Author: Ludovic Marcotte <lmarcotte@inverse.ca>
|
||||
Date: Thu Dec 18 08:53:31 2014 -0500
|
||||
|
||||
@@ -911,7 +911,8 @@ resource if the entry has the calendarresource objectClass set.
|
||||
to which a resource can be part of at any point in time.
|
||||
|
||||
If this is set to `0`, or if the attribute is missing, it means no
|
||||
limit.
|
||||
limit. If set to `-1`, no limit is imposed but the resource will
|
||||
be marked as busy the first time it is booked.
|
||||
|
||||
|filter (optional)
|
||||
|The filter to use for LDAP queries, it should be defined as an
|
||||
@@ -998,7 +999,17 @@ context as Dovecot stores in its database.
|
||||
|passwordPolicy
|
||||
|If set to `YES`, SOGo will use the extended LDAP Password Policies
|
||||
attributes. If you LDAP server does not support those and you activate
|
||||
this feature, every LDAP requests will fail.
|
||||
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
|
||||
@@ -1326,27 +1337,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
|
||||
@@ -1476,7 +1487,10 @@ case, SOGo will consider the returned entry to be a resource.
|
||||
which a resource can be part of at any point in time.
|
||||
|
||||
If this is set to `0`, or if the attribute is missing, it means no
|
||||
limit.
|
||||
limit and the resource will always be marked as free. If set to `-1`,
|
||||
no limit is imposed but the resource will be marked as busy the first
|
||||
time it is booked. If greater than 0, the resource will get marked as
|
||||
busy once it reaches the value.
|
||||
|
||||
|DomainFieldName (optional)
|
||||
|If set, SOGo will use the value of that field as the domain associated
|
||||
@@ -1778,6 +1792,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
|
||||
@@ -2340,8 +2360,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.
|
||||
|
||||
@@ -2390,6 +2410,15 @@ _SOGoMaximumPingInterval_.
|
||||
|
||||
If not set, it defaults to `10` seconds.
|
||||
|
||||
|S |SOGoMaximumSyncResponseSize
|
||||
|Parameter used to overwrite the maximum response size during
|
||||
a Sync operation. The value is in kilobytes. Setting this to 512
|
||||
means the response size will be of 524288 bytes or less. Note that
|
||||
if you set the value too low and a mail message (or any other object)
|
||||
surpasses it, it will still be synced but only this item will be.
|
||||
|
||||
Defaults to `0`, which means no overwrite is performed.
|
||||
|
||||
|S |SOGoMaximumSyncWindowSize
|
||||
|Parameter used to overwrite the maximum number of items returned during
|
||||
a Sync operation.
|
||||
@@ -2402,24 +2431,22 @@ have unexpected behaviour with various ActiveSync clients.
|
||||
|
||||
Please be aware of the following limitations:
|
||||
|
||||
* Currently, only the personal calendar and address book are
|
||||
synchronized. Adding support for all folders is planned.
|
||||
* When creating an Outlook 2013 profile, you must actually kill Outlook
|
||||
before the end of the creation process. See http://www.vionblog.com/connect-zimbra-community-with-outlook-2013
|
||||
for a procedure example.
|
||||
* Outlook 2013 does not search the GAL. One possible alternative
|
||||
solution is to configure Outlook to use a LDAP server (over SSL) with
|
||||
authentication. Alternatively, when supporting more than just the
|
||||
personal address book, we'll also be able to expose the LDAP/SQL based
|
||||
address books in SOGo over ActiveSync.
|
||||
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
|
||||
see the warning and thus, preventing any synchronization to happen.
|
||||
* ActiveSync clients keep connections open for a while. Each connection
|
||||
will grab a hold on a sogod process so you will need a lot of processes
|
||||
to handle many clients. This limitation will eventually be overcome in
|
||||
SOGo.
|
||||
to handle many clients. Make sure you tune your SOGo server when having
|
||||
lots of ActiveSync clients.
|
||||
* Repetitive events with occurrences exceptions are currently not
|
||||
supported.
|
||||
* Outlook 2013 Autodiscovery is currently not supported.
|
||||
@@ -2441,13 +2468,90 @@ user base.
|
||||
|
||||
To contact Microsoft, please visit:
|
||||
|
||||
http://www.microsoft.com/en-us/legal/intellectualproperty/IPLicensing/Programs/exchangeactivesyncprotocol.aspx
|
||||
http://www.microsoft.com/en-us/legal/intellectualproperty/
|
||||
|
||||
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
|
||||
----------
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
<!-- TODO have the build system take care of this -->
|
||||
<releaseinfo>Version 2.2.12 - December 2014</releaseinfo>
|
||||
<subtitle>for version 2.2.12</subtitle>
|
||||
<date>2014-12-18</date>
|
||||
<releaseinfo>Version 2.2.16 - February 2015</releaseinfo>
|
||||
<subtitle>for version 2.2.16</subtitle>
|
||||
<date>2015-02-12</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>
|
||||
|
||||
@@ -6,13 +6,13 @@
|
||||
Authors:
|
||||
- Inverse inc. <info@inverse.ca>
|
||||
|
||||
Copyright (C) 2008-2014 Inverse inc.
|
||||
Copyright (C) 2008-2015 Inverse inc.
|
||||
License: GFDL 1.2 or later. http://www.gnu.org/licenses/fdl.html
|
||||
|
||||
////
|
||||
|
||||
// TODO have the build system take care of this
|
||||
|
||||
:release_version: 2.2.12
|
||||
:release_version: 2.2.16
|
||||
|
||||
// vim: set syntax=asciidoc tabstop=2 shiftwidth=2 expandtab:
|
||||
+2
-2
@@ -245,7 +245,7 @@ static BOOL debugLeaks;
|
||||
}
|
||||
else
|
||||
{
|
||||
NSLog (@"No value specified for '%@'", *urlString);
|
||||
[self errorWithFormat: @"No value specified for '%@'", *urlString];
|
||||
ok = NO;
|
||||
}
|
||||
}
|
||||
@@ -441,7 +441,7 @@ static BOOL debugLeaks;
|
||||
if (debugLeaks)
|
||||
{
|
||||
if (debugOn)
|
||||
NSLog (@"allocated classes:\n%s", GSDebugAllocationList (YES));
|
||||
[self logWithFormat: @"allocated classes:\n%s", GSDebugAllocationList (YES)];
|
||||
else
|
||||
{
|
||||
debugOn = YES;
|
||||
|
||||
+4
-4
@@ -1,15 +1,15 @@
|
||||
/*
|
||||
Copyright (C) 2004-2005 SKYRIX Software AG
|
||||
Copyright (C) 2006-2009 Inverse inc.
|
||||
Copyright (C) 2006-2015 Inverse inc.
|
||||
|
||||
This file is part of OpenGroupware.org.
|
||||
This file is part of SOGo.
|
||||
|
||||
OGo is free software; you can redistribute it and/or modify it under
|
||||
SOGo 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, or (at your option) any
|
||||
later version.
|
||||
|
||||
OGo is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||
SOGo 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.
|
||||
|
||||
@@ -1,3 +1,71 @@
|
||||
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 (2015-01-30)
|
||||
-------------------
|
||||
|
||||
Enhancements
|
||||
- improved handling of EAS Push when no heartbeat is provided
|
||||
- no longer need to kill Outlook 2013 when creating EAS profiles (#3076)
|
||||
- improved server-side CSS cleaner (#3040)
|
||||
- unified the logging messages in sogo.log file (#2534/#3063)
|
||||
- updated Brazilian (Portuguese) and Hungarian translations
|
||||
|
||||
2.2.14 (2015-01-20)
|
||||
-------------------
|
||||
|
||||
Enhancements
|
||||
- MultipleBookingsFieldName can be set to -1 to show busy status when booked at least once
|
||||
- handle multipart objects in EAS/ItemOperations
|
||||
|
||||
Bug fixes
|
||||
- fixed calendar selection in event and task editors (#3049, #3050)
|
||||
- check for resources existence when listing subscribed ones (#3054)
|
||||
- correctly recognize Apple Calendar on Yosemite (#2960)
|
||||
- fixed two potential autorelease pool leaks (#3026 and #3051)
|
||||
- fixed birthday offset in EAS
|
||||
- fixed From's full name over EAS
|
||||
- fixed potential issue when handling multiple Add/Change/Delete/Fetch EAS commands (#3057)
|
||||
- fixed wrong timezone calculation on recurring events
|
||||
|
||||
2.2.13 (2014-12-30)
|
||||
-------------------
|
||||
|
||||
Enhancements
|
||||
- initial support for empty sync request/response for EAS
|
||||
- added the SOGoMaximumSyncResponseSize EAS configuration parameter to
|
||||
support memory-limited sync response sizes
|
||||
- we now not only use the creation date for event's cutoff date (EAS)
|
||||
|
||||
Bug fixes
|
||||
- fixed contact description truncation on WP8 phones (#3028)
|
||||
- fixed freebusy information not always returned
|
||||
- fixed tz issue when the user one was different from the system one with EAS
|
||||
|
||||
2.2.12a (2014-12-19)
|
||||
--------------------
|
||||
|
||||
Bug fixes
|
||||
- fixed empty HTML mails being sent (#3034)
|
||||
|
||||
2.2.12 (2014-12-18)
|
||||
-------------------
|
||||
|
||||
@@ -12,12 +80,12 @@ Enhancements
|
||||
- updated Czech, Dutch, Finnish, French, German, Polish and Spanish (Spain) translations
|
||||
|
||||
Bug fixes
|
||||
- fix for privacy and categories for EAS (#3022)
|
||||
- fixed for privacy and categories for EAS (#3022)
|
||||
- correctly set MeetingStatus for EAS on iOS devices
|
||||
- Ubuntu Lucid fixes for EAS
|
||||
- fix calendar reminders for future events (#3008)
|
||||
- fixed calendar reminders for future events (#3008)
|
||||
- make sure all text parts are UTF-8 re-encoded for Outlook 2013 over EAS (#3003)
|
||||
- fix task description truncation affecting WP8 phones over EAS (#3028)
|
||||
- fixed task description truncation affecting WP8 phones over EAS (#3028)
|
||||
|
||||
2.2.11a (2014-12-10)
|
||||
--------------------
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -20,6 +20,7 @@
|
||||
*/
|
||||
|
||||
#import <Foundation/NSDictionary.h>
|
||||
#import <Foundation/NSKeyValueCoding.h>
|
||||
#import <Foundation/NSSet.h>
|
||||
#import <Foundation/NSString.h>
|
||||
|
||||
@@ -147,7 +148,7 @@
|
||||
if ([keys count] == 0)
|
||||
return folderQualifier;
|
||||
|
||||
bindings = [_folder valuesForKeys:keys];
|
||||
bindings = [_folder dictionaryWithValuesForKeys:keys];
|
||||
return [folderQualifier qualifierWithBindings:bindings
|
||||
requiresAllVariables:NO];
|
||||
}
|
||||
|
||||
@@ -59,8 +59,10 @@ static NGCardsSaxHandler *sax = nil;
|
||||
[parser setErrorHandler:sax];
|
||||
}
|
||||
else
|
||||
NSLog(@"ERROR(%s): did not find a parser for text/x-vcard!",
|
||||
__PRETTY_FUNCTION__);
|
||||
{
|
||||
//NSLog(@"ERROR(%s): did not find a parser for text/x-vcard!",
|
||||
// __PRETTY_FUNCTION__);
|
||||
}
|
||||
}
|
||||
|
||||
return parser;
|
||||
@@ -170,8 +172,8 @@ static NGCardsSaxHandler *sax = nil;
|
||||
{
|
||||
if (![aChild isKindOfClass: mappedClass])
|
||||
{
|
||||
NSLog (@"warning: new child to entity '%@': '%@' converted to '%@'",
|
||||
tag, childTag, NSStringFromClass(mappedClass));
|
||||
//NSLog (@"warning: new child to entity '%@': '%@' converted to '%@'",
|
||||
// tag, childTag, NSStringFromClass(mappedClass));
|
||||
newChild = [aChild elementWithClass: mappedClass];
|
||||
}
|
||||
}
|
||||
|
||||
@@ -54,7 +54,7 @@
|
||||
|
||||
- (BOOL)appendLine:(NSString *)_line {
|
||||
if (self->isFinished) {
|
||||
NSLog(@"WARNING[%s]: already finished!", __PRETTY_FUNCTION__);
|
||||
//NSLog(@"WARNING[%s]: already finished!", __PRETTY_FUNCTION__);
|
||||
return NO;
|
||||
}
|
||||
// limit length to 75 chars
|
||||
|
||||
@@ -77,8 +77,8 @@ static NSString *gmtcalfmt = @"%Y%m%dT%H%M%SZ";
|
||||
return [self icalStringInGMT];
|
||||
else {
|
||||
/* not in GMT */
|
||||
NSLog(@"WARNING(%s): arbitary timezones not supported yet: %@",
|
||||
__PRETTY_FUNCTION__, _tz);
|
||||
//NSLog(@"WARNING(%s): arbitary timezones not supported yet: %@",
|
||||
// __PRETTY_FUNCTION__, _tz);
|
||||
return [self icalStringInGMT];
|
||||
}
|
||||
}
|
||||
|
||||
@@ -276,7 +276,9 @@
|
||||
}
|
||||
}
|
||||
else
|
||||
NSLog(@"Cannot parse iCal duration value: '%@'", self);
|
||||
{
|
||||
//NSLog(@"Cannot parse iCal duration value: '%@'", self);
|
||||
}
|
||||
|
||||
if (isNegative)
|
||||
ti = -ti;
|
||||
|
||||
@@ -59,8 +59,8 @@ static NSTimeZone *defTZ = nil;
|
||||
[self takeValue:_value forXKey:_key];
|
||||
}
|
||||
else {
|
||||
NSLog(@"0x%08x[%@]: ignoring attempt to set unbound key '%@'",
|
||||
self, NSStringFromClass([self class]), _key);
|
||||
//NSLog(@"0x%08x[%@]: ignoring attempt to set unbound key '%@'",
|
||||
// self, NSStringFromClass([self class]), _key);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -156,13 +156,15 @@
|
||||
[tzStart setTimeZone: [NSTimeZone timeZoneWithName: @"GMT"]];
|
||||
tmpDate = [NSCalendarDate dateWithYear: [refDate yearOfCommonEra]
|
||||
month: [[[rrule byMonth] objectAtIndex: 0] intValue]
|
||||
day: 1 hour: [tzStart hourOfDay]
|
||||
day: 1
|
||||
hour: [tzStart hourOfDay]
|
||||
minute: [tzStart minuteOfHour] second: 0
|
||||
timeZone: [NSTimeZone timeZoneWithName: @"GMT"]];
|
||||
|
||||
tmpDate = [tmpDate addYear: 0 month: ((pos > 0) ? 0 : 1)
|
||||
day: 0 hour: 0 minute: 0
|
||||
second: 0];
|
||||
|
||||
|
||||
/* If the day of the time change is "-XSU", we need to determine whether the
|
||||
first day of next month is in the same week. In practice, as most time
|
||||
changes occurs on sundays, it will be false only when that first day is a
|
||||
@@ -171,10 +173,42 @@
|
||||
if (dateDayOfWeek > dayOfWeek && pos < 0)
|
||||
pos++;
|
||||
|
||||
/* We check if the days of the week are identical. This is important because if they
|
||||
are, "pos" actually includes the first day of tmpDate which means we must decrement
|
||||
pos by 1. This happens for example in the eastern timezone (America/Montreal)
|
||||
in 2015. We have:
|
||||
|
||||
BEGIN:VTIMEZONE
|
||||
TZID:America/Montreal
|
||||
X-LIC-LOCATION:America/Montreal
|
||||
BEGIN:DAYLIGHT
|
||||
TZOFFSETFROM:-0500
|
||||
TZOFFSETTO:-0400
|
||||
TZNAME:EDT
|
||||
DTSTART:19700308T020000
|
||||
RRULE:FREQ=YEARLY;BYMONTH=3;BYDAY=2SU
|
||||
END:DAYLIGHT
|
||||
BEGIN:STANDARD
|
||||
TZOFFSETFROM:-0400
|
||||
TZOFFSETTO:-0500
|
||||
TZNAME:EST
|
||||
DTSTART:19701101T020000
|
||||
RRULE:FREQ=YEARLY;BYMONTH=11;BYDAY=1SU
|
||||
END:STANDARD
|
||||
END:VTIMEZONE
|
||||
|
||||
The time changes occure on a Sunday, but in March, the 1st is a Sunday itself and in November
|
||||
the 1st is also a Sunday. If we don't decrement "pos" by one, tmpDate (which is set to March or November 1st
|
||||
because of "day: 1" will have 14 more days added for March and 7 more days added for November - which will
|
||||
effectively shift the time change by a whole week.
|
||||
*/
|
||||
if (dayOfWeek == dateDayOfWeek)
|
||||
pos--;
|
||||
|
||||
offset = (dayOfWeek - dateDayOfWeek) + (pos * 7);
|
||||
tmpDate = [tmpDate addYear: 0 month: 0 day: offset
|
||||
hour: 0 minute: 0 second: 0];
|
||||
|
||||
|
||||
return tmpDate;
|
||||
}
|
||||
|
||||
|
||||
@@ -492,6 +492,9 @@ static Class iCalEventK = nil;
|
||||
//
|
||||
// If the user is NOT the owner of the calendar, by default we exclude the freebusy information.
|
||||
//
|
||||
// We must include the freebusy information of other users if we are actually looking at their freebusy information
|
||||
// but we aren't necessarily subscribed to their calendars.
|
||||
//
|
||||
- (BOOL) includeInFreeBusy
|
||||
{
|
||||
NSNumber *excludeFromFreeBusy;
|
||||
@@ -500,16 +503,31 @@ static Class iCalEventK = nil;
|
||||
|
||||
userLogin = [[context activeUser] login];
|
||||
is_owner = [userLogin isEqualToString: [self ownerInContext: context]];
|
||||
|
||||
|
||||
// Check if the owner (not the active user) has excluded the calendar from her/his free busy data.
|
||||
excludeFromFreeBusy
|
||||
= [self folderPropertyValueInCategory: @"FreeBusyExclusions"
|
||||
forUser: [SOGoUser userWithLogin: userLogin]];
|
||||
|
||||
if (!excludeFromFreeBusy && !is_owner)
|
||||
return NO;
|
||||
|
||||
return ![excludeFromFreeBusy boolValue];
|
||||
if ([self isSubscription])
|
||||
{
|
||||
// If the user has not yet set an include/not include fb information let's EXCLUDE it.
|
||||
if (!excludeFromFreeBusy)
|
||||
return NO;
|
||||
else
|
||||
return ![excludeFromFreeBusy boolValue];
|
||||
}
|
||||
else if (is_owner)
|
||||
{
|
||||
// We are the owner but we haven't included/excluded freebusy info, let's INCLUDE it.
|
||||
if (!excludeFromFreeBusy)
|
||||
return YES;
|
||||
else
|
||||
return ![excludeFromFreeBusy boolValue];
|
||||
}
|
||||
|
||||
// It's not a subscribtion and we aren't the owner. Let's INCLUDE the freebusy info.
|
||||
return YES;
|
||||
}
|
||||
|
||||
- (void) setIncludeInFreeBusy: (BOOL) newInclude
|
||||
@@ -908,11 +926,6 @@ static Class iCalEventK = nil;
|
||||
dateSecs = [NSNumber numberWithInt: [date timeIntervalSince1970]];
|
||||
[record setObject: dateSecs forKey: @"c_enddate"];
|
||||
|
||||
// The first instance date is added to the dictionary so it can
|
||||
// be used by UIxCalListingActions to compute the DST offset.
|
||||
date = [theFirstCycle startDate];
|
||||
[record setObject: date forKey: @"cycleStartDate"];
|
||||
|
||||
return record;
|
||||
}
|
||||
|
||||
@@ -1085,9 +1098,7 @@ firstInstanceCalendarDateRange: (NGCalendarDateRange *) fir
|
||||
|
||||
[newRecord setObject: dateSecs forKey: @"c_recurrence_id"];
|
||||
[newRecord setObject: [NSNumber numberWithInt: 1] forKey: @"c_iscycle"];
|
||||
// The first instance date is added to the dictionary so it can
|
||||
// be used by UIxCalListingActions to compute the DST offset.
|
||||
[newRecord setObject: [fir startDate] forKey: @"cycleStartDate"];
|
||||
|
||||
// We identified the record as an exception.
|
||||
[newRecord setObject: [NSNumber numberWithInt: 1] forKey: @"isException"];
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
|
||||
/* SOGoAppointmentFolders.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
|
||||
|
||||
@@ -400,9 +400,9 @@
|
||||
{
|
||||
currentUID = [currentAttendee uid];
|
||||
if (currentUID)
|
||||
[self _addOrUpdateEvent: newEvent
|
||||
forUID: currentUID
|
||||
owner: owner];
|
||||
[self _addOrUpdateEvent: newEvent
|
||||
forUID: currentUID
|
||||
owner: owner];
|
||||
}
|
||||
|
||||
[self sendEMailUsingTemplateNamed: @"Update"
|
||||
@@ -443,7 +443,7 @@
|
||||
us = [user userSettings];
|
||||
moduleSettings = [us objectForKey:@"Calendar"];
|
||||
|
||||
// Check if the user prevented his account from beeing invited to events
|
||||
// Check if the user prevented their account from beeing invited to events
|
||||
if (![user isResource] && [[moduleSettings objectForKey:@"PreventInvitations"] boolValue])
|
||||
{
|
||||
// Check if the user have a whiteList
|
||||
@@ -637,10 +637,10 @@
|
||||
|
||||
if ([fbInfo count])
|
||||
{
|
||||
// If we always force the auto-accept if numberOfSimultaneousBookings == 0 (ie., no limit
|
||||
// If we always force the auto-accept if numberOfSimultaneousBookings <= 0 (ie., no limit
|
||||
// is imposed) or if numberOfSimultaneousBookings is greater than the number of
|
||||
// overlapping events
|
||||
if ([user numberOfSimultaneousBookings] == 0 ||
|
||||
if ([user numberOfSimultaneousBookings] <= 0 ||
|
||||
[user numberOfSimultaneousBookings] > [fbInfo count])
|
||||
{
|
||||
if (currentAttendee)
|
||||
@@ -734,7 +734,7 @@ inRecurrenceExceptionsForEvent: (iCalEvent *) theEvent
|
||||
// the modification was actually NOT made on the master event
|
||||
if ([theEvent recurrenceId])
|
||||
return;
|
||||
|
||||
|
||||
events = [[theEvent parent] events];
|
||||
|
||||
for (i = 0; i < [events count]; i++)
|
||||
@@ -743,12 +743,12 @@ inRecurrenceExceptionsForEvent: (iCalEvent *) theEvent
|
||||
if ([e recurrenceId])
|
||||
for (j = 0; j < [theAttendees count]; j++)
|
||||
if (shouldAdd)
|
||||
[e addToAttendees: [theAttendees objectAtIndex: j]];
|
||||
else
|
||||
[e removeFromAttendees: [theAttendees objectAtIndex: j]];
|
||||
[e addToAttendees: [theAttendees objectAtIndex: j]];
|
||||
else
|
||||
[e removeFromAttendees: [theAttendees objectAtIndex: j]];
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//
|
||||
//
|
||||
//
|
||||
@@ -803,14 +803,17 @@ inRecurrenceExceptionsForEvent: (iCalEvent *) theEvent
|
||||
// We insert the attendees in all exception occurences, if
|
||||
// the attendees were added to the master event.
|
||||
[self _addOrDeleteAttendees: addedAttendees
|
||||
inRecurrenceExceptionsForEvent: newEvent
|
||||
add: YES];
|
||||
inRecurrenceExceptionsForEvent: newEvent
|
||||
add: YES];
|
||||
|
||||
if ([changes sequenceShouldBeIncreased])
|
||||
{
|
||||
[newEvent increaseSequence];
|
||||
|
||||
// Update attendees calendars and send them an update
|
||||
// notification by email
|
||||
// notification by email. We ignore the newly added
|
||||
// attendees as we don't want to send them invitation
|
||||
// update emails
|
||||
[self _handleSequenceUpdateInEvent: newEvent
|
||||
ignoringAttendees: addedAttendees
|
||||
fromOldEvent: oldEvent];
|
||||
@@ -1093,7 +1096,7 @@ inRecurrenceExceptionsForEvent: (iCalEvent *) theEvent
|
||||
else
|
||||
{
|
||||
// We must REMOVE any SENT-BY here. This is important since if A accepted
|
||||
// the event for B and then, B changes by himself his participation status,
|
||||
// the event for B and then, B changes by theirself their participation status,
|
||||
// we don't want to keep the previous SENT-BY attribute there.
|
||||
[(NSMutableDictionary *)[otherAttendee attributes] removeObjectForKey: @"SENT-BY"];
|
||||
}
|
||||
@@ -1191,7 +1194,7 @@ inRecurrenceExceptionsForEvent: (iCalEvent *) theEvent
|
||||
else
|
||||
{
|
||||
// We must REMOVE any SENT-BY here. This is important since if A accepted
|
||||
// the event for B and then, B changes by himself his participation status,
|
||||
// the event for B and then, B changes by theirself their participation status,
|
||||
// we don't want to keep the previous SENT-BY attribute there.
|
||||
[(NSMutableDictionary *)[attendee attributes] removeObjectForKey: @"SENT-BY"];
|
||||
}
|
||||
@@ -1208,7 +1211,7 @@ inRecurrenceExceptionsForEvent: (iCalEvent *) theEvent
|
||||
|
||||
delegatedUID = [otherDelegate uid];
|
||||
if (delegatedUID)
|
||||
// Delegate attendee is a local user; remove event from his calendar
|
||||
// Delegate attendee is a local user; remove event from their calendar
|
||||
[self _removeEventFromUID: delegatedUID
|
||||
owner: [theOwnerUser login]
|
||||
withRecurrenceId: [event recurrenceId]];
|
||||
@@ -1240,7 +1243,7 @@ inRecurrenceExceptionsForEvent: (iCalEvent *) theEvent
|
||||
[event addToAttendees: delegate];
|
||||
|
||||
if (delegatedUID)
|
||||
// Delegate attendee is a local user; add event to his calendar
|
||||
// Delegate attendee is a local user; add event to their calendar
|
||||
[self _addOrUpdateEvent: event
|
||||
forUID: delegatedUID
|
||||
owner: [theOwnerUser login]];
|
||||
|
||||
@@ -644,8 +644,8 @@
|
||||
// As much as we can, we try to use c_name == c_uid in order
|
||||
// to avoid tricky scenarios with some CalDAV clients. For example,
|
||||
// if Alice invites Bob (both use SOGo) and Bob accepts the invitation
|
||||
// using Lightning before having refreshed his calendar, he'll end up
|
||||
// with a duplicate of the event in his database tables.
|
||||
// using Lightning before having refreshed their calendar, they'll end up
|
||||
// with a duplicate of the event in their database tables.
|
||||
if (isNew)
|
||||
{
|
||||
newUid = nameInContainer;
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -93,7 +93,7 @@
|
||||
element = [elements objectAtIndex: 0];
|
||||
else
|
||||
{
|
||||
NSLog(@"ERROR: given calendar contains no elements: %@", self);
|
||||
//NSLog(@"ERROR: given calendar contains no elements: %@", self);
|
||||
element = nil;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,8 +1,6 @@
|
||||
/* NSData+Mail.h - this file is part of SOGo
|
||||
*
|
||||
* Copyright (C) 2007 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
|
||||
|
||||
@@ -1,9 +1,6 @@
|
||||
/* NSData+Mail.m - this file is part of SOGo
|
||||
*
|
||||
* Copyright (C) 2007-2008 Inverse inc.
|
||||
*
|
||||
* Author: Wolfgang Sourdeau <wsourdeau@inverse.ca>
|
||||
* Ludovic Marcotte <lmarcotte@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
|
||||
@@ -52,7 +49,7 @@
|
||||
else
|
||||
{
|
||||
decodedData = nil;
|
||||
NSLog (@"encoding '%@' unknown, returning nil data", realEncoding);
|
||||
//NSLog (@"encoding '%@' unknown, returning nil data", realEncoding);
|
||||
}
|
||||
}
|
||||
else
|
||||
|
||||
@@ -1217,13 +1217,13 @@ static NSString *userAgent = nil;
|
||||
|
||||
if (!isHTML)
|
||||
{
|
||||
[map setObject: contentTypeValue forKey: @"content-type"];
|
||||
[message setHeader: contentTypeValue forKey: @"content-type"];
|
||||
body = text;
|
||||
}
|
||||
else
|
||||
{
|
||||
body = [[[NGMimeMultipartBody alloc] initWithPart: message] autorelease];
|
||||
[map addObject: MultiAlternativeType forKey: @"content-type"];
|
||||
[message setHeader: MultiAlternativeType forKey: @"content-type"];
|
||||
|
||||
// Get the text part from it and add it
|
||||
[body addBodyPart: [self plainTextBodyPartForText]];
|
||||
|
||||
@@ -40,6 +40,7 @@
|
||||
#import <NGImap4/NGImap4Connection.h>
|
||||
#import <NGImap4/NGImap4Client.h>
|
||||
#import <NGImap4/NGImap4Context.h>
|
||||
#import <NGImap4/NSString+Imap4.h>
|
||||
|
||||
#import <SOGo/NSArray+Utilities.h>
|
||||
#import <SOGo/NSString+Utilities.h>
|
||||
@@ -667,19 +668,14 @@ static NSString *inboxFolderName = @"INBOX";
|
||||
NSDictionary *result, *nresult, *namespaceDict;
|
||||
NSMutableDictionary *folders;
|
||||
NGImap4Client *client;
|
||||
SOGoUserDefaults *ud;
|
||||
NSArray *folderList;
|
||||
NSEnumerator *e;
|
||||
NSString *guid;
|
||||
id object;
|
||||
|
||||
BOOL subscribedOnly, hasAnnotatemore;
|
||||
BOOL hasAnnotatemore;
|
||||
|
||||
ud = [[context activeUser] userDefaults];
|
||||
subscribedOnly = [ud mailShowSubscribedFoldersOnly];
|
||||
|
||||
folderList = [[self imap4Connection] allFoldersForURL: [self imap4URL]
|
||||
onlySubscribedFolders: subscribedOnly];
|
||||
folderList = [self allFolderPaths];
|
||||
|
||||
folders = [NSMutableDictionary dictionary];
|
||||
|
||||
@@ -688,19 +684,19 @@ static NSString *inboxFolderName = @"INBOX";
|
||||
hasAnnotatemore = [self hasCapability: @"annotatemore"];
|
||||
|
||||
if (hasAnnotatemore)
|
||||
result = [client annotation: @"*" entryName: @"/comment" attributeName: @"value.priv"];
|
||||
result = [client annotation: @"*" entryName: @"/comment" attributeName: @"value.priv"];
|
||||
else
|
||||
result = [client lstatus: @"*" flags: [NSArray arrayWithObjects: @"x-guid", nil]];
|
||||
|
||||
result = [client lstatus: @"*" flags: [NSArray arrayWithObjects: @"x-guid", nil]];
|
||||
|
||||
e = [folderList objectEnumerator];
|
||||
|
||||
|
||||
while ((object = [e nextObject]))
|
||||
{
|
||||
if (hasAnnotatemore)
|
||||
guid = [[[[result objectForKey: @"FolderList"] objectForKey: [object substringFromIndex: 1]] objectForKey: @"/comment"] objectForKey: @"value.priv"];
|
||||
guid = [[[[result objectForKey: @"FolderList"] objectForKey: [object substringFromIndex: 1]] objectForKey: @"/comment"] objectForKey: @"value.priv"];
|
||||
else
|
||||
guid = [[[result objectForKey: @"status"] objectForKey: [object substringFromIndex: 1]] objectForKey: @"x-guid"];
|
||||
|
||||
guid = [[[result objectForKey: @"status"] objectForKey: [object substringFromIndex: 1]] objectForKey: @"x-guid"];
|
||||
|
||||
if (!guid)
|
||||
{
|
||||
// Don't generate a GUID for "Other users" and "Shared" namespace folders - user foldername instead
|
||||
@@ -710,18 +706,28 @@ static NSString *inboxFolderName = @"INBOX";
|
||||
[folders setObject: [NSString stringWithFormat: @"folder%@", [object substringFromIndex: 1]] forKey: [NSString stringWithFormat: @"folder%@", [object substringFromIndex: 1]]];
|
||||
continue;
|
||||
}
|
||||
|
||||
guid = [[NSProcessInfo processInfo] globallyUniqueString];
|
||||
nresult = [client annotation: [object substringFromIndex: 1] entryName: @"/comment" attributeName: @"value.priv" attributeValue: guid];
|
||||
|
||||
if (![[nresult objectForKey: @"result"] boolValue])
|
||||
guid = [NSString stringWithFormat: @"%@", [object substringFromIndex: 1]];
|
||||
// if folder doesn't exists - ignore it
|
||||
nresult = [client status: [object substringFromIndex: 1]
|
||||
flags: [NSArray arrayWithObject: @"UIDVALIDITY"]];
|
||||
if (![[nresult valueForKey: @"result"] boolValue])
|
||||
continue;
|
||||
|
||||
if (hasAnnotatemore)
|
||||
{
|
||||
guid = [[NSProcessInfo processInfo] globallyUniqueString];
|
||||
nresult = [client annotation: [object substringFromIndex: 1] entryName: @"/comment" attributeName: @"value.priv" attributeValue: guid];
|
||||
}
|
||||
|
||||
// setannotation failed or annotatemore is not available
|
||||
if (![[nresult objectForKey: @"result"] boolValue] || !hasAnnotatemore)
|
||||
guid = [NSString stringWithFormat: @"%@", [object substringFromIndex: 1]];
|
||||
}
|
||||
|
||||
|
||||
[folders setObject: [NSString stringWithFormat: @"folder%@", guid] forKey: [NSString stringWithFormat: @"folder%@", [object substringFromIndex: 1]]];
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
return folders;
|
||||
}
|
||||
|
||||
|
||||
@@ -43,10 +43,6 @@
|
||||
|
||||
@implementation SOGoMailBaseObject
|
||||
|
||||
#if 0
|
||||
static BOOL debugOn = YES;
|
||||
#endif
|
||||
|
||||
- (id) initWithImap4URL: (NSURL *) _url
|
||||
inContainer: (id) _container
|
||||
{
|
||||
|
||||
@@ -1069,7 +1069,11 @@ _compareFetchResultsByMODSEQ (id entry1, id entry2, void *data)
|
||||
NSException *error;
|
||||
|
||||
if ([self imap4Connection])
|
||||
error = [imap4 deleteMailboxAtURL: [self imap4URL]];
|
||||
{
|
||||
error = [imap4 deleteMailboxAtURL: [self imap4URL]];
|
||||
if (!error)
|
||||
[[imap4 client] unsubscribe: [[self imap4URL] path]];
|
||||
}
|
||||
else
|
||||
error = [NSException exceptionWithName: @"SOGoMailException"
|
||||
reason: @"IMAP connection is invalid"
|
||||
|
||||
@@ -11,6 +11,8 @@
|
||||
#import <string.h>
|
||||
#import <objc/objc.h>
|
||||
|
||||
#import <NGExtensions/NSObject+Logs.h>
|
||||
|
||||
static NSMutableDictionary *timezoneCache = nil;
|
||||
|
||||
#define BSONTYPE(tag,className) [className class], [NSNumber numberWithChar: (tag)]
|
||||
@@ -604,7 +606,7 @@ static NSDictionary *BSONTypes()
|
||||
if (tz)
|
||||
[timezoneCache setObject: tz forKey: key];
|
||||
else
|
||||
NSLog(@"ERROR: timezone (%@) not found when deserializing BSON data", key);
|
||||
[self errorWithFormat: @"BSON error: timezone (%@) not found when deserializing BSON data", key];
|
||||
}
|
||||
|
||||
return [NSCalendarDate dateWithYear: year
|
||||
|
||||
@@ -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 \
|
||||
|
||||
@@ -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
|
||||
@@ -78,6 +78,7 @@
|
||||
NSMutableArray *searchAttributes;
|
||||
|
||||
BOOL passwordPolicy;
|
||||
BOOL updateSambaNTLMPasswords;
|
||||
|
||||
/* resources handling */
|
||||
NSString *kindField;
|
||||
|
||||
+63
-21
@@ -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
|
||||
@@ -113,6 +112,7 @@ static Class NSStringK;
|
||||
|
||||
searchAttributes = nil;
|
||||
passwordPolicy = NO;
|
||||
updateSambaNTLMPasswords = NO;
|
||||
|
||||
kindField = nil;
|
||||
multipleBookingsField = nil;
|
||||
@@ -245,6 +245,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"]);
|
||||
}
|
||||
|
||||
@@ -598,6 +601,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 +688,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 +698,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];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -40,6 +40,8 @@
|
||||
- (NSString *) labelForKey: (NSString *) key
|
||||
inContext: (WOContext *) context;
|
||||
|
||||
+ (void) memoryStatistics;
|
||||
|
||||
@end
|
||||
|
||||
#endif /* NSOBJECT+UTILITIES_H */
|
||||
@@ -22,6 +22,7 @@
|
||||
|
||||
#import <Foundation/NSArray.h>
|
||||
#import <Foundation/NSBundle.h>
|
||||
#import <Foundation/NSDebug.h>
|
||||
#import <Foundation/NSEnumerator.h>
|
||||
#import <Foundation/NSString.h>
|
||||
|
||||
@@ -122,4 +123,33 @@
|
||||
return label;
|
||||
}
|
||||
|
||||
//
|
||||
// Set SOGoDebugLeaks = YES in your defaults to enable.
|
||||
//
|
||||
+ (void) memoryStatistics
|
||||
{
|
||||
Class *classList = GSDebugAllocationClassList ();
|
||||
Class *pointer;
|
||||
int i, count, total, peak;
|
||||
NSString *className;
|
||||
|
||||
pointer = classList;
|
||||
i = 0;
|
||||
|
||||
printf("Class count total peak\n");
|
||||
while (pointer[i] != NULL)
|
||||
{
|
||||
className = NSStringFromClass (pointer[i]);
|
||||
count = GSDebugAllocationCount (pointer[i]);
|
||||
total = GSDebugAllocationTotal (pointer[i]);
|
||||
peak = GSDebugAllocationPeak (pointer[i]);
|
||||
|
||||
printf("%s %d %d %d\n", [className UTF8String], count, total, peak);
|
||||
i++;
|
||||
}
|
||||
NSZoneFree(NSDefaultMallocZone(), classList);
|
||||
|
||||
printf("Done!\n");
|
||||
}
|
||||
|
||||
@end
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
@@ -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;
|
||||
|
||||
@@ -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"];
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/* SOGoFolder.h - this file is part of SOGo
|
||||
*
|
||||
* Copyright (C) 2007-2013 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
|
||||
|
||||
@@ -1,8 +1,6 @@
|
||||
/* SOGoFolder.m - this file is part of SOGo
|
||||
*
|
||||
* Copyright (C) 2007-2011 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
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
Copyright (C) 2004-2005 SKYRIX Software AG
|
||||
Copyright (C) 2006-2014 Inverse inc.
|
||||
Copyright (C) 2006-2015 Inverse inc.
|
||||
|
||||
This file is part of SOGo.
|
||||
|
||||
|
||||
@@ -302,7 +302,7 @@ static NSArray *childRecordFields = nil;
|
||||
}
|
||||
|
||||
/* This method fetches the display name defined by the owner, but is also the
|
||||
fallback when a subscriber has not redefined the display name yet in his
|
||||
fallback when a subscriber has not redefined the display name yet in their
|
||||
environment. */
|
||||
- (NSString *) _displayNameFromOwner
|
||||
{
|
||||
@@ -895,7 +895,7 @@ static NSArray *childRecordFields = nil;
|
||||
allUsers = [NSMutableArray arrayWithArray: [aGroup members]];
|
||||
|
||||
// We remove the active user from the group (if present) in order to
|
||||
// not subscribe him to his own resource!
|
||||
// not subscribe him to their own resource!
|
||||
[allUsers removeObject: [context activeUser]];
|
||||
}
|
||||
else
|
||||
@@ -1165,7 +1165,14 @@ static NSArray *childRecordFields = nil;
|
||||
int syncTokenInt;
|
||||
|
||||
fields = [NSMutableArray arrayWithObjects: @"c_name", @"c_component",
|
||||
@"c_creationdate", @"c_lastmodified", nil];
|
||||
@"c_creationdate", @"c_lastmodified", nil];
|
||||
|
||||
if ([[self folderType] isEqualToString: @"Appointment"])
|
||||
{
|
||||
[fields addObject: @"c_enddate"];
|
||||
[fields addObject: @"c_cycleenddate"];
|
||||
}
|
||||
|
||||
addFields = [[properties allValues] objectEnumerator];
|
||||
while ((currentField = [addFields nextObject]))
|
||||
if ([currentField length])
|
||||
@@ -1181,7 +1188,9 @@ static NSArray *childRecordFields = nil;
|
||||
if (theStartDate)
|
||||
{
|
||||
EOQualifier *sinceDateQualifier = [EOQualifier qualifierWithQualifierFormat:
|
||||
@"c_creationdate > %d", (int)[theStartDate timeIntervalSince1970]];
|
||||
@"(c_enddate > %d OR c_enddate = NULL) OR (c_iscycle = 1 and (c_cycleenddate > %d OR c_cycleenddate = NULL))",
|
||||
(int)[theStartDate timeIntervalSince1970],
|
||||
(int)[theStartDate timeIntervalSince1970]];
|
||||
|
||||
qualifier = [[EOAndQualifier alloc] initWithQualifiers: sinceDateQualifier, qualifier,
|
||||
nil];
|
||||
@@ -1211,7 +1220,9 @@ static NSArray *childRecordFields = nil;
|
||||
if (theStartDate)
|
||||
{
|
||||
EOQualifier *sinceDateQualifier = [EOQualifier qualifierWithQualifierFormat:
|
||||
@"c_creationdate > %d", (int)[theStartDate timeIntervalSince1970]];
|
||||
@"(c_enddate > %d OR c_enddate = NULL) OR (c_iscycle = 1 and (c_cycleenddate > %d OR c_cycleenddate = NULL))",
|
||||
(int)[theStartDate timeIntervalSince1970],
|
||||
(int)[theStartDate timeIntervalSince1970]];
|
||||
|
||||
qualifier = [[EOAndQualifier alloc] initWithQualifiers: sinceDateQualifier, qualifier,
|
||||
nil];
|
||||
|
||||
@@ -1,8 +1,6 @@
|
||||
/* SOGoMailer.h - this file is part of SOGo
|
||||
*
|
||||
* Copyright (C) 2007 Inverse inc.
|
||||
*
|
||||
* Author: Wolfgang Sourdeau <wsourdeau@inverse.ca>
|
||||
* Copyright (C) 2007-2014 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
|
||||
|
||||
@@ -1,8 +1,6 @@
|
||||
/* SOGoMailer.m - this file is part of SOGo
|
||||
*
|
||||
* Copyright (C) 2007-2013 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
|
||||
@@ -215,7 +213,7 @@
|
||||
}
|
||||
NS_HANDLER
|
||||
{
|
||||
NSLog(@"Could not connect to the SMTP server %@ on port %d", host, port);
|
||||
[self errorWithFormat: @"Could not connect to the SMTP server %@ on port %d", host, port];
|
||||
result = [NSException exceptionWithHTTPStatus: 500
|
||||
reason: @"cannot send message:"
|
||||
@" (smtp) error when connecting"];
|
||||
|
||||
@@ -1,14 +1,15 @@
|
||||
/*
|
||||
Copyright (C) 2004-2005 SKYRIX Software AG
|
||||
Copyright (C) 2006-2015 Inverse inc.
|
||||
|
||||
This file is part of OpenGroupware.org.
|
||||
This file is part of SOGo.
|
||||
|
||||
OGo is free software; you can redistribute it and/or modify it under
|
||||
SOGo 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, or (at your option) any
|
||||
later version.
|
||||
|
||||
OGo is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||
SOGo 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.
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
/* SOGoObject.m - this file is part of SOGo
|
||||
*
|
||||
* Copyright (C) 2004-2005 SKYRIX Software AG
|
||||
* Copyright (C) 2006-2013 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
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/* SOGoParentFolder.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
|
||||
|
||||
@@ -1,8 +1,6 @@
|
||||
/* SOGoParentFolder.m - this file is part of SOGo
|
||||
*
|
||||
* Copyright (C) 2006-2009 Inverse inc.
|
||||
*
|
||||
* Author: Wolfgang Sourdeau <wsourdeau@inverse.ca>
|
||||
* 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
|
||||
@@ -294,6 +292,18 @@ static SoSecurityManager *sm = nil;
|
||||
subscribedFolder
|
||||
= [subFolderClass folderWithSubscriptionReference: sourceKey
|
||||
inContainer: self];
|
||||
|
||||
// We check with -ocsFolderForPath if the folder also exists in the database.
|
||||
// This is important because user A could delete folder X, and user B has subscribed to it.
|
||||
// If the "default roles" are enabled for calendars/address books, -validatePersmission:.. will
|
||||
// work (grabbing the default role) and the deleted resource will be incorrectly returned.
|
||||
//
|
||||
// FIXME - 2015/01/29 - this fix (24c6c8c91d421594bd51f07904d4cd3911cd187c) was reverted. Normally, we should add
|
||||
// [subscribedFolder ocsFolderForPath: [subscribedFolder ocsPath]] to check the existence of the folder but it causes
|
||||
// massive SQL traffic.
|
||||
//
|
||||
// The proper fix would be to check upon login and only upon login if GCS folders that we're subscribed to are still existent.
|
||||
//
|
||||
if (subscribedFolder
|
||||
&& ![sm validatePermission: SOGoPerm_AccessObject
|
||||
onObject: subscribedFolder
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/* SOGoSieveManager.m - this file is part of SOGo
|
||||
*
|
||||
* Copyright (C) 2010-2014 Inverse inc.
|
||||
* Copyright (C) 2010-2015 Inverse inc.
|
||||
*
|
||||
* Author: Inverse <info@inverse.ca>
|
||||
*
|
||||
@@ -31,6 +31,7 @@
|
||||
#import <SOGo/SOGoDomainDefaults.h>
|
||||
#import <SOGo/SOGoUser.h>
|
||||
|
||||
#import <NGExtensions/NSObject+Logs.h>
|
||||
#import <NGStreams/NGInternetSocketAddress.h>
|
||||
#import <NGImap4/NGSieveClient.h>
|
||||
|
||||
@@ -717,7 +718,7 @@ static NSString *sieveScriptName = @"sogo";
|
||||
client = [[NGSieveClient alloc] initWithURL: url];
|
||||
|
||||
if (!client) {
|
||||
NSLog(@"Sieve connection failed on %@", [url description]);
|
||||
[self errorWithFormat: @"Sieve connection failed on %@", [url description]];
|
||||
return nil;
|
||||
}
|
||||
|
||||
@@ -738,19 +739,19 @@ static NSString *sieveScriptName = @"sogo";
|
||||
|
||||
if (!connected)
|
||||
{
|
||||
NSLog(@"Sieve connection failed on %@", [url description]);
|
||||
[self errorWithFormat: @"Sieve connection failed on %@", [url description]];
|
||||
return nil;
|
||||
}
|
||||
|
||||
if (![[result valueForKey:@"result"] boolValue] && !theUsername && !thePassword) {
|
||||
NSLog(@"failure. Attempting with a renewed password (no authname supported)");
|
||||
[self logWithFormat: @"failure. Attempting with a renewed password (no authname supported)"];
|
||||
password = [theAccount imap4PasswordRenewed: YES];
|
||||
result = [client login: login password: password];
|
||||
}
|
||||
|
||||
if (![[result valueForKey:@"result"] boolValue]) {
|
||||
NSLog(@"Could not login '%@' on Sieve server: %@: %@",
|
||||
login, client, result);
|
||||
[self logWithFormat: @"Could not login '%@' on Sieve server: %@: %@",
|
||||
login, client, result];
|
||||
[client closeConnection];
|
||||
return nil;
|
||||
}
|
||||
@@ -823,7 +824,7 @@ static NSString *sieveScriptName = @"sogo";
|
||||
}
|
||||
else
|
||||
{
|
||||
NSLog(@"Sieve generation failure: %@", [self lastScriptError]);
|
||||
[self errorWithFormat: @"Sieve generation failure: %@", [self lastScriptError]];
|
||||
[client closeConnection];
|
||||
return NO;
|
||||
}
|
||||
@@ -834,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;
|
||||
@@ -848,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];
|
||||
}
|
||||
|
||||
|
||||
@@ -913,7 +927,7 @@ static NSString *sieveScriptName = @"sogo";
|
||||
result = [client deleteScript: sieveScriptName];
|
||||
|
||||
if (![[result valueForKey:@"result"] boolValue]) {
|
||||
NSLog(@"WARNING: Could not delete Sieve script - continuing...: %@", result);
|
||||
[self logWithFormat: @"WARNING: Could not delete Sieve script - continuing...: %@", result];
|
||||
}
|
||||
|
||||
// We put and activate the script only if we actually have a script
|
||||
@@ -923,14 +937,14 @@ static NSString *sieveScriptName = @"sogo";
|
||||
result = [client putScript: sieveScriptName script: script];
|
||||
|
||||
if (![[result valueForKey:@"result"] boolValue]) {
|
||||
NSLog(@"Could not upload Sieve script: %@", result);
|
||||
[self logWithFormat: @"Could not upload Sieve script: %@", result];
|
||||
[client closeConnection];
|
||||
return NO;
|
||||
}
|
||||
|
||||
result = [client setActiveScript: sieveScriptName];
|
||||
if (![[result valueForKey:@"result"] boolValue]) {
|
||||
NSLog(@"Could not enable Sieve script: %@", result);
|
||||
[self logWithFormat: @"Could not enable Sieve script: %@", result];
|
||||
[client closeConnection];
|
||||
return NO;
|
||||
}
|
||||
|
||||
@@ -99,6 +99,7 @@
|
||||
- (int) maximumSyncInterval;
|
||||
- (int) internalSyncInterval;
|
||||
- (int) maximumSyncWindowSize;
|
||||
- (int) maximumSyncResponseSize;
|
||||
|
||||
@end
|
||||
|
||||
|
||||
@@ -636,4 +636,16 @@ _injectConfigurationFromFile (NSMutableDictionary *defaultsDict,
|
||||
return [self integerForKey: @"SOGoMaximumSyncWindowSize"];
|
||||
}
|
||||
|
||||
- (int) maximumSyncResponseSize
|
||||
{
|
||||
int v;
|
||||
|
||||
v = [self integerForKey: @"SOGoMaximumSyncResponseSize"];
|
||||
|
||||
if (v > 0)
|
||||
v = v * 1024;
|
||||
|
||||
return v;
|
||||
}
|
||||
|
||||
@end
|
||||
@@ -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.
|
||||
|
||||
@@ -31,7 +31,7 @@
|
||||
Child objects:
|
||||
'Calendar': SOGoAppointmentFolder
|
||||
|
||||
The SOGoUserFolder is the "home directory" of the user where all his
|
||||
The SOGoUserFolder is the "home directory" of the user where all their
|
||||
processing starts. It is the 'znek' in such a path:
|
||||
/SOGo/so/znek/Calendar
|
||||
*/
|
||||
|
||||
@@ -1,9 +1,6 @@
|
||||
/* SOGoUserManager.h - this file is part of SOGo
|
||||
*
|
||||
* Copyright (C) 2007-2013 Inverse inc.
|
||||
*
|
||||
* Author: Wolfgang Sourdeau <wsourdeau@inverse.ca>
|
||||
* Francis Lachapelle <flachapelle@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
|
||||
|
||||
@@ -1,9 +1,6 @@
|
||||
/* SOGoUserManager.m - this file is part of SOGo
|
||||
*
|
||||
* Copyright (C) 2007-2013 Inverse inc.
|
||||
*
|
||||
* Author: Wolfgang Sourdeau <wsourdeau@inverse.ca>
|
||||
* Francis Lachapelle <flachapelle@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
|
||||
|
||||
@@ -1,8 +1,6 @@
|
||||
/* SOGoWebDAVAclManager.m - this file is part of SOGo
|
||||
*
|
||||
* Copyright (C) 2008 Inverse inc.
|
||||
*
|
||||
* Author: Wolfgang Sourdeau <wsourdeau@inverse.ca>
|
||||
* Copyright (C) 2008-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
|
||||
|
||||
@@ -1,8 +1,6 @@
|
||||
/* WOContext+SOGo.h - this file is part of SOGo
|
||||
*
|
||||
* Copyright (C) 2008 Inverse inc.
|
||||
*
|
||||
* Author: Francis Lachapelle <flachapelle@inverse.ca>
|
||||
* Copyright (C) 2008-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
|
||||
|
||||
@@ -1,8 +1,6 @@
|
||||
/* WOContext+SOGo.m - this file is part of SOGo
|
||||
*
|
||||
* Copyright (C) 2008 Inverse inc.
|
||||
*
|
||||
* Author: Francis Lachapelle <flachapelle@inverse.ca>
|
||||
* Copyright (C) 2008-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
|
||||
|
||||
@@ -1,8 +1,6 @@
|
||||
/* WORequest+SOGo.h - this file is part of SOGo
|
||||
*
|
||||
* Copyright (C) 2007 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
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/* WORequest+SOGo.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
|
||||
@@ -127,10 +127,14 @@
|
||||
|| [self isAppleDAVWithSubstring: @"iOS/"];
|
||||
}
|
||||
|
||||
//
|
||||
// Mac+OS+X/10.10.1 (14B25) CalendarAgent/315
|
||||
//
|
||||
- (BOOL) isICal
|
||||
{
|
||||
return ([self isAppleDAVWithSubstring: @"Mac OS X/10."]
|
||||
|| [self isAppleDAVWithSubstring: @"Mac_OS_X/"]
|
||||
|| [self isAppleDAVWithSubstring: @"Mac+OS+X/"]
|
||||
|| [self isAppleDAVWithSubstring: @"CoreDAV/"]);
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,742 @@
|
||||
#include "lmhash.h"
|
||||
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
|
||||
/* ========================================================================== **
|
||||
*
|
||||
* LMhash.h
|
||||
*
|
||||
* Copyright:
|
||||
* Copyright (C) 2004 by Christopher R. Hertel
|
||||
*
|
||||
* Email: crh@ubiqx.mn.org
|
||||
*
|
||||
* $Id: LMhash.h,v 0.1 2004/05/30 02:26:31 crh Exp $
|
||||
*
|
||||
* -------------------------------------------------------------------------- **
|
||||
*
|
||||
* Description:
|
||||
*
|
||||
* Implemention of the LAN Manager hash (LM hash) and LM response
|
||||
* algorithms.
|
||||
*
|
||||
* -------------------------------------------------------------------------- **
|
||||
*
|
||||
* License:
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*
|
||||
* -------------------------------------------------------------------------- **
|
||||
*
|
||||
* Notes:
|
||||
*
|
||||
* This module implements the LM hash. The NT hash is simply the MD4() of
|
||||
* the password, so we don't need a separate implementation of that. This
|
||||
* module also implements the LM response, which can be combined with the
|
||||
* NT hash to produce the NTLM response.
|
||||
*
|
||||
* This implementation was created based on the description in my own book.
|
||||
* The book description was, in turn, written after studying many existing
|
||||
* examples in various documentation. Jeremy Allison and Andrew Tridgell
|
||||
* deserve lots of credit for having figured out the secrets of Lan Manager
|
||||
* authentication many years ago.
|
||||
*
|
||||
* See:
|
||||
* Implementing CIFS - the Common Internet File System
|
||||
* by your truly. ISBN 0-13-047116-X, Prentice Hall PTR., August 2003
|
||||
* Section 15.3, in particular.
|
||||
* (Online at: http://ubiqx.org/cifs/SMB.html#SMB.8.3)
|
||||
*
|
||||
* ========================================================================== **
|
||||
*/
|
||||
|
||||
|
||||
/* Initial permutation map.
|
||||
* In the first step of DES, the bits of the initial plaintext are rearranged
|
||||
* according to the map given below. This map and those like it are read by
|
||||
* the Permute() function (below) which uses the maps as a guide when moving
|
||||
* bits from one place to another.
|
||||
*
|
||||
* Note that the values here are all one less than those shown in Schneier.
|
||||
* That's because C likes to start counting from 0, not 1.
|
||||
*
|
||||
* According to Schneier (Ch12, pg 271), the purpose of the initial
|
||||
* permutation was to make it easier to load plaintext and ciphertext into
|
||||
* a DES ecryption chip. I have no idea why that would be the case.
|
||||
*/
|
||||
static const uint8_t InitialPermuteMap[64] =
|
||||
{
|
||||
57, 49, 41, 33, 25, 17, 9, 1,
|
||||
59, 51, 43, 35, 27, 19, 11, 3,
|
||||
61, 53, 45, 37, 29, 21, 13, 5,
|
||||
63, 55, 47, 39, 31, 23, 15, 7,
|
||||
56, 48, 40, 32, 24, 16, 8, 0,
|
||||
58, 50, 42, 34, 26, 18, 10, 2,
|
||||
60, 52, 44, 36, 28, 20, 12, 4,
|
||||
62, 54, 46, 38, 30, 22, 14, 6
|
||||
};
|
||||
|
||||
|
||||
/* Key permutation map.
|
||||
* Like the input data and encryption result, the key is permuted before
|
||||
* the algorithm really gets going. The original algorithm called for an
|
||||
* eight-byte key in which each byte contained a parity bit. During the
|
||||
* key permutiation, the parity bits were discarded. The DES algorithm,
|
||||
* as used with SMB, does not make use of the parity bits. Instead, SMB
|
||||
* passes 7-byte keys to DES. For DES implementations that expect parity,
|
||||
* the parity bits must be added. In this case, however, we're just going
|
||||
* to start with a 7-byte (56 bit) key. KeyPermuteMap, below, is adjusted
|
||||
* accordingly and, of course, each entry in the map is reduced by 1 with
|
||||
* respect to the documented values because C likes to start counting from
|
||||
* 0, not 1.
|
||||
*/
|
||||
static const uint8_t KeyPermuteMap[56] =
|
||||
{
|
||||
49, 42, 35, 28, 21, 14, 7, 0,
|
||||
50, 43, 36, 29, 22, 15, 8, 1,
|
||||
51, 44, 37, 30, 23, 16, 9, 2,
|
||||
52, 45, 38, 31, 55, 48, 41, 34,
|
||||
27, 20, 13, 6, 54, 47, 40, 33,
|
||||
26, 19, 12, 5, 53, 46, 39, 32,
|
||||
25, 18, 11, 4, 24, 17, 10, 3,
|
||||
};
|
||||
|
||||
|
||||
/* Key rotation table.
|
||||
* At the start of each round of encryption, the key is split and each
|
||||
* 28-bit half is rotated left. The number of bits of rotation per round
|
||||
* is given in the table below.
|
||||
*/
|
||||
static const uint8_t KeyRotation[16] =
|
||||
{ 1, 1, 2, 2, 2, 2, 2, 2, 1, 2, 2, 2, 2, 2, 2, 1 };
|
||||
|
||||
|
||||
/* Key compression table.
|
||||
* This table is used to select 48 of the 56 bits of the key.
|
||||
* The left and right halves of the source text are each 32 bits,
|
||||
* but they are expanded to 48 bits and the results are XOR'd
|
||||
* against the compressed (48-bit) key.
|
||||
*/
|
||||
static const uint8_t KeyCompression[48] =
|
||||
{
|
||||
13, 16, 10, 23, 0, 4, 2, 27,
|
||||
14, 5, 20, 9, 22, 18, 11, 3,
|
||||
25, 7, 15, 6, 26, 19, 12, 1,
|
||||
40, 51, 30, 36, 46, 54, 29, 39,
|
||||
50, 44, 32, 47, 43, 48, 38, 55,
|
||||
33, 52, 45, 41, 49, 35, 28, 31
|
||||
};
|
||||
|
||||
|
||||
/* Data expansion table.
|
||||
* This table is used after the data block (64-bits) has been split
|
||||
* into two 32-bit (4-byte) halves (generally denoted L and R).
|
||||
* Each 32-bit half is "expanded", using this table, to a 48 bit
|
||||
* data block, which is then XOR'd with the 48 bit subkey for the
|
||||
* round.
|
||||
*/
|
||||
static const uint8_t DataExpansion[48] =
|
||||
{
|
||||
31, 0, 1, 2, 3, 4, 3, 4,
|
||||
5, 6, 7, 8, 7, 8, 9, 10,
|
||||
11, 12, 11, 12, 13, 14, 15, 16,
|
||||
15, 16, 17, 18, 19, 20, 19, 20,
|
||||
21, 22, 23, 24, 23, 24, 25, 26,
|
||||
27, 28, 27, 28, 29, 30, 31, 0
|
||||
};
|
||||
|
||||
|
||||
/* The (in)famous S-boxes.
|
||||
* These are used to perform substitutions.
|
||||
* Six bits worth of input will return four bits of output.
|
||||
* The four bit values are stored in these tables. Each table has
|
||||
* 64 entries...and 6 bits provides a number between 0 and 63.
|
||||
* There are eight S-boxes, one per 6 bits of a 48-bit value.
|
||||
* Thus, 48 bits are reduced to 32 bits. Obviously, this step
|
||||
* follows the DataExpansion step.
|
||||
*
|
||||
* Note that the literature generally shows this as 8 arrays each
|
||||
* with four rows and 16 colums. There is a complex formula for
|
||||
* mapping the 6 bit input values to the correct row and column.
|
||||
* I've pre-computed that mapping, and the tables below provide
|
||||
* direct 6-bit input to 4-bit output. See pp 274-274 in Schneier.
|
||||
*/
|
||||
static const uint8_t SBox[8][64] =
|
||||
{
|
||||
{ /* S0 */
|
||||
14, 0, 4, 15, 13, 7, 1, 4, 2, 14, 15, 2, 11, 13, 8, 1,
|
||||
3, 10, 10, 6, 6, 12, 12, 11, 5, 9, 9, 5, 0, 3, 7, 8,
|
||||
4, 15, 1, 12, 14, 8, 8, 2, 13, 4, 6, 9, 2, 1, 11, 7,
|
||||
15, 5, 12, 11, 9, 3, 7, 14, 3, 10, 10, 0, 5, 6, 0, 13
|
||||
},
|
||||
{ /* S1 */
|
||||
15, 3, 1, 13, 8, 4, 14, 7, 6, 15, 11, 2, 3, 8, 4, 14,
|
||||
9, 12, 7, 0, 2, 1, 13, 10, 12, 6, 0, 9, 5, 11, 10, 5,
|
||||
0, 13, 14, 8, 7, 10, 11, 1, 10, 3, 4, 15, 13, 4, 1, 2,
|
||||
5, 11, 8, 6, 12, 7, 6, 12, 9, 0, 3, 5, 2, 14, 15, 9
|
||||
},
|
||||
{ /* S2 */
|
||||
10, 13, 0, 7, 9, 0, 14, 9, 6, 3, 3, 4, 15, 6, 5, 10,
|
||||
1, 2, 13, 8, 12, 5, 7, 14, 11, 12, 4, 11, 2, 15, 8, 1,
|
||||
13, 1, 6, 10, 4, 13, 9, 0, 8, 6, 15, 9, 3, 8, 0, 7,
|
||||
11, 4, 1, 15, 2, 14, 12, 3, 5, 11, 10, 5, 14, 2, 7, 12
|
||||
},
|
||||
{ /* S3 */
|
||||
7, 13, 13, 8, 14, 11, 3, 5, 0, 6, 6, 15, 9, 0, 10, 3,
|
||||
1, 4, 2, 7, 8, 2, 5, 12, 11, 1, 12, 10, 4, 14, 15, 9,
|
||||
10, 3, 6, 15, 9, 0, 0, 6, 12, 10, 11, 1, 7, 13, 13, 8,
|
||||
15, 9, 1, 4, 3, 5, 14, 11, 5, 12, 2, 7, 8, 2, 4, 14
|
||||
},
|
||||
{ /* S4 */
|
||||
2, 14, 12, 11, 4, 2, 1, 12, 7, 4, 10, 7, 11, 13, 6, 1,
|
||||
8, 5, 5, 0, 3, 15, 15, 10, 13, 3, 0, 9, 14, 8, 9, 6,
|
||||
4, 11, 2, 8, 1, 12, 11, 7, 10, 1, 13, 14, 7, 2, 8, 13,
|
||||
15, 6, 9, 15, 12, 0, 5, 9, 6, 10, 3, 4, 0, 5, 14, 3
|
||||
},
|
||||
{ /* S5 */
|
||||
12, 10, 1, 15, 10, 4, 15, 2, 9, 7, 2, 12, 6, 9, 8, 5,
|
||||
0, 6, 13, 1, 3, 13, 4, 14, 14, 0, 7, 11, 5, 3, 11, 8,
|
||||
9, 4, 14, 3, 15, 2, 5, 12, 2, 9, 8, 5, 12, 15, 3, 10,
|
||||
7, 11, 0, 14, 4, 1, 10, 7, 1, 6, 13, 0, 11, 8, 6, 13
|
||||
},
|
||||
{ /* S6 */
|
||||
4, 13, 11, 0, 2, 11, 14, 7, 15, 4, 0, 9, 8, 1, 13, 10,
|
||||
3, 14, 12, 3, 9, 5, 7, 12, 5, 2, 10, 15, 6, 8, 1, 6,
|
||||
1, 6, 4, 11, 11, 13, 13, 8, 12, 1, 3, 4, 7, 10, 14, 7,
|
||||
10, 9, 15, 5, 6, 0, 8, 15, 0, 14, 5, 2, 9, 3, 2, 12
|
||||
},
|
||||
{ /* S7 */
|
||||
13, 1, 2, 15, 8, 13, 4, 8, 6, 10, 15, 3, 11, 7, 1, 4,
|
||||
10, 12, 9, 5, 3, 6, 14, 11, 5, 0, 0, 14, 12, 9, 7, 2,
|
||||
7, 2, 11, 1, 4, 14, 1, 7, 9, 4, 12, 10, 14, 8, 2, 13,
|
||||
0, 15, 6, 12, 10, 9, 13, 0, 15, 3, 3, 5, 5, 6, 8, 11
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
/* P-Box permutation.
|
||||
* This permutation is applied to the result of the S-Box Substitutions.
|
||||
* It's a straight-forward re-arrangement of the bits.
|
||||
*/
|
||||
static const uint8_t PBox[32] =
|
||||
{
|
||||
15, 6, 19, 20, 28, 11, 27, 16,
|
||||
0, 14, 22, 25, 4, 17, 30, 9,
|
||||
1, 7, 23, 13, 31, 26, 2, 8,
|
||||
18, 12, 29, 5, 21, 10, 3, 24
|
||||
};
|
||||
|
||||
|
||||
/* Final permutation map.
|
||||
* This is supposed to be the inverse of the Initial Permutation,
|
||||
* but there's been a bit of fiddling done.
|
||||
* As always, the values given are one less than those in the literature
|
||||
* (because C starts counting from 0, not 1). In addition, the penultimate
|
||||
* step in DES is to swap the left and right hand sides of the ciphertext.
|
||||
* The inverse of the Initial Permutation is then applied to produce the
|
||||
* final result.
|
||||
* To save a step, the map below does the left/right swap as well as the
|
||||
* inverse permutation.
|
||||
*/
|
||||
static const uint8_t FinalPermuteMap[64] =
|
||||
{
|
||||
7, 39, 15, 47, 23, 55, 31, 63,
|
||||
6, 38, 14, 46, 22, 54, 30, 62,
|
||||
5, 37, 13, 45, 21, 53, 29, 61,
|
||||
4, 36, 12, 44, 20, 52, 28, 60,
|
||||
3, 35, 11, 43, 19, 51, 27, 59,
|
||||
2, 34, 10, 42, 18, 50, 26, 58,
|
||||
1, 33, 9, 41, 17, 49, 25, 57,
|
||||
0, 32, 8, 40, 16, 48, 24, 56
|
||||
};
|
||||
|
||||
|
||||
/* -------------------------------------------------------------------------- **
|
||||
* Macros:
|
||||
*
|
||||
* CLRBIT( STR, IDX )
|
||||
* Input: STR - (uchar *) pointer to an array of 8-bit bytes.
|
||||
* IDX - (int) bitwise index of a bit within the STR array
|
||||
* that is to be cleared (that is, given a value of 0).
|
||||
* Notes: This macro clears a bit within an array of bits (which is
|
||||
* built within an array of bytes).
|
||||
* - The macro converts to an assignment of the form A &= B.
|
||||
* - The string of bytes is viewed as an array of bits, read from
|
||||
* highest order bit first. The highest order bit of a byte
|
||||
* would, therefore, be bit 0 (within that byte).
|
||||
*
|
||||
* SETBIT( STR, IDX )
|
||||
* Input: STR - (uchar *) pointer to an array of 8-bit bytes.
|
||||
* IDX - (int) bitwise index of a bit within the STR array
|
||||
* that is to be set (that is, given a value of 1).
|
||||
* Notes: This macro sets a bit within an array of bits (which is
|
||||
* built within an array of bytes).
|
||||
* - The macro converts to an assignment of the form A |= B.
|
||||
* - The string of bytes is viewed as an array of bits, read from
|
||||
* highest order bit first. The highest order bit of a byte
|
||||
* would, therefore, be bit 0 (within that byte).
|
||||
*
|
||||
* GETBIT( STR, IDX )
|
||||
* Input: STR - (uchar *) pointer to an array of 8-bit bytes.
|
||||
* IDX - (int) bit-wise index of a bit within the STR array
|
||||
* that is to be read.
|
||||
* Output: True (1) if the indexed bit was set, else false (0).
|
||||
*
|
||||
* -------------------------------------------------------------------------- **
|
||||
*/
|
||||
|
||||
#define CLRBIT( STR, IDX ) ( (STR)[(IDX)/8] &= ~(0x01 << (7 - ((IDX)%8))) )
|
||||
|
||||
#define SETBIT( STR, IDX ) ( (STR)[(IDX)/8] |= (0x01 << (7 - ((IDX)%8))) )
|
||||
|
||||
#define GETBIT( STR, IDX ) (( ((STR)[(IDX)/8]) >> (7 - ((IDX)%8)) ) & 0x01)
|
||||
|
||||
|
||||
/* -------------------------------------------------------------------------- **
|
||||
* Static Functions:
|
||||
*/
|
||||
|
||||
static void Permute( uchar *dst,
|
||||
const uchar *src,
|
||||
const uint8_t *map,
|
||||
const int mapsize )
|
||||
/* ------------------------------------------------------------------------ **
|
||||
* Performs a DES permutation, which re-arranges the bits in an array of
|
||||
* bytes.
|
||||
*
|
||||
* Input: dst - Destination into which to put the re-arranged bits.
|
||||
* src - Source from which to read the bits.
|
||||
* map - Permutation map.
|
||||
* mapsize - Number of bytes represented by the <map>. This also
|
||||
* represents the number of bytes to be copied to <dst>.
|
||||
*
|
||||
* Output: none.
|
||||
*
|
||||
* Notes: <src> and <dst> must not point to the same location.
|
||||
*
|
||||
* - No checks are done to ensure that there is enough room
|
||||
* in <dst>, or that the bit numbers in <map> do not exceed
|
||||
* the bits available in <src>. A good reason to make this
|
||||
* function static (private).
|
||||
*
|
||||
* - The <mapsize> value is in bytes. All permutations in DES
|
||||
* use tables that are a multiple of 8 bits, so there is no
|
||||
* need to handle partial bytes. (Yes, I know that there
|
||||
* are some machines out there that still use bytes of a size
|
||||
* other than 8 bits. For our purposes we'll stick with 8-bit
|
||||
* bytes.)
|
||||
*
|
||||
* ------------------------------------------------------------------------ **
|
||||
*/
|
||||
{
|
||||
int bitcount;
|
||||
int i;
|
||||
|
||||
/* Clear all bits in the destination.
|
||||
*/
|
||||
for( i = 0; i < mapsize; i++ )
|
||||
dst[i] = 0;
|
||||
|
||||
/* Set destination bit if the mapped source bit it set. */
|
||||
bitcount = mapsize * 8;
|
||||
for( i = 0; i < bitcount; i++ )
|
||||
{
|
||||
if( GETBIT( src, map[i] ) )
|
||||
SETBIT( dst, i );
|
||||
}
|
||||
} /* Permute */
|
||||
|
||||
|
||||
static void KeyShift( uchar *key, const int numbits )
|
||||
/* ------------------------------------------------------------------------ **
|
||||
* Split the 56-bit key in half & left rotate each half by <numbits> bits.
|
||||
*
|
||||
* Input: key - The 56-bit key to be split-rotated.
|
||||
* numbits - The number of bits by which to rotate the key.
|
||||
*
|
||||
* Output: none.
|
||||
*
|
||||
* Notes: There are probably several better ways to implement this.
|
||||
*
|
||||
* ------------------------------------------------------------------------ **
|
||||
*/
|
||||
{
|
||||
int i;
|
||||
uchar keep = key[0]; /* Copy the highest order bits of the key. */
|
||||
|
||||
/* Repeat the shift process <numbits> times.
|
||||
*/
|
||||
for( i = 0; i < numbits; i++ )
|
||||
{
|
||||
int j;
|
||||
|
||||
/* Shift the entire thing, byte by byte.
|
||||
*/
|
||||
for( j = 0; j < 7; j++ )
|
||||
{
|
||||
if( j && (key[j] & 0x80) ) /* If the top bit of this byte is set. */
|
||||
key[j-1] |= 0x01; /* ...shift it to last byte's low bit. */
|
||||
key[j] <<= 1; /* Then left-shift the whole byte. */
|
||||
}
|
||||
|
||||
/* Now move the high-order bits of each 28-bit half-key to their
|
||||
* correct locations.
|
||||
* Bit 27 is the lowest order bit of the first half-key.
|
||||
* Before the shift, it was the highest order bit of the 2nd half-key.
|
||||
*/
|
||||
if( GETBIT( key, 27 ) ) /* If bit 27 is set... */
|
||||
{
|
||||
CLRBIT( key, 27 ); /* ...clear bit 27. */
|
||||
SETBIT( key, 55 ); /* ...set lowest order bit of 2nd half-key. */
|
||||
}
|
||||
|
||||
/* We kept the highest order bit of the first half-key in <keep>.
|
||||
* If it's set, copy it to bit 27.
|
||||
*/
|
||||
if( keep & 0x80 )
|
||||
SETBIT( key, 27 );
|
||||
|
||||
/* Rotate the <keep> byte too, in case <numbits> is 2 and there's
|
||||
* a second round coming.
|
||||
*/
|
||||
keep <<= 1;
|
||||
}
|
||||
} /* KeyShift */
|
||||
|
||||
|
||||
static void sbox( uchar *dst, const uchar *src )
|
||||
/* ------------------------------------------------------------------------ **
|
||||
* Perform S-Box substitutions.
|
||||
*
|
||||
* Input: dst - Destination byte array into which the S-Box substituted
|
||||
* bitmap will be written.
|
||||
* src - Source byte array.
|
||||
*
|
||||
* Output: none.
|
||||
*
|
||||
* Notes: It's really not possible (for me, anyway) to understand how
|
||||
* this works without reading one or more detailed explanations.
|
||||
* Quick overview, though:
|
||||
*
|
||||
* After the DataExpansion step (in which a 32-bit bit array is
|
||||
* expanded to a 48-bit bit array) the expanded data block is
|
||||
* XOR'd with 48-bits worth of key. That 48 bits then needs to
|
||||
* be condensed back into 32 bits.
|
||||
*
|
||||
* The S-Box substitution handles the data reduction by breaking
|
||||
* the 48-bit value into eight 6-bit values. For each of these
|
||||
* 6-bit values there is a table (an S-Box table). The table
|
||||
* contains 64 possible values. Conveniently, a 6-bit integer
|
||||
* can represent a value between 0 and 63.
|
||||
*
|
||||
* So, if you think of the 48-bit bit array as an array of 6-bit
|
||||
* integers, you use S-Box table 0 with the 0th 6-bit value.
|
||||
* Table 1 is used with the 6-bit value #1, and so on until #7.
|
||||
* Within each table, the correct substitution is found based
|
||||
* simply on the value of the 6-bit integer.
|
||||
*
|
||||
* Well, the original algorithm (and most documentation) don't
|
||||
* make it so simple. There's a complex formula for mapping
|
||||
* the 6-bit values to the correct substitution. Fortunately,
|
||||
* those lookups can be precomputed (and have been for this
|
||||
* implementation). See pp 274-274 in Schneier.
|
||||
*
|
||||
* Oh, and the substitute values are all 4-bit values, so each
|
||||
* 6-bits gets reduced to 4-bits resulting in a 32-bit bit array.
|
||||
*
|
||||
* ------------------------------------------------------------------------ **
|
||||
*/
|
||||
{
|
||||
int i;
|
||||
|
||||
/* Clear the destination array.
|
||||
*/
|
||||
for( i = 0; i < 4; i++ )
|
||||
dst[i] = 0;
|
||||
|
||||
/* For each set of six input bits...
|
||||
*/
|
||||
for( i = 0; i < 8; i++ )
|
||||
{
|
||||
int j;
|
||||
int Snum;
|
||||
int bitnum;
|
||||
|
||||
/* Extract the 6-bit integer from the source.
|
||||
* This will be the lookup key within the SBox[i] array.
|
||||
*/
|
||||
for( Snum = j = 0, bitnum = (i * 6); j < 6; j++, bitnum++ )
|
||||
{
|
||||
Snum <<= 1;
|
||||
Snum |= GETBIT( src, bitnum );
|
||||
}
|
||||
|
||||
/* Find the correct value in the correct SBox[]
|
||||
* and copy it into the destination.
|
||||
* Left shift the nibble four bytes for even values of <i>.
|
||||
*/
|
||||
if( 0 == (i%2) )
|
||||
dst[i/2] |= ((SBox[i][Snum]) << 4);
|
||||
else
|
||||
dst[i/2] |= SBox[i][Snum];
|
||||
}
|
||||
} /* sbox */
|
||||
|
||||
|
||||
static void xor( uchar *dst, const uchar *a, const uchar *b, const int count )
|
||||
/* ------------------------------------------------------------------------ **
|
||||
* Perform an XOR operation on two byte arrays.
|
||||
*
|
||||
* Input: dst - Destination array to which the result will be written.
|
||||
* a - The first string of bytes.
|
||||
* b - The second string of bytes.
|
||||
* count - Number of bytes to XOR against one another.
|
||||
*
|
||||
* Output: none.
|
||||
*
|
||||
* Notes: This function operates on whole byte chunks. There's no need
|
||||
* to XOR partial bytes so no need to write code to handle it.
|
||||
*
|
||||
* - This function essentially implements dst = a ^ b; for byte
|
||||
* arrays.
|
||||
*
|
||||
* - <dst> may safely point to the same location as <a> or <b>.
|
||||
*
|
||||
* ------------------------------------------------------------------------ **
|
||||
*/
|
||||
{
|
||||
int i;
|
||||
|
||||
for( i = 0; i < count; i++ )
|
||||
dst[i] = a[i] ^ b[i];
|
||||
} /* xor */
|
||||
|
||||
|
||||
/* -------------------------------------------------------------------------- **
|
||||
* Functions:
|
||||
*/
|
||||
|
||||
uchar *auth_DESkey8to7( uchar *dst, const uchar *key )
|
||||
/* ------------------------------------------------------------------------ **
|
||||
* Compress an 8-byte DES key to its 7-byte form.
|
||||
*
|
||||
* Input: dst - Pointer to a memory location (minimum 7 bytes) to accept
|
||||
* the compressed key.
|
||||
* key - Pointer to an 8-byte DES key. See the notes below.
|
||||
*
|
||||
* Output: A pointer to the compressed key (same as <dst>) or NULL if
|
||||
* either <src> or <dst> were NULL.
|
||||
*
|
||||
* Notes: There are no checks done to ensure that <dst> and <key> point
|
||||
* to sufficient space. Please be carefull.
|
||||
*
|
||||
* The two pointers, <dst> and <key> may point to the same
|
||||
* memory location. Internally, a temporary buffer is used and
|
||||
* the results are copied back to <dst>.
|
||||
*
|
||||
* The DES algorithm uses 8 byte keys by definition. The first
|
||||
* step in the algorithm, however, involves removing every eigth
|
||||
* bit to produce a 56-bit key (seven bytes). SMB authentication
|
||||
* skips this step and uses 7-byte keys. The <auth_DEShash()>
|
||||
* algorithm in this module expects 7-byte keys. This function
|
||||
* is used to convert an 8-byte DES key into a 7-byte SMB DES key.
|
||||
*
|
||||
* ------------------------------------------------------------------------ **
|
||||
*/
|
||||
{
|
||||
int i;
|
||||
uchar tmp[7];
|
||||
static const uint8_t map8to7[56] =
|
||||
{
|
||||
0, 1, 2, 3, 4, 5, 6,
|
||||
8, 9, 10, 11, 12, 13, 14,
|
||||
16, 17, 18, 19, 20, 21, 22,
|
||||
24, 25, 26, 27, 28, 29, 30,
|
||||
32, 33, 34, 35, 36, 37, 38,
|
||||
40, 41, 42, 43, 44, 45, 46,
|
||||
48, 49, 50, 51, 52, 53, 54,
|
||||
56, 57, 58, 59, 60, 61, 62
|
||||
};
|
||||
|
||||
if( (NULL == dst) || (NULL == key) )
|
||||
return( NULL );
|
||||
|
||||
Permute( tmp, key, map8to7, 7 );
|
||||
for( i = 0; i < 7; i++ )
|
||||
dst[i] = tmp[i];
|
||||
|
||||
return( dst );
|
||||
} /* auth_DESkey8to7 */
|
||||
|
||||
|
||||
uchar *auth_DEShash( uchar *dst, const uchar *key, const uchar *src )
|
||||
/* ------------------------------------------------------------------------ **
|
||||
* DES encryption of the input data using the input key.
|
||||
*
|
||||
* Input: dst - Destination buffer. It *must* be at least eight bytes
|
||||
* in length, to receive the encrypted result.
|
||||
* key - Encryption key. Exactly seven bytes will be used.
|
||||
* If your key is shorter, ensure that you pad it to seven
|
||||
* bytes.
|
||||
* src - Source data to be encrypted. Exactly eight bytes will
|
||||
* be used. If your source data is shorter, ensure that
|
||||
* you pad it to eight bytes.
|
||||
*
|
||||
* Output: A pointer to the encrpyted data (same as <dst>).
|
||||
*
|
||||
* Notes: In SMB, the DES function is used as a hashing function rather
|
||||
* than an encryption/decryption tool. When used for generating
|
||||
* the LM hash the <src> input is the known value "KGS!@#$%" and
|
||||
* the key is derived from the password entered by the user.
|
||||
* When used to generate the LM or NTLM response, the <key> is
|
||||
* derived from the LM or NTLM hash, and the challenge is used
|
||||
* as the <src> input.
|
||||
* See: http://ubiqx.org/cifs/SMB.html#SMB.8.3
|
||||
*
|
||||
* - This function is called "DEShash" rather than just "DES"
|
||||
* because it is only used for creating LM hashes and the
|
||||
* LM/NTLM responses. For all practical purposes, however, it
|
||||
* is a full DES encryption implementation.
|
||||
*
|
||||
* - This DES implementation does not need to be fast, nor is a
|
||||
* DES decryption function needed. The goal is to keep the
|
||||
* code small, simple, and well documented.
|
||||
*
|
||||
* - The input values are copied and refiddled within the module
|
||||
* and the result is not written to <dst> until the very last
|
||||
* step, so it's okay if <dst> points to the same memory as
|
||||
* <key> or <src>.
|
||||
*
|
||||
* ------------------------------------------------------------------------ **
|
||||
*/
|
||||
{
|
||||
int i; /* Loop counter. */
|
||||
uchar K[7]; /* Holds the key, as we manipulate it. */
|
||||
uchar D[8]; /* The data block, as we manipulate it. */
|
||||
|
||||
/* Create the permutations of the key and the source.
|
||||
*/
|
||||
Permute( K, key, KeyPermuteMap, 7 );
|
||||
Permute( D, src, InitialPermuteMap, 8 );
|
||||
|
||||
/* DES encryption proceeds in 16 rounds.
|
||||
* The stuff inside the loop is known in the literature as "function f".
|
||||
*/
|
||||
for( i = 0; i < 16; i++ )
|
||||
{
|
||||
int j;
|
||||
uchar *L = D; /* The left 4 bytes (half) of the data block. */
|
||||
uchar *R = &(D[4]); /* The right half of the ciphertext block. */
|
||||
uchar Rexp[6]; /* Expanded right half. */
|
||||
uchar Rn[4]; /* New value of R, as we manipulate it. */
|
||||
uchar SubK[6]; /* The 48-bit subkey. */
|
||||
|
||||
/* Generate the subkey for this round.
|
||||
*/
|
||||
KeyShift( K, KeyRotation[i] );
|
||||
Permute( SubK, K, KeyCompression, 6 );
|
||||
|
||||
/* Expand the right half (R) of the data block to 48 bytes,
|
||||
* then XOR the result with the Subkey for this round.
|
||||
*/
|
||||
Permute( Rexp, R, DataExpansion, 6 );
|
||||
xor( Rexp, Rexp, SubK, 6 );
|
||||
|
||||
/* S-Box substitutions, P-Box permutation, and final XOR.
|
||||
* The S-Box substitutions return a 32-bit value, which is then
|
||||
* run through the 32-bit to 32-bit P-Box permutation. The P-Box
|
||||
* result is then XOR'd with the left-hand half of the key.
|
||||
* (Rexp is used as a temporary variable between the P-Box & XOR).
|
||||
*/
|
||||
sbox( Rn, Rexp );
|
||||
Permute( Rexp, Rn, PBox, 4 );
|
||||
xor( Rn, L, Rexp, 4 );
|
||||
|
||||
/* The previous R becomes the new L,
|
||||
* and Rn is moved into R ready for the next round.
|
||||
*/
|
||||
for( j = 0; j < 4; j++ )
|
||||
{
|
||||
L[j] = R[j];
|
||||
R[j] = Rn[j];
|
||||
}
|
||||
}
|
||||
|
||||
/* The encryption is complete.
|
||||
* Now reverse-permute the ciphertext to produce the final result.
|
||||
* We actually combine two steps here. The penultimate step is to
|
||||
* swap the positions of L and R in the result of the 16 rounds,
|
||||
* after which the reverse of the Initial Permutation is applied.
|
||||
* To save a step, the FinalPermuteMap applies both the L/R swap
|
||||
* and the inverse of the Initial Permutation.
|
||||
*/
|
||||
Permute( dst, D, FinalPermuteMap, 8 );
|
||||
return( dst );
|
||||
} /* auth_DEShash */
|
||||
|
||||
|
||||
static const uchar SMB_LMhash_Magic[8] =
|
||||
{ 'K', 'G', 'S', '!', '@', '#', '$', '%' };
|
||||
|
||||
uchar *auth_LMhash( uchar *dst, const uchar *pwd, const int pwdlen )
|
||||
/* ------------------------------------------------------------------------ **
|
||||
* Generate an LM Hash from the input password.
|
||||
*
|
||||
* Input: dst - Pointer to a location to which to write the LM Hash.
|
||||
* Requires 16 bytes minimum.
|
||||
* pwd - Source password. Should be in OEM charset (extended
|
||||
* ASCII) format in all upper-case, but this
|
||||
* implementation doesn't really care. See the notes
|
||||
* below.
|
||||
* pwdlen - Length, in bytes, of the password. Normally, this
|
||||
* will be strlen( pwd ).
|
||||
*
|
||||
* Output: Pointer to the resulting LM hash (same as <dst>).
|
||||
*
|
||||
* Notes: This function does not convert the input password to upper
|
||||
* case. The upper-case conversion should be done before the
|
||||
* password gets this far. DOS codepage handling and such
|
||||
* should be taken into consideration. Rather than attempt to
|
||||
* work out all those details here, the function assumes that
|
||||
* the password is in the correct form before it reaches this
|
||||
* point.
|
||||
*
|
||||
* ------------------------------------------------------------------------ **
|
||||
*/
|
||||
{
|
||||
int i,
|
||||
max14;
|
||||
uint8_t tmp_pwd[14] = { 0,0,0,0,0,0,0,0,0,0,0,0,0,0 };
|
||||
|
||||
/* Copy at most 14 bytes of <pwd> into <tmp_pwd>.
|
||||
* If the password is less than 14 bytes long
|
||||
* the rest will be nul padded.
|
||||
*/
|
||||
max14 = pwdlen > 14 ? 14 : pwdlen;
|
||||
for( i = 0; i < max14; i++ )
|
||||
tmp_pwd[i] = pwd[i];
|
||||
|
||||
/* The password is split into two 7-byte keys, each of which
|
||||
* are used to DES-encrypt the magic string. The results are
|
||||
* concatonated to produce the 16-byte LM Hash.
|
||||
*/
|
||||
(void)auth_DEShash( dst, tmp_pwd, SMB_LMhash_Magic );
|
||||
(void)auth_DEShash( &dst[8], &tmp_pwd[7], SMB_LMhash_Magic );
|
||||
|
||||
/* Return a pointer to the result.
|
||||
*/
|
||||
return( dst );
|
||||
} /* auth_LMhash */
|
||||
@@ -0,0 +1,95 @@
|
||||
#ifndef AUTH_LMHASH_H
|
||||
#define AUTH_LMHASH_H
|
||||
|
||||
typedef unsigned char uchar;
|
||||
|
||||
/* ========================================================================== **
|
||||
*
|
||||
* LMhash.h
|
||||
*
|
||||
* Copyright:
|
||||
* Copyright (C) 2004 by Christopher R. Hertel
|
||||
*
|
||||
* Email: crh@ubiqx.mn.org
|
||||
*
|
||||
* $Id: LMhash.h,v 0.1 2004/05/30 02:26:31 crh Exp $
|
||||
*
|
||||
* -------------------------------------------------------------------------- **
|
||||
*
|
||||
* Description:
|
||||
*
|
||||
* Implemention of the LAN Manager hash (LM hash) and LM response
|
||||
* algorithms.
|
||||
*
|
||||
* -------------------------------------------------------------------------- **
|
||||
*
|
||||
* License:
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*
|
||||
* -------------------------------------------------------------------------- **
|
||||
*
|
||||
* Notes:
|
||||
*
|
||||
* This module implements the LM hash. The NT hash is simply the MD4() of
|
||||
* the password, so we don't need a separate implementation of that. This
|
||||
* module also implements the LM response, which can be combined with the
|
||||
* NT hash to produce the NTLM response.
|
||||
*
|
||||
* This implementation was created based on the description in my own book.
|
||||
* The book description was, in turn, written after studying many existing
|
||||
* examples in various documentation. Jeremy Allison and Andrew Tridgell
|
||||
* deserve lots of credit for having figured out the secrets of Lan Manager
|
||||
* authentication many years ago.
|
||||
*
|
||||
* See:
|
||||
* Implementing CIFS - the Common Internet File System
|
||||
* by your truly. ISBN 0-13-047116-X, Prentice Hall PTR., August 2003
|
||||
* Section 15.3, in particular.
|
||||
* (Online at: http://ubiqx.org/cifs/SMB.html#SMB.8.3)
|
||||
*
|
||||
* ========================================================================== **
|
||||
*/
|
||||
|
||||
/* -------------------------------------------------------------------------- **
|
||||
* Functions:
|
||||
*/
|
||||
|
||||
uchar *auth_LMhash( uchar *dst, const uchar *pwd, const int pwdlen );
|
||||
/* ------------------------------------------------------------------------ **
|
||||
* Generate an LM Hash from the input password.
|
||||
*
|
||||
* Input: dst - Pointer to a location to which to write the LM Hash.
|
||||
* Requires 16 bytes minimum.
|
||||
* pwd - Source password. Should be in OEM charset (extended
|
||||
* ASCII) format in all upper-case, but this
|
||||
* implementation doesn't really care. See the notes
|
||||
* below.
|
||||
* pwdlen - Length, in bytes, of the password. Normally, this
|
||||
* will be strlen( pwd ).
|
||||
*
|
||||
* Output: Pointer to the resulting LM hash (same as <dst>).
|
||||
*
|
||||
* Notes: This function does not convert the input password to upper
|
||||
* case. The upper-case conversion should be done before the
|
||||
* password gets this far. DOS codepage handling and such
|
||||
* should be taken into consideration. Rather than attempt to
|
||||
* work out all those details here, the function assumes that
|
||||
* the password is in the correct form before it reaches this
|
||||
* point.
|
||||
*
|
||||
* ------------------------------------------------------------------------ **
|
||||
*/
|
||||
#endif
|
||||
+1
-3
@@ -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
|
||||
|
||||
+1
-3
@@ -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
|
||||
|
||||
@@ -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];
|
||||
}
|
||||
|
||||
@@ -106,7 +106,7 @@
|
||||
acquire: NO];
|
||||
if ([test isKindOfClass: [NSException class]])
|
||||
{
|
||||
NSLog (@"%@ not found", [card reference]);
|
||||
//NSLog (@"%@ not found", [card reference]);
|
||||
cardCopy = [card copy];
|
||||
[invalid addObject: cardCopy];
|
||||
[cardCopy release];
|
||||
|
||||
@@ -272,7 +272,7 @@ static NSData* _sanitizeContent(NSData *theData)
|
||||
if ([tag caseInsensitiveCompare: found_tag] == NSOrderedSame)
|
||||
{
|
||||
// Remove the leading slash
|
||||
NSLog(@"Found void tag with invalid leading slash: </%@>", found_tag);
|
||||
//NSLog(@"Found void tag with invalid leading slash: </%@>", found_tag);
|
||||
i--;
|
||||
[d replaceBytesInRange: NSMakeRange(i, 1)
|
||||
withBytes: NULL
|
||||
@@ -455,6 +455,10 @@ static NSData* _sanitizeContent(NSData *theData)
|
||||
{
|
||||
if (*currentChar == '{')
|
||||
inCSSDeclaration = YES;
|
||||
if (*currentChar == '}')
|
||||
// CSS syntax error: ending declaration character while not in a CSS declaration.
|
||||
// Ignore eveything from last CSS declaration.
|
||||
start = currentChar + 1;
|
||||
else if (*currentChar == ',')
|
||||
hasEmbeddedCSS = NO;
|
||||
else if (!hasEmbeddedCSS)
|
||||
|
||||
@@ -436,7 +436,7 @@ static NSArray *infoKeys = nil;
|
||||
- (NSDictionary *) storeInfo
|
||||
{
|
||||
[self debugWithFormat:@"storing info ..."];
|
||||
return [self valuesForKeys: infoKeys];
|
||||
return [self dictionaryWithValuesForKeys: infoKeys];
|
||||
}
|
||||
|
||||
/* contacts search */
|
||||
|
||||
@@ -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]];
|
||||
|
||||
@@ -77,7 +77,7 @@ static NSString *mailETag = nil;
|
||||
SOGO_MAJOR_VERSION,
|
||||
SOGO_MINOR_VERSION,
|
||||
SOGO_SUBMINOR_VERSION];
|
||||
NSLog (@"Note: using constant etag for mail viewer: '%@'", mailETag);
|
||||
//NSLog (@"Note: using constant etag for mail viewer: '%@'", mailETag);
|
||||
}
|
||||
|
||||
- (void) dealloc
|
||||
|
||||
@@ -21,6 +21,8 @@
|
||||
|
||||
#import <Foundation/NSBundle.h>
|
||||
|
||||
#import <SOGo/SOGoCache.h>
|
||||
#import <SOGo/NSObject+Utilities.h>
|
||||
#import <SOGo/SOGoFolder.h>
|
||||
|
||||
#import <NGObjWeb/NSException+HTTP.h>
|
||||
@@ -54,13 +56,17 @@
|
||||
|
||||
ex = [dispatcher dispatchRequest: request inResponse: response context: context];
|
||||
|
||||
//[[self class] memoryStatistics];
|
||||
|
||||
if (ex)
|
||||
{
|
||||
return [NSException exceptionWithHTTPStatus: 500];
|
||||
}
|
||||
|
||||
RELEASE(dispatcher);
|
||||
|
||||
|
||||
[[SOGoCache sharedCache] killCache];
|
||||
|
||||
return response;
|
||||
}
|
||||
|
||||
|
||||
@@ -361,7 +361,7 @@
|
||||
|
||||
if (login)
|
||||
{
|
||||
/* We redirect the user to his "homepage" when newLocation could not be
|
||||
/* We redirect the user to their "homepage" when newLocation could not be
|
||||
deduced from the "cas-location" cookie and the current action is not a
|
||||
login callback (ticket != nil). */
|
||||
if (!newLocation || !ticket)
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/* SOGoUserHomePage.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
|
||||
@@ -103,8 +103,8 @@
|
||||
maxBookings = [user numberOfSimultaneousBookings];
|
||||
isResource = [user isResource];
|
||||
|
||||
// Don't fetch freebusy information if the user is of type 'resource' and has unlimited bookings
|
||||
if (!isResource || maxBookings > 0)
|
||||
// Fetch freebusy information if the user is NOT a resource or if multiplebookings isn't unlimited
|
||||
if (!isResource || maxBookings != 0)
|
||||
{
|
||||
for (recordCount = 0; recordCount < recordMax; recordCount++)
|
||||
{
|
||||
@@ -167,10 +167,10 @@
|
||||
endInterval += (delta/60/15);
|
||||
|
||||
// Update bit string representation
|
||||
// If the user is a resource, keep the sum of overlapping events
|
||||
// If the user is a resource with restristed amount of bookings, keep the sum of overlapping events
|
||||
for (count = startInterval; count < endInterval; count++)
|
||||
{
|
||||
*(items + count) = isResource ? *(items + count) + 1 : 1;
|
||||
*(items + count) = (isResource && maxBookings > 0) ? *(items + count) + 1 : 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -189,6 +189,9 @@
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
//
|
||||
//
|
||||
- (NSString *) _freeBusyFromStartDate: (NSCalendarDate *) startDate
|
||||
toEndDate: (NSCalendarDate *) endDate
|
||||
forFreeBusy: (SOGoFreeBusyObject *) fb
|
||||
|
||||
@@ -153,6 +153,9 @@
|
||||
"displayremoteinlineimages_never" = "Nunca";
|
||||
"displayremoteinlineimages_always" = "Sempre";
|
||||
|
||||
"Auto save every" = "Auto salvar cada";
|
||||
"minutes" = "minutos";
|
||||
|
||||
/* Contact */
|
||||
"Personal Address Book" = "Catálogo Pessoal";
|
||||
"Collected Address Book" = "Catálogo Coletado";
|
||||
|
||||
@@ -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 :";
|
||||
|
||||
@@ -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:";
|
||||
|
||||
@@ -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:";
|
||||
|
||||
@@ -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 :";
|
||||
|
||||
@@ -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:";
|
||||
|
||||
@@ -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 :";
|
||||
@@ -153,6 +157,9 @@
|
||||
"displayremoteinlineimages_never" = "Soha";
|
||||
"displayremoteinlineimages_always" = "Mindig";
|
||||
|
||||
"Auto save every" = "Automatikus mentés gyakorisága";
|
||||
"minutes" = "perc";
|
||||
|
||||
/* Contact */
|
||||
"Personal Address Book" = "Személyes címjegyzék";
|
||||
"Collected Address Book" = "Összegyűjtöt címek jegyzéke";
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -1,8 +1,6 @@
|
||||
/* UIxCalView.m - this file is part of SOGo
|
||||
*
|
||||
* Copyright (C) 2006-2009 Inverse inc.
|
||||
*
|
||||
* Author: Wolfgang Sourdeau <wsourdeau@inverse.ca>
|
||||
* 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
|
||||
@@ -584,7 +582,7 @@
|
||||
[uri appendString: @"Groups/_custom_"];
|
||||
[uri appendString: uidsString];
|
||||
[uri appendString: @"/"];
|
||||
NSLog (@"Group URI = '%@'", uri);
|
||||
//NSLog (@"Group URI = '%@'", uri);
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/* UIxComponentEditor.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
|
||||
@@ -594,7 +594,7 @@ iRANGE(2);
|
||||
ASSIGN (reminderUnit, @"MINUTES");
|
||||
break;
|
||||
default:
|
||||
NSLog(@"Cannot process duration unit: '%c'", c);
|
||||
//NSLog(@"Cannot process duration unit: '%c'", c);
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -1428,7 +1428,8 @@ iRANGE(2);
|
||||
|
||||
- (void) setComponentCalendar: (SOGoAppointmentFolder *) _componentCalendar
|
||||
{
|
||||
ASSIGN(componentCalendar, _componentCalendar);
|
||||
if (_componentCalendar)
|
||||
ASSIGN(componentCalendar, _componentCalendar);
|
||||
}
|
||||
|
||||
/* priorities */
|
||||
@@ -1886,7 +1887,9 @@ RANGE(2);
|
||||
[component setAttendees: newAttendees];
|
||||
}
|
||||
else
|
||||
NSLog(@"Error scanning following JSON:\n%@", json);
|
||||
{
|
||||
//NSLog(@"Error scanning following JSON:\n%@", json);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -12,7 +12,7 @@
|
||||
</var:if>
|
||||
|
||||
<var:if condition="isBody">
|
||||
<var:string value="currentUserName" const:escapeHTML="NO"/> has modified your access rights for his <var:string const:value='"' const:escapeHTML="NO"/><var:string value="resourceName" const:escapeHTML="NO"/><var:string const:value='"' const:escapeHTML="NO"/> folder.
|
||||
<var:string value="currentUserName" const:escapeHTML="NO"/> has modified your access rights for their <var:string const:value='"' const:escapeHTML="NO"/><var:string value="resourceName" const:escapeHTML="NO"/><var:string const:value='"' const:escapeHTML="NO"/> folder.
|
||||
<!--
|
||||
You can subscribe directly to that folder by following this link:
|
||||
<var:string value="httpAdvisoryURL" const:escapeHTML="NO"/>subscribe?mail-invitation=YES
|
||||
|
||||
@@ -12,7 +12,7 @@
|
||||
</var:if>
|
||||
|
||||
<var:if condition="isBody">
|
||||
<var:string value="currentUserName" const:escapeHTML="NO"/> has removed you from the access list for his <var:string const:value='"' const:escapeHTML="NO"/><var:string value="resourceName" const:escapeHTML="NO"/><var:string const:value='"' const:escapeHTML="NO"/> folder.
|
||||
<var:string value="currentUserName" const:escapeHTML="NO"/> has removed you from the access list for their <var:string const:value='"' const:escapeHTML="NO"/><var:string value="resourceName" const:escapeHTML="NO"/><var:string const:value='"' const:escapeHTML="NO"/> folder.
|
||||
<!--
|
||||
You can unsubscribe directly to that folder by following this link:
|
||||
<var:string value="httpAdvisoryURL" const:escapeHTML="NO"/>unsubscribe?mail-invitation=YES
|
||||
|
||||
@@ -12,7 +12,7 @@
|
||||
</var:if>
|
||||
|
||||
<var:if condition="isBody">
|
||||
<var:string value="currentUserName" const:escapeHTML="NO"/> has modified your access rights for his <var:string const:value='"' const:escapeHTML="NO"/><var:string value="resourceName" const:escapeHTML="NO"/><var:string const:value='"' const:escapeHTML="NO"/> folder.
|
||||
<var:string value="currentUserName" const:escapeHTML="NO"/> has modified your access rights for their <var:string const:value='"' const:escapeHTML="NO"/><var:string value="resourceName" const:escapeHTML="NO"/><var:string const:value='"' const:escapeHTML="NO"/> folder.
|
||||
<!--
|
||||
You can subscribe directly to that folder by following this link:
|
||||
<var:string value="httpAdvisoryURL" const:escapeHTML="NO"/>subscribe?mail-invitation=YES
|
||||
|
||||
@@ -12,7 +12,7 @@
|
||||
</var:if>
|
||||
|
||||
<var:if condition="isBody">
|
||||
<var:string value="currentUserName" const:escapeHTML="NO"/> has removed you from the access list for his <var:string const:value='"' const:escapeHTML="NO"/><var:string value="resourceName" const:escapeHTML="NO"/><var:string const:value='"' const:escapeHTML="NO"/> folder.
|
||||
<var:string value="currentUserName" const:escapeHTML="NO"/> has removed you from the access list for their <var:string const:value='"' const:escapeHTML="NO"/><var:string value="resourceName" const:escapeHTML="NO"/><var:string const:value='"' const:escapeHTML="NO"/> folder.
|
||||
<!--
|
||||
You can unsubscribe directly to that folder by following this link:
|
||||
<var:string value="httpAdvisoryURL" const:escapeHTML="NO"/>unsubscribe?mail-invitation=YES
|
||||
|
||||
Loaded 100 of 105 files, more files were not shown because too many files have changed in this diff.
Show more
Reference in new issue
Block a user