Merge branch 'master-upstream' into jjgarcia/merge-from-upstream

pull/65/merge^2
Julio García 2015-02-02 12:53:49 +01:00
commit 12b159a6c0
695 changed files with 8277 additions and 3680 deletions

View File

@ -33,6 +33,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#import <Foundation/NSCalendarDate.h> #import <Foundation/NSCalendarDate.h>
#import <Foundation/NSDictionary.h> #import <Foundation/NSDictionary.h>
#import <Foundation/NSString.h> #import <Foundation/NSString.h>
#import <Foundation/NSTimeZone.h>
#import <NGExtensions/NSString+misc.h> #import <NGExtensions/NSString+misc.h>
@ -193,11 +194,12 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
if ((o = [self note])) 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]; o = [o activeSyncRepresentationInContext: context];
[s appendString: @"<Body xmlns=\"AirSyncBase:\">"]; [s appendString: @"<Body xmlns=\"AirSyncBase:\">"];
[s appendFormat: @"<Type>%d</Type>", 1]; [s appendFormat: @"<Type>%d</Type>", 1];
[s appendFormat: @"<EstimatedDataSize>%d</EstimatedDataSize>", [o length]]; [s appendFormat: @"<EstimatedDataSize>%d</EstimatedDataSize>", [o length]];
[s appendFormat: @"<Truncated>%d</Truncated>", 0];
[s appendFormat: @"<Data>%@</Data>", o]; [s appendFormat: @"<Data>%@</Data>", o];
[s appendString: @"</Body>"]; [s appendString: @"</Body>"];
} }
@ -222,14 +224,14 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
[self setNote: o]; [self setNote: o];
// Categories // Categories
if ((o = [theValues objectForKey: @"Categories"])) if ((o = [theValues objectForKey: @"Categories"]) && [o length])
[self setCategories: o]; [self setCategories: o];
// Birthday // Birthday
if ((o = [theValues objectForKey: @"Birthday"])) if ((o = [theValues objectForKey: @"Birthday"]))
{ {
o = [o calendarDate]; o = [o calendarDate];
[self setBday: [o descriptionWithCalendarFormat: @"%Y-%m-%d"]]; [self setBday: [o descriptionWithCalendarFormat: @"%Y-%m-%d" timeZone: nil locale: nil]];
} }
// //

View File

@ -33,6 +33,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#import <Foundation/NSString.h> #import <Foundation/NSString.h>
#import <NGExtensions/NGBase64Coding.h> #import <NGExtensions/NGBase64Coding.h>
#import <NGExtensions/NSObject+Logs.h>
#include <wbxml/wbxml.h> #include <wbxml/wbxml.h>
#include <wbxml/wbxml_conv.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]]; path = [NSString stringWithFormat: @"/tmp/%@.data", [[NSProcessInfo processInfo] globallyUniqueString]];
[self writeToFile: path atomically: YES]; [self writeToFile: path atomically: YES];
NSLog(@"Original data written to: %@", path); [self errorWithFormat: @"Original data written to: %@", path];
} }
// //
@ -81,20 +82,18 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
if (ret != WBXML_OK) 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]; [self _dumpToFile];
return nil; return nil;
} }
data = [[NSData alloc] initWithBytes: xml length: xml_len]; data = [NSData dataWithBytesNoCopy: xml length: xml_len freeWhenDone: YES];
#if WBXMLDEBUG #if WBXMLDEBUG
[data writeToFile: @"/tmp/protocol.decoded" atomically: YES]; [data writeToFile: @"/tmp/protocol.decoded" atomically: YES];
#endif #endif
free(xml); return data;
return AUTORELEASE(data);
} }
@ -116,7 +115,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
if (ret != WBXML_OK) 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]; [self _dumpToFile];
return nil; return nil;
} }
@ -131,22 +130,21 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
if (ret != WBXML_OK) 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]; [self _dumpToFile];
free(wbxml); free(wbxml);
wbxml_conv_xml2wbxml_destroy(conv); wbxml_conv_xml2wbxml_destroy(conv);
return nil; return nil;
} }
data = [[NSData alloc] initWithBytes: wbxml length: wbxml_len]; data = [NSData dataWithBytesNoCopy: wbxml length: wbxml_len freeWhenDone: YES];
#if WBXMLDEBUG #if WBXMLDEBUG
[data writeToFile: @"/tmp/protocol.encoded" atomically: YES]; [data writeToFile: @"/tmp/protocol.encoded" atomically: YES];
#endif #endif
free(wbxml);
wbxml_conv_xml2wbxml_destroy(conv); wbxml_conv_xml2wbxml_destroy(conv);
return AUTORELEASE(data); return data;
} }
@end @end

View File

@ -33,11 +33,17 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#include <Foundation/NSCalendarDate.h> #include <Foundation/NSCalendarDate.h>
#include <Foundation/NSData.h> #include <Foundation/NSData.h>
#include <Foundation/NSDate.h> #include <Foundation/NSDate.h>
#include <Foundation/NSTimeZone.h>
#include <SOGo/NSString+Utilities.h> #include <SOGo/NSString+Utilities.h>
#include <SOGo/NSData+Crypto.h>
#include <NGExtensions/NGBase64Coding.h>
#include <NGExtensions/NSString+misc.h> #include <NGExtensions/NSString+misc.h>
static NSArray *easCommandCodes = nil;
static NSArray *easCommandParameters = nil;
@implementation NSString (ActiveSync) @implementation NSString (ActiveSync)
- (NSString *) sanitizedServerIdWithType: (SOGoMicrosoftActiveSyncFolderType) folderType - (NSString *) sanitizedServerIdWithType: (SOGoMicrosoftActiveSyncFolderType) folderType
@ -61,9 +67,9 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
{ {
NSString *s; NSString *s;
s = [self stringByEscapingHTMLString]; s = [self safeString];
return [s safeString]; return [s stringByEscapingHTMLString];
} }
- (int) activeSyncFolderType - (int) activeSyncFolderType
@ -122,23 +128,98 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
// //
- (NSCalendarDate *) calendarDate - (NSCalendarDate *) calendarDate
{ {
NSString *s;
id o; 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) 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; return o;
} }
- (NSString *) _valueForParameter: (NSString *) theParameter - (NSString *) _valueForParameter: (NSString *) theParameter
{ {
NSArray *components; NSMutableArray *components;
NSString *s; NSString *s;
int i; int i;
components = [[[self componentsSeparatedByString: @"?"] lastObject] componentsSeparatedByString: @"&"]; components = [NSMutableArray arrayWithArray: [[[self componentsSeparatedByString: @"?"] lastObject] componentsSeparatedByString: @"&"]];
// We handle BASE64 encoded queryStrings. See http://msdn.microsoft.com/en-us/library/ee160227%28v=exchg.80%29.aspx for details.
if ([components count] == 1)
{
NSString *deviceType, *parameterValue;
NSData *queryString;
int cmd_code, deviceid_length, policy_length, devicetype_length, parameter_code, parameter_length, i;
const char* qs_bytes;
queryString = [[components objectAtIndex: 0] dataByDecodingBase64];
if (![queryString length])
return nil;
qs_bytes = (const char*)[queryString bytes];
if (!easCommandCodes)
{
easCommandCodes = [NSArray arrayWithObjects:@"Sync", @"SendMail", @"SmartForward", @"SmartReply", @"GetAttachment", @"na", @"na", @"na", @"na",
@"FolderSync", @"FolderCreate", @"FolderDelete", @"FolderUpdate", @"MoveItems", @"GetItemEstimate", @"MeetingResponse",
@"Search", @"Settings", @"Ping", @"ItemOperations", @"Provision", @"ResolveRecipients", @"ValidateCert", nil];
RETAIN(easCommandCodes);
}
if (!easCommandParameters)
{
easCommandParameters = [NSArray arrayWithObjects:@"AttachmentName", @"CollectionId", @"na", @"ItemId", @"LongId", @"na", @"Occurrence", @"Options", @"User", nil];
RETAIN(easCommandParameters);
}
// Command code, 1 byte, ie.: cmd=
cmd_code = qs_bytes[1];
[components addObject: [NSString stringWithFormat: @"cmd=%@", [easCommandCodes objectAtIndex: cmd_code]]];
// Device ID length and Device ID (variable)
deviceid_length = qs_bytes[4];
[components addObject: [NSString stringWithFormat: @"deviceId=%@", [[NSData encodeDataAsHexString: [queryString subdataWithRange: NSMakeRange(5, deviceid_length)]] uppercaseString]]];
// Device type length and type (variable)
policy_length = qs_bytes[5+deviceid_length];
devicetype_length = qs_bytes[5+deviceid_length+1+policy_length];
deviceType = [[NSString alloc] initWithData: [queryString subdataWithRange: NSMakeRange(5+deviceid_length+1+policy_length+1, devicetype_length)]
encoding: NSASCIIStringEncoding];
AUTORELEASE(deviceType);
[components addObject: [NSString stringWithFormat: @"deviceType=%@", deviceType]];
// Command Parameters
i = 5+deviceid_length+1+policy_length+1+devicetype_length;
while (i < [queryString length])
{
parameter_code = qs_bytes[i];
parameter_length = qs_bytes[i+1];
parameterValue = [[NSString alloc] initWithData: [queryString subdataWithRange: NSMakeRange(i+1+1, parameter_length)]
encoding: NSASCIIStringEncoding];
AUTORELEASE(parameterValue);
// parameter_code 7 == Options
// http://msdn.microsoft.com/en-us/library/ee237789(v=exchg.80).aspx
if (parameter_code == 7)
[components addObject: [NSString stringWithFormat: @"%@=%@", [easCommandParameters objectAtIndex: parameter_code],
([parameterValue isEqualToString: @"\001"]) ? @"SaveInSent" : @"AcceptMultiPart"]];
else
[components addObject: [NSString stringWithFormat: @"%@=%@", [easCommandParameters objectAtIndex: parameter_code], parameterValue]];
i = i + 1 + 1 + parameter_length;
}
}
for (i = 0; i < [components count]; i++) for (i = 0; i < [components count]; i++)
{ {

View File

@ -4,7 +4,7 @@ to negotiate the fees associated to your user base.
To contact Microsoft, please visit: 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 and send an email to iplicreq@microsoft.com

View File

@ -29,11 +29,12 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/ */
#import "SOGoActiveSyncDispatcher+Sync.h" #import "SOGoActiveSyncDispatcher+Sync.h"
#import <Foundation/NSArray.h> #import <Foundation/NSArray.h>
#import <Foundation/NSAutoreleasePool.h>
#import <Foundation/NSCalendarDate.h> #import <Foundation/NSCalendarDate.h>
#import <Foundation/NSNull.h> #import <Foundation/NSNull.h>
#import <Foundation/NSProcessInfo.h> #import <Foundation/NSProcessInfo.h>
#import <Foundation/NSSortDescriptor.h>
#import <Foundation/NSTimeZone.h> #import <Foundation/NSTimeZone.h>
#import <Foundation/NSURL.h> #import <Foundation/NSURL.h>
#import <Foundation/NSValue.h> #import <Foundation/NSValue.h>
@ -56,6 +57,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#import <NGCards/NGVCard.h> #import <NGCards/NGVCard.h>
#import <NGExtensions/NSCalendarDate+misc.h> #import <NGExtensions/NSCalendarDate+misc.h>
#import <NGExtensions/NSObject+Logs.h>
#import <NGExtensions/NSString+misc.h> #import <NGExtensions/NSString+misc.h>
#import <NGImap4/NSString+Imap4.h> #import <NGImap4/NSString+Imap4.h>
@ -114,22 +116,26 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
forKey: (NSString *) theFolderKey forKey: (NSString *) theFolderKey
{ {
SOGoCacheGCSObject *o; SOGoCacheGCSObject *o;
NSDictionary *values;
NSString *key; NSString *key;
key = [NSString stringWithFormat: @"%@+%@", [context objectForKey: @"DeviceId"], theFolderKey]; key = [NSString stringWithFormat: @"%@+%@", [context objectForKey: @"DeviceId"], theFolderKey];
values = [theFolderMetadata copy];
o = [SOGoCacheGCSObject objectWithName: key inContainer: nil]; o = [SOGoCacheGCSObject objectWithName: key inContainer: nil];
[o setObjectType: ActiveSyncFolderCacheObject]; [o setObjectType: ActiveSyncFolderCacheObject];
[o setTableUrl: [self folderTableURL]]; [o setTableUrl: [self folderTableURL]];
[o reloadIfNeeded]; //[o reloadIfNeeded];
[[o properties] removeObjectForKey: @"SyncKey"]; [[o properties] removeObjectForKey: @"SyncKey"];
[[o properties] removeObjectForKey: @"SyncCache"]; [[o properties] removeObjectForKey: @"SyncCache"];
[[o properties] removeObjectForKey: @"DateCache"]; [[o properties] removeObjectForKey: @"DateCache"];
[[o properties] removeObjectForKey: @"MoreAvailable"]; [[o properties] removeObjectForKey: @"MoreAvailable"];
[[o properties] removeObjectForKey: @"SuccessfulMoveItemsOps"];
[[o properties] addEntriesFromDictionary: theFolderMetadata]; [[o properties] addEntriesFromDictionary: values];
[o save]; [o save];
[values release];
} }
- (NSMutableDictionary *) _folderMetadataForKey: (NSString *) theFolderKey - (NSMutableDictionary *) _folderMetadataForKey: (NSString *) theFolderKey
@ -147,6 +153,29 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
return [o properties]; return [o properties];
} }
- (NSString *) _getNameInCache: (id) theCollection withType: (SOGoMicrosoftActiveSyncFolderType) theFolderType
{
NSString *nameInCache;
if (theFolderType == ActiveSyncMailFolder)
nameInCache= [[[theCollection mailAccountFolder] imapFolderGUIDs] objectForKey: [theCollection nameInContainer]];
else
{
NSString *component_name;
if (theFolderType == ActiveSyncContactFolder)
component_name = @"vcard";
else if (theFolderType == ActiveSyncEventFolder)
component_name = @"vevent";
else
component_name = @"vtodo";
nameInCache= [NSString stringWithFormat: @"%@/%@", component_name, [theCollection nameInContainer]];
}
return nameInCache;
}
// //
// <?xml version="1.0"?> // <?xml version="1.0"?>
@ -190,7 +219,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
withType: (SOGoMicrosoftActiveSyncFolderType) theFolderType withType: (SOGoMicrosoftActiveSyncFolderType) theFolderType
inBuffer: (NSMutableString *) theBuffer inBuffer: (NSMutableString *) theBuffer
{ {
NSMutableDictionary *allValues; NSMutableDictionary *folderMetadata, *dateCache, *syncCache, *allValues;
NSString *clientId, *serverId; NSString *clientId, *serverId;
NSArray *additions; NSArray *additions;
@ -259,9 +288,8 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
case ActiveSyncMailFolder: case ActiveSyncMailFolder:
default: default:
{ {
// FIXME // FIXME - what to do?
//continue; [self errorWithFormat: @"Fatal error occured - tried to call -processSyncAddCommand: ... on a mail folder. We abort."];
NSLog(@"BLARG!");
abort(); abort();
} }
} }
@ -276,6 +304,17 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
[theBuffer appendFormat: @"<ServerId>%@</ServerId>", serverId]; [theBuffer appendFormat: @"<ServerId>%@</ServerId>", serverId];
[theBuffer appendFormat: @"<Status>%d</Status>", 1]; [theBuffer appendFormat: @"<Status>%d</Status>", 1];
[theBuffer appendString: @"</Add>"]; [theBuffer appendString: @"</Add>"];
// Update syncCache
folderMetadata = [self _folderMetadataForKey: [self _getNameInCache: theCollection withType: theFolderType]];
syncCache = [folderMetadata objectForKey: @"SyncCache"];
dateCache = [folderMetadata objectForKey: @"DateCache"];
[syncCache setObject: [folderMetadata objectForKey: @"SyncKey"] forKey: serverId];
[dateCache setObject: [NSCalendarDate date] forKey: serverId];
[self _setFolderMetadata: folderMetadata forKey: [self _getNameInCache: theCollection withType: theFolderType]];
} }
} }
} }
@ -375,6 +414,10 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
} }
} }
[theBuffer appendString: @"<Change>"];
[theBuffer appendFormat: @"<ServerId>%@</ServerId>", serverId];
[theBuffer appendFormat: @"<Status>%d</Status>", 1];
[theBuffer appendString: @"</Change>"];
} }
} }
} }
@ -431,6 +474,23 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
if (![sogoObject isKindOfClass: [NSException class]]) if (![sogoObject isKindOfClass: [NSException class]])
[sogoObject delete]; [sogoObject delete];
[theBuffer appendString: @"<Delete>"];
[theBuffer appendFormat: @"<ServerId>%@</ServerId>", serverId];
[theBuffer appendFormat: @"<Status>%d</Status>", 1];
[theBuffer appendString: @"</Delete>"];
// update syncCache
NSMutableDictionary *folderMetadata, *dateCache, *syncCache;
folderMetadata = [self _folderMetadataForKey: [self _getNameInCache: theCollection withType: theFolderType]];
syncCache = [folderMetadata objectForKey: @"SyncCache"];
dateCache = [folderMetadata objectForKey: @"DateCache"];
[syncCache removeObjectForKey: serverId];
[dateCache removeObjectForKey: serverId];
[self _setFolderMetadata: folderMetadata forKey: [self _getNameInCache: theCollection withType: theFolderType]];
} }
} }
} }
@ -471,6 +531,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- (void) processSyncGetChanges: (id <DOMElement>) theDocumentElement - (void) processSyncGetChanges: (id <DOMElement>) theDocumentElement
inCollection: (id) theCollection inCollection: (id) theCollection
withWindowSize: (unsigned int) theWindowSize withWindowSize: (unsigned int) theWindowSize
withMaxSyncResponseSize: (unsigned int) theMaxSyncResponseSize
withSyncKey: (NSString *) theSyncKey withSyncKey: (NSString *) theSyncKey
withFolderType: (SOGoMicrosoftActiveSyncFolderType) theFolderType withFolderType: (SOGoMicrosoftActiveSyncFolderType) theFolderType
withFilterType: (NSCalendarDate *) theFilterType withFilterType: (NSCalendarDate *) theFilterType
@ -479,6 +540,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
{ {
NSMutableDictionary *folderMetadata, *dateCache, *syncCache; NSMutableDictionary *folderMetadata, *dateCache, *syncCache;
NSAutoreleasePool *pool;
NSMutableString *s; NSMutableString *s;
BOOL more_available; BOOL more_available;
@ -488,18 +550,30 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
more_available = NO; more_available = NO;
if (theFolderType == ActiveSyncMailFolder && !([theSyncKey isEqualToString: @"-1"]) && theFilterType) folderMetadata = [self _folderMetadataForKey: [self _getNameInCache: theCollection withType: theFolderType]];
// If this is a new sync operation, DateCache and SyncCache needs to be deleted
if ([theSyncKey isEqualToString: @"-1"])
{
[folderMetadata setObject: [NSMutableDictionary dictionary] forKey: @"SyncCache"];
[folderMetadata setObject: [NSMutableDictionary dictionary] forKey: @"DateCache"];
}
syncCache = [folderMetadata objectForKey: @"SyncCache"];
dateCache = [folderMetadata objectForKey: @"DateCache"];
if ((theFolderType == ActiveSyncMailFolder || theFolderType == ActiveSyncEventFolder || theFolderType == ActiveSyncTaskFolder) &&
!([folderMetadata objectForKey: @"MoreAvailable"]) && // previous sync operation reached the windowSize or maximumSyncReponseSize
!([theSyncKey isEqualToString: @"-1"]) && // new sync operation
theFilterType)
{ {
NSArray *allKeys; NSArray *allKeys;
NSString *key; NSString *key;
int softdelete_count; int softdelete_count;
softdelete_count = 0; softdelete_count = 0;
folderMetadata = [self _folderMetadataForKey: [theCollection nameInContainer]];
dateCache = [folderMetadata objectForKey: @"DateCache"];
syncCache = [folderMetadata objectForKey: @"SyncCache"];
allKeys = [dateCache allKeys]; allKeys = [dateCache allKeys];
for (i = 0; i < [allKeys count]; i++) for (i = 0; i < [allKeys count]; i++)
{ {
@ -510,17 +584,17 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
[s appendString: @"<SoftDelete xmlns=\"AirSync:\">"]; [s appendString: @"<SoftDelete xmlns=\"AirSync:\">"];
[s appendFormat: @"<ServerId xmlns=\"AirSync:\">%@</ServerId>", key]; [s appendFormat: @"<ServerId xmlns=\"AirSync:\">%@</ServerId>", key];
[s appendString: @"</SoftDelete>"]; [s appendString: @"</SoftDelete>"];
[syncCache removeObjectForKey: key]; [syncCache removeObjectForKey: key];
[dateCache removeObjectForKey: key]; [dateCache removeObjectForKey: key];
softdelete_count++; softdelete_count++;
} }
if (softdelete_count >= theWindowSize) if (softdelete_count >= theWindowSize || (theMaxSyncResponseSize > 0 && [s length] >= theMaxSyncResponseSize))
{ {
[folderMetadata setObject: [NSNumber numberWithBool: YES] forKey: @"MoreAvailable"]; [folderMetadata setObject: [NSNumber numberWithBool: YES] forKey: @"MoreAvailable"];
[self _setFolderMetadata: folderMetadata forKey: [theCollection nameInContainer]]; [self _setFolderMetadata: folderMetadata forKey: [self _getNameInCache: theCollection withType: theFolderType]];
more_available = YES; more_available = YES;
*theLastServerKey = theSyncKey; *theLastServerKey = theSyncKey;
@ -532,7 +606,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
} }
[folderMetadata removeObjectForKey: @"MoreAvailable"]; [folderMetadata removeObjectForKey: @"MoreAvailable"];
[self _setFolderMetadata: folderMetadata forKey: [theCollection nameInContainer]]; [self _setFolderMetadata: folderMetadata forKey: [self _getNameInCache: theCollection withType: theFolderType]];
} }
// //
@ -557,7 +631,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
NSArray *allComponents; NSArray *allComponents;
BOOL updated; BOOL updated;
int deleted; int deleted, return_count;
if (theFolderType == ActiveSyncContactFolder) if (theFolderType == ActiveSyncContactFolder)
component_name = @"vcard"; component_name = @"vcard";
@ -567,39 +641,67 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
component_name = @"vtodo"; component_name = @"vtodo";
allComponents = [theCollection syncTokenFieldsWithProperties: nil matchingSyncToken: theSyncKey fromDate: theFilterType]; allComponents = [theCollection syncTokenFieldsWithProperties: nil matchingSyncToken: theSyncKey fromDate: theFilterType];
allComponents = [allComponents sortedArrayUsingDescriptors: [NSArray arrayWithObjects: [[NSSortDescriptor alloc] initWithKey: @"c_lastmodified" ascending:YES], nil]];
// Check for the WindowSize // Check for the WindowSize
max = [allComponents count]; max = [allComponents count];
// Disabled for now for GCS folders. return_count = 0;
// if (max > theWindowSize)
// {
// max = theWindowSize;
// more_available = YES;
// }
for (i = 0; i < max; i++) for (i = 0; i < max; i++)
{ {
pool = [[NSAutoreleasePool alloc] init];
// Check for the WindowSize and slice accordingly
if (return_count >= theWindowSize || (theMaxSyncResponseSize > 0 && [s length] >= theMaxSyncResponseSize))
{
more_available = YES;
// -1 to make sure that we miss no event in case there are more with the same c_lastmodified
*theLastServerKey = [[NSString alloc] initWithFormat: @"%d", [[component objectForKey: @"c_lastmodified"] intValue] - 1];
DESTROY(pool);
break;
}
component = [allComponents objectAtIndex: i]; component = [allComponents objectAtIndex: i];
deleted = [[component objectForKey: @"c_deleted"] intValue]; deleted = [[component objectForKey: @"c_deleted"] intValue];
if (!deleted && ![[component objectForKey: @"c_component"] isEqualToString: component_name]) if (!deleted && ![[component objectForKey: @"c_component"] isEqualToString: component_name])
continue; {
DESTROY(pool);
continue;
}
uid = [[component objectForKey: @"c_name"] sanitizedServerIdWithType: theFolderType]; uid = [[component objectForKey: @"c_name"] sanitizedServerIdWithType: theFolderType];
if (deleted) if (deleted)
{ {
[s appendString: @"<Delete xmlns=\"AirSync:\">"]; if ([syncCache objectForKey: uid])
[s appendFormat: @"<ServerId xmlns=\"AirSync:\">%@</ServerId>", uid]; {
[s appendString: @"</Delete>"]; [s appendString: @"<Delete xmlns=\"AirSync:\">"];
[s appendFormat: @"<ServerId xmlns=\"AirSync:\">%@</ServerId>", uid];
[s appendString: @"</Delete>"];
[syncCache removeObjectForKey: uid];
[dateCache removeObjectForKey: uid];
return_count++;
}
} }
else else
{ {
updated = YES; updated = YES;
if ([[component objectForKey: @"c_creationdate"] intValue] > [theSyncKey intValue]) if (![syncCache objectForKey: uid])
updated = NO; updated = NO;
else if ([[component objectForKey: @"c_lastmodified"] intValue] == [[syncCache objectForKey: uid] intValue])
{
DESTROY(pool);
continue;
}
return_count++;
sogoObject = [theCollection lookupName: [uid sanitizedServerIdWithType: theFolderType] sogoObject = [theCollection lookupName: [uid sanitizedServerIdWithType: theFolderType]
inContext: context inContext: context
@ -610,7 +712,6 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
else else
componentObject = [sogoObject component: NO secure: NO]; componentObject = [sogoObject component: NO secure: NO];
// //
// We do NOT synchronize NEW events that are in fact, invitations // We do NOT synchronize NEW events that are in fact, invitations
// to events. This is due to the fact that Outlook 2013 creates // to events. This is due to the fact that Outlook 2013 creates
@ -632,9 +733,29 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
partstat = [attendee participationStatus]; partstat = [attendee participationStatus];
if (partstat == iCalPersonPartStatNeedsAction) 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) if (updated)
[s appendString: @"<Change xmlns=\"AirSync:\">"]; [s appendString: @"<Change xmlns=\"AirSync:\">"];
@ -652,13 +773,28 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
[s appendString: @"</Change>"]; [s appendString: @"</Change>"];
else else
[s appendString: @"</Add>"]; [s appendString: @"</Add>"];
}
} // for ...
folderMetadata = [NSDictionary dictionaryWithObject: [theCollection davCollectionTag] return_count++;
forKey: @"SyncKey"]; }
DESTROY(pool);
} // for (i = 0; i < max; i++) ...
if (more_available)
{
[folderMetadata setObject: [NSNumber numberWithBool: YES] forKey: @"MoreAvailable"];
[folderMetadata setObject: *theLastServerKey forKey: @"SyncKey"];
}
else
{
[folderMetadata removeObjectForKey: @"MoreAvailable"];
[folderMetadata setObject: [theCollection davCollectionTag] forKey: @"SyncKey"];
}
[self _setFolderMetadata: folderMetadata [self _setFolderMetadata: folderMetadata
forKey: [NSString stringWithFormat: @"%@/%@", component_name, [theCollection nameInContainer]]]; forKey: [NSString stringWithFormat: @"%@/%@", component_name, [theCollection nameInContainer]]];
RELEASE(*theLastServerKey);
} }
break; break;
case ActiveSyncMailFolder: case ActiveSyncMailFolder:
@ -673,7 +809,6 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
int j, k, return_count; int j, k, return_count;
BOOL found_in_cache; BOOL found_in_cache;
allMessages = [theCollection syncTokenFieldsWithProperties: nil matchingSyncToken: theSyncKey fromDate: theFilterType]; allMessages = [theCollection syncTokenFieldsWithProperties: nil matchingSyncToken: theSyncKey fromDate: theFilterType];
max = [allMessages count]; max = [allMessages count];
@ -685,27 +820,6 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
sequence: [[[allMessages objectAtIndex: i] allValues] lastObject]]]; sequence: [[[allMessages objectAtIndex: i] allValues] lastObject]]];
} }
// If it's a new Sync operation, DateCache and SyncCache need to be deleted
// but GUID stored by folderSync shouldn't be touched
folderMetadata = [self _folderMetadataForKey: [theCollection nameInContainer]];
if ([theSyncKey isEqualToString: @"-1"])
{
[folderMetadata setObject: [NSMutableDictionary dictionary] forKey: @"SyncCache"];
[folderMetadata setObject: [NSMutableDictionary dictionary] forKey: @"DateCache"];
}
// Check whether GUID in cache is equal to the GUID from imap - this is to avoid cache corruptions if a folder has been renamed and a new folder
// with the same name has been created but folderSync has not yet updated the cache
if (!([[theCollection nameInContainer] isEqualToString:
[NSString stringWithFormat: @"folder%@", [self globallyUniqueIDToIMAPFolderName: [folderMetadata objectForKey: @"GUID"] type: theFolderType]]]))
{
NSLog(@"GUID mismatch don't sync now!");
return;
}
syncCache = [folderMetadata objectForKey: @"SyncCache"];
dateCache = [folderMetadata objectForKey: @"DateCache"];
sortedBySequence = [[NSMutableArray alloc] initWithDictionary: syncCache]; sortedBySequence = [[NSMutableArray alloc] initWithDictionary: syncCache];
[sortedBySequence sortUsingSelector: @selector(compareSequence:)]; [sortedBySequence sortUsingSelector: @selector(compareSequence:)];
[sortedBySequence autorelease]; [sortedBySequence autorelease];
@ -732,7 +846,6 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
else else
found_in_cache = NO; found_in_cache = NO;
if (found_in_cache) if (found_in_cache)
k = j+1; k = j+1;
else else
@ -747,15 +860,18 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
for (; k < [allCacheObjects count]; k++) for (; k < [allCacheObjects count]; k++)
{ {
pool = [[NSAutoreleasePool alloc] init];
// Check for the WindowSize and slice accordingly // Check for the WindowSize and slice accordingly
if (return_count >= theWindowSize) if (return_count >= theWindowSize || (theMaxSyncResponseSize > 0 && [s length] >= theMaxSyncResponseSize))
{ {
NSString *lastSequence; NSString *lastSequence;
more_available = YES; more_available = YES;
lastSequence = ([[aCacheObject sequence] isEqual: [NSNull null]] ? @"1" : [aCacheObject sequence]); lastSequence = ([[aCacheObject sequence] isEqual: [NSNull null]] ? @"1" : [aCacheObject sequence]);
*theLastServerKey = [NSString stringWithFormat: @"%@-%@", [aCacheObject uid], lastSequence]; *theLastServerKey = [[NSString alloc] initWithFormat: @"%@-%@", [aCacheObject uid], lastSequence];
//NSLog(@"Reached windowSize - lastUID will be: %@", *theLastServerKey); //NSLog(@"Reached windowSize - lastUID will be: %@", *theLastServerKey);
DESTROY(pool);
break; break;
} }
@ -836,7 +952,8 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
} }
} }
} DESTROY(pool);
} // for (; k < ...)
if (more_available) if (more_available)
{ {
@ -849,8 +966,9 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
[folderMetadata setObject: [theCollection davCollectionTag] forKey: @"SyncKey"]; [folderMetadata setObject: [theCollection davCollectionTag] forKey: @"SyncKey"];
} }
[self _setFolderMetadata: folderMetadata [self _setFolderMetadata: folderMetadata forKey: [self _getNameInCache: theCollection withType: theFolderType]];
forKey: [theCollection nameInContainer]]; RELEASE(*theLastServerKey);
} // default: } // default:
break; break;
} // switch (folderType) ... } // switch (folderType) ...
@ -862,9 +980,6 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
[theBuffer appendString: @"<Commands>"]; [theBuffer appendString: @"<Commands>"];
[theBuffer appendString: s]; [theBuffer appendString: s];
[theBuffer appendString: @"</Commands>"]; [theBuffer appendString: @"</Commands>"];
if (more_available)
[theBuffer appendString: @"<MoreAvailable/>"];
} }
} }
@ -904,7 +1019,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
if ([[element tagName] isEqualToString: @"Add"]) if ([[element tagName] isEqualToString: @"Add"])
{ {
// Add // Add
[self processSyncAddCommand: aCommand [self processSyncAddCommand: element
inCollection: theCollection inCollection: theCollection
withType: theFolderType withType: theFolderType
inBuffer: theBuffer]; inBuffer: theBuffer];
@ -913,7 +1028,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
else if ([[element tagName] isEqualToString: @"Change"]) else if ([[element tagName] isEqualToString: @"Change"])
{ {
// Change // Change
[self processSyncChangeCommand: aCommand [self processSyncChangeCommand: element
inCollection: theCollection inCollection: theCollection
withType: theFolderType withType: theFolderType
inBuffer: theBuffer]; inBuffer: theBuffer];
@ -922,15 +1037,16 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
else if ([[element tagName] isEqualToString: @"Delete"]) else if ([[element tagName] isEqualToString: @"Delete"])
{ {
// Delete // Delete
[self processSyncDeleteCommand: aCommand [self processSyncDeleteCommand: element
inCollection: theCollection inCollection: theCollection
withType: theFolderType withType: theFolderType
inBuffer: theBuffer]; inBuffer: theBuffer];
*processed = YES;
} }
else if ([[element tagName] isEqualToString: @"Fetch"]) else if ([[element tagName] isEqualToString: @"Fetch"])
{ {
// Fetch // Fetch
[self processSyncFetchCommand: aCommand [self processSyncFetchCommand: element
inCollection: theCollection inCollection: theCollection
withType: theFolderType withType: theFolderType
inBuffer: theBuffer]; inBuffer: theBuffer];
@ -947,14 +1063,16 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- (void) processSyncCollection: (id <DOMElement>) theDocumentElement - (void) processSyncCollection: (id <DOMElement>) theDocumentElement
inBuffer: (NSMutableString *) theBuffer inBuffer: (NSMutableString *) theBuffer
changeDetected: (BOOL *) changeDetected changeDetected: (BOOL *) changeDetected
maxSyncResponseSize: (int) theMaxSyncResponseSize
{ {
NSString *collectionId, *realCollectionId, *syncKey, *davCollectionTag, *bodyPreferenceType, *lastServerKey; NSString *collectionId, *realCollectionId, *syncKey, *davCollectionTag, *bodyPreferenceType, *lastServerKey, *syncKeyInCache;
SOGoMicrosoftActiveSyncFolderType folderType; SOGoMicrosoftActiveSyncFolderType folderType;
id collection, value; id collection, value;
NSMutableString *changeBuffer, *commandsBuffer; NSMutableString *changeBuffer, *commandsBuffer;
BOOL getChanges, first_sync; BOOL getChanges, first_sync;
unsigned int windowSize, v; unsigned int windowSize, v, status;
NSMutableDictionary *folderMetadata;
changeBuffer = [NSMutableString string]; changeBuffer = [NSMutableString string];
commandsBuffer = [NSMutableString string]; commandsBuffer = [NSMutableString string];
@ -965,6 +1083,19 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
collection = [self collectionFromId: realCollectionId type: folderType]; collection = [self collectionFromId: realCollectionId type: folderType];
syncKey = davCollectionTag = [[(id)[theDocumentElement getElementsByTagName: @"SyncKey"] lastObject] textValue]; syncKey = davCollectionTag = [[(id)[theDocumentElement getElementsByTagName: @"SyncKey"] lastObject] textValue];
if (collection == nil)
{
// Collection not found - next folderSync will do the cleanup
//NSLog(@"Sync Collection not found %@ %@", collectionId, realCollectionId);
//Outlook doesn't like following response
//[theBuffer appendString: @"<Collection>"];
//[theBuffer appendFormat: @"<SyncKey>%@</SyncKey>", syncKey];
//[theBuffer appendFormat: @"<CollectionId>%@</CollectionId>", collectionId];
//[theBuffer appendFormat: @"<Status>%d</Status>", 8];
//[theBuffer appendString: @"</Collection>"];
return;
}
// We check for a window size, default to 100 if not specfied or out of bounds // We check for a window size, default to 100 if not specfied or out of bounds
windowSize = [[[(id)[theDocumentElement getElementsByTagName: @"WindowSize"] lastObject] textValue] intValue]; windowSize = [[[(id)[theDocumentElement getElementsByTagName: @"WindowSize"] lastObject] textValue] intValue];
@ -978,6 +1109,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
windowSize = v; windowSize = v;
lastServerKey = nil; lastServerKey = nil;
status = 1;
// From the documention, if GetChanges is missing, we must assume it's a YES. // From the documention, if GetChanges is missing, we must assume it's a YES.
// See http://msdn.microsoft.com/en-us/library/gg675447(v=exchg.80).aspx for all details. // See http://msdn.microsoft.com/en-us/library/gg675447(v=exchg.80).aspx for all details.
@ -995,6 +1127,18 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
first_sync = YES; first_sync = YES;
*changeDetected = YES; *changeDetected = YES;
} }
else if ((![syncKey isEqualToString: @"-1"]) && !([[self _folderMetadataForKey: [self _getNameInCache: collection withType: folderType]] objectForKey: @"SyncCache"]))
{
//NSLog(@"Reset folder: %@", [collection nameInContainer]);
davCollectionTag = @"0";
first_sync = YES;
*changeDetected = YES;
if (!([[self _folderMetadataForKey: [self _getNameInCache: collection withType: folderType]] objectForKey: @"displayName"]))
status = 12; // need folderSync
else
status = 3; // do a complete resync
}
// We check our sync preferences and we stash them // We check our sync preferences and we stash them
bodyPreferenceType = [[(id)[[(id)[theDocumentElement getElementsByTagName: @"BodyPreference"] lastObject] getElementsByTagName: @"Type"] lastObject] textValue]; bodyPreferenceType = [[(id)[[(id)[theDocumentElement getElementsByTagName: @"BodyPreference"] lastObject] getElementsByTagName: @"Type"] lastObject] textValue];
@ -1004,7 +1148,6 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
[context setObject: bodyPreferenceType forKey: @"BodyPreferenceType"]; [context setObject: bodyPreferenceType forKey: @"BodyPreferenceType"];
// We generate the commands, if any, for the response. We might also have // We generate the commands, if any, for the response. We might also have
// generated some in processSyncCommand:inResponse: as we could have // generated some in processSyncCommand:inResponse: as we could have
// received a Fetch command // received a Fetch command
@ -1013,6 +1156,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
[self processSyncGetChanges: theDocumentElement [self processSyncGetChanges: theDocumentElement
inCollection: collection inCollection: collection
withWindowSize: windowSize withWindowSize: windowSize
withMaxSyncResponseSize: theMaxSyncResponseSize
withSyncKey: syncKey withSyncKey: syncKey
withFolderType: folderType withFolderType: folderType
withFilterType: [NSCalendarDate dateFromFilterType: [[(id)[theDocumentElement getElementsByTagName: @"FilterType"] lastObject] textValue]] withFilterType: [NSCalendarDate dateFromFilterType: [[(id)[theDocumentElement getElementsByTagName: @"FilterType"] lastObject] textValue]]
@ -1037,27 +1181,41 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
inBuffer: s inBuffer: s
processed: &processed]; processed: &processed];
if (processed) // Windows phons don't empty Responses tags - such as: <Responses></Responses>.
// We onnly generate this tag when the command has generated a response.
if (processed && [s length])
[commandsBuffer appendFormat: @"<Responses>%@</Responses>", s]; [commandsBuffer appendFormat: @"<Responses>%@</Responses>", s];
else
[commandsBuffer appendString: s];
} }
folderMetadata = [self _folderMetadataForKey: [self _getNameInCache: collection withType: folderType]];
// If we got any changes or if we have applied any commands // If we got any changes or if we have applied any commands
// let's regenerate our SyncKey based on the collection tag. // let's regenerate our SyncKey based on the collection tag.
if ([changeBuffer length] || [commandsBuffer length]) if ([changeBuffer length] || [commandsBuffer length])
{ {
if (lastServerKey) if (lastServerKey)
davCollectionTag = lastServerKey; davCollectionTag = lastServerKey;
else if (![[self _folderMetadataForKey: [collection nameInContainer]] objectForKey: @"MoreAvailable"]) else
davCollectionTag = [collection davCollectionTag]; {
// 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 = [folderMetadata objectForKey: @"SyncKey"];
if (!davCollectionTag)
davCollectionTag = [collection davCollectionTag];
}
*changeDetected = YES; *changeDetected = YES;
} }
else else
{ {
if (folderType == ActiveSyncMailFolder && [syncKey isEqualToString: @"-1"]) // Make sure that client is updated with the right syncKey. - This keeps vtodo's and vevent's syncKey in sync.
davCollectionTag = [collection davCollectionTag]; syncKeyInCache = [folderMetadata objectForKey: @"SyncKey"];
if (syncKeyInCache && !([davCollectionTag isEqualToString:syncKeyInCache]))
{
davCollectionTag = syncKeyInCache;
*changeDetected = YES;
}
} }
// Generate the response buffer // Generate the response buffer
@ -1074,10 +1232,15 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
[theBuffer appendFormat: @"<SyncKey>%@</SyncKey>", davCollectionTag]; [theBuffer appendFormat: @"<SyncKey>%@</SyncKey>", davCollectionTag];
[theBuffer appendFormat: @"<CollectionId>%@</CollectionId>", collectionId]; [theBuffer appendFormat: @"<CollectionId>%@</CollectionId>", collectionId];
[theBuffer appendFormat: @"<Status>%d</Status>", 1]; [theBuffer appendFormat: @"<Status>%d</Status>", status];
// 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 ([folderMetadata objectForKey: @"MoreAvailable"])
[theBuffer appendString: @"<MoreAvailable/>"];
[theBuffer appendString: changeBuffer];
[theBuffer appendString: commandsBuffer]; [theBuffer appendString: commandsBuffer];
[theBuffer appendString: changeBuffer];
[theBuffer appendString: @"</Collection>"]; [theBuffer appendString: @"</Collection>"];
} }
@ -1186,21 +1349,42 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
NSArray *allCollections; NSArray *allCollections;
NSData *d; NSData *d;
int i, j, defaultInterval, heartbeatInterval, internalInterval; int i, j, defaultInterval, heartbeatInterval, internalInterval, maxSyncResponseSize;
BOOL changeDetected; BOOL changeDetected;
changeDetected = NO;
maxSyncResponseSize = [[SOGoSystemDefaults sharedSystemDefaults] maximumSyncResponseSize];
// We initialize our output buffer // We initialize our output buffer
output = [NSMutableString string]; output = [[NSMutableString alloc] init];
[output appendString: @"<?xml version=\"1.0\" encoding=\"utf-8\"?>"]; [output appendString: @"<?xml version=\"1.0\" encoding=\"utf-8\"?>"];
[output appendString: @"<!DOCTYPE ActiveSync PUBLIC \"-//MICROSOFT//DTD ActiveSync//EN\" \"http://www.microsoft.com/\">"]; [output appendString: @"<!DOCTYPE ActiveSync PUBLIC \"-//MICROSOFT//DTD ActiveSync//EN\" \"http://www.microsoft.com/\">"];
[output appendString: @"<Sync xmlns=\"AirSync:\">"]; [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]; defaults = [SOGoSystemDefaults sharedSystemDefaults];
heartbeatInterval = [[[(id)[theDocumentElement getElementsByTagName: @"HeartbeatInterval"] lastObject] textValue] intValue]; heartbeatInterval = [[[(id)[theDocumentElement getElementsByTagName: @"HeartbeatInterval"] lastObject] textValue] intValue];
defaultInterval = [defaults maximumSyncInterval]; defaultInterval = [defaults maximumSyncInterval];
internalInterval = [defaults internalSyncInterval]; 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. // We check to see if our heartbeat interval falls into the supported ranges.
if (heartbeatInterval > defaultInterval || heartbeatInterval < 1) if (heartbeatInterval > defaultInterval || heartbeatInterval < 1)
{ {
@ -1217,7 +1401,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
allCollections = (id)[theDocumentElement getElementsByTagName: @"Collection"]; allCollections = (id)[theDocumentElement getElementsByTagName: @"Collection"];
// We enter our loop detection change // We enter our loop detection change
for (i = 0; i < (defaultInterval/internalInterval); i++) for (i = 0; i < (heartbeatInterval/internalInterval); i++)
{ {
s = [NSMutableString string]; s = [NSMutableString string];
@ -1225,31 +1409,46 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
{ {
aCollection = [allCollections objectAtIndex: j]; aCollection = [allCollections objectAtIndex: j];
[self processSyncCollection: aCollection inBuffer: s changeDetected: &changeDetected]; [self processSyncCollection: aCollection
inBuffer: s
changeDetected: &changeDetected
maxSyncResponseSize: maxSyncResponseSize];
} }
if (changeDetected) if (changeDetected)
{ {
NSLog(@"Change detected, we push the content."); [self logWithFormat: @"Change detected, we push the content."];
break; break;
} }
else if (heartbeatInterval > 1)
{
[self logWithFormat: @"Sleeping %d seconds while detecting changes...", internalInterval];
sleep(internalInterval);
}
else else
{ {
NSLog(@"Sleeping %d seconds while detecting changes...", internalInterval); break;
sleep(internalInterval);
} }
} }
// We always return the last generated response. // Only send a response if there are changes otherwise send an empty response.
// If we only return <Sync><Collections/></Sync>, if (changeDetected)
// iOS powered devices will simply crash. {
[output appendString: s]; // 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] xml2wbxml];
[theResponse setContent: d]; d = [output dataUsingEncoding: NSUTF8StringEncoding];
d = [d xml2wbxml];
[theResponse setContent: d];
}
// Avoid overloading the autorelease pool here, as Sync command can
// generate fairly large responses.
RELEASE(output);
} }
@end @end

File diff suppressed because it is too large Load Diff

View File

@ -30,7 +30,6 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#include "SOGoMailObject+ActiveSync.h" #include "SOGoMailObject+ActiveSync.h"
#import <Foundation/NSArray.h> #import <Foundation/NSArray.h>
#import <Foundation/NSAutoreleasePool.h>
#import <Foundation/NSCalendarDate.h> #import <Foundation/NSCalendarDate.h>
#import <Foundation/NSDictionary.h> #import <Foundation/NSDictionary.h>
#import <Foundation/NSException.h> #import <Foundation/NSException.h>
@ -67,8 +66,8 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#include <Appointments/iCalPerson+SOGo.h> #include <Appointments/iCalPerson+SOGo.h>
#include <Mailer/NSString+Mail.h> #include <Mailer/NSString+Mail.h>
#include <Mailer/SOGoMailBodyPart.h> #include <Mailer/SOGoMailBodyPart.h>
#include <SOGo/SOGoUser.h> #include <SOGo/SOGoUser.h>
#include <SOGo/NSString+Utilities.h>
typedef struct { typedef struct {
uint32_t dwLowDateTime; uint32_t dwLowDateTime;
@ -283,7 +282,7 @@ struct GlobalObjectId {
performed: b]; performed: b];
} }
} }
else if ([thePart isKindOfClass: [NGMimeBodyPart class]]) else if ([thePart isKindOfClass: [NGMimeBodyPart class]] || [thePart isKindOfClass: [NGMimeMessage class]])
{ {
NGMimeFileData *fdata; NGMimeFileData *fdata;
id body; id body;
@ -323,6 +322,11 @@ struct GlobalObjectId {
if (s) if (s)
{ {
// We sanitize the content immediately, in case we have non-UNICODE safe
// characters that would be re-encoded later in HTML entities and thus,
// ignore afterwards.
s = [s safeString];
body = [s dataUsingEncoding: NSUTF8StringEncoding]; body = [s dataUsingEncoding: NSUTF8StringEncoding];
} }
@ -362,8 +366,7 @@ struct GlobalObjectId {
if (message) if (message)
{ {
[self _sanitizedMIMEPart: [message body] [self _sanitizedMIMEPart: message performed: &b];
performed: &b];
if (b) if (b)
{ {
@ -506,7 +509,6 @@ struct GlobalObjectId {
// //
- (NSString *) activeSyncRepresentationInContext: (WOContext *) _context - (NSString *) activeSyncRepresentationInContext: (WOContext *) _context
{ {
NSAutoreleasePool *pool;
NSData *d, *globalObjId; NSData *d, *globalObjId;
NSArray *attachmentKeys; NSArray *attachmentKeys;
NSMutableString *s; NSMutableString *s;
@ -699,10 +701,6 @@ struct GlobalObjectId {
// Body - namespace 17 // Body - namespace 17
preferredBodyType = [[context objectForKey: @"BodyPreferenceType"] intValue]; preferredBodyType = [[context objectForKey: @"BodyPreferenceType"] intValue];
// Make use of a local pool here as _preferredBodyDataUsingType:nativeType: will consume
// a significant amout of RAM and file descriptors
pool = [[NSAutoreleasePool alloc] init];
nativeBodyType = 1; nativeBodyType = 1;
d = [self _preferredBodyDataUsingType: preferredBodyType nativeType: &nativeBodyType]; d = [self _preferredBodyDataUsingType: preferredBodyType nativeType: &nativeBodyType];
@ -742,9 +740,7 @@ struct GlobalObjectId {
} }
[s appendString: @"</Body>"]; [s appendString: @"</Body>"];
} }
DESTROY(pool);
// Attachments -namespace 16 // Attachments -namespace 16
attachmentKeys = [self fetchFileAttachmentKeys]; attachmentKeys = [self fetchFileAttachmentKeys];
if ([attachmentKeys count]) if ([attachmentKeys count])

View File

@ -55,7 +55,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
[o setUID: theUID]; [o setUID: theUID];
[o setSequence: theSequence]; [o setSequence: theSequence];
return o; return [o autorelease];
} }
- (void) dealloc - (void) dealloc

View File

@ -61,7 +61,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
[response setHeader: @"private" forKey: @"Cache-Control"]; [response setHeader: @"private" forKey: @"Cache-Control"];
[response setHeader: @"OPTIONS, POST" forKey: @"Allow"]; [response setHeader: @"OPTIONS, POST" forKey: @"Allow"];
[response setHeader: @"14.1" forKey: @"MS-Server-ActiveSync"]; [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: @"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"]; [response setHeader: @"OPTIONS, POST" forKey: @"Public"];
} }

View File

@ -16,10 +16,11 @@ ADDITIONAL_INCLUDE_DIRS += \
-I../../SOPE -I../../SOPE
ADDITIONAL_LIB_DIRS += \ ADDITIONAL_LIB_DIRS += \
-L../SoObjects/SOGo/SOGo.framework/ \ -L../SoObjects/SOGo/SOGo.framework/Versions/Current/sogo \
-L../SoObjects/SOGo/$(GNUSTEP_OBJ_DIR)/ \ -L../SoObjects/SOGo/$(GNUSTEP_OBJ_DIR)/ \
-L../SOPE/NGCards/$(GNUSTEP_OBJ_DIR)/ \ -L../SOPE/NGCards/$(GNUSTEP_OBJ_DIR)/ \
-L/usr/local/lib -L/usr/local/lib \
-Wl,-rpath,$(SOGO_SYSLIBDIR)/sogo
BUNDLE_LIBS += \ BUNDLE_LIBS += \
-lSOGo \ -lSOGo \

View File

@ -76,13 +76,13 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- (NSString *) activeSyncRepresentationInContext: (WOContext *) context - (NSString *) activeSyncRepresentationInContext: (WOContext *) context
{ {
NSMutableString *s; NSMutableString *s;
NSArray *attendees; NSArray *attendees, *categories;
iCalPerson *organizer, *attendee; iCalPerson *organizer, *attendee;
iCalTimeZone *tz; iCalTimeZone *tz;
id o; id o;
int v; int v, i, meetingStatus;
NSTimeZone *userTimeZone; NSTimeZone *userTimeZone;
userTimeZone = [[[context activeUser] userDefaults] timeZone]; userTimeZone = [[[context activeUser] userDefaults] timeZone];
@ -127,13 +127,14 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
tz = [(iCalDateTime *)[self firstChildWithTag: @"dtstart"] timeZone]; tz = [(iCalDateTime *)[self firstChildWithTag: @"dtstart"] timeZone];
if (!tz) if (!tz)
tz = [iCalTimeZone timeZoneForName: @"Europe/London"]; tz = [iCalTimeZone timeZoneForName: [userTimeZone name]];
[s appendFormat: @"<TimeZone xmlns=\"Calendar:\">%@</TimeZone>", [tz activeSyncRepresentationInContext: context]]; [s appendFormat: @"<TimeZone xmlns=\"Calendar:\">%@</TimeZone>", [tz activeSyncRepresentationInContext: context]];
// Organizer and other invitations related properties // Organizer and other invitations related properties
if ((organizer = [self organizer])) if ((organizer = [self organizer]))
{ {
meetingStatus = 1; // meeting and the user is the meeting organizer.
o = [organizer rfc822Email]; o = [organizer rfc822Email];
if ([o length]) if ([o length])
{ {
@ -159,7 +160,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
[s appendString: @"<Attendee xmlns=\"Calendar:\">"]; [s appendString: @"<Attendee xmlns=\"Calendar:\">"];
attendee = [attendees objectAtIndex: i]; attendee = [attendees objectAtIndex: i];
[s appendFormat: @"<Attendee_Email xmlns=\"Calendar:\">%@</Attendee_Email>", [attendee rfc822Email]]; [s appendFormat: @"<Attendee_Email xmlns=\"Calendar:\">%@</Attendee_Email>", [[attendee rfc822Email] activeSyncRepresentationInContext: context]];
[s appendFormat: @"<Attendee_Name xmlns=\"Calendar:\">%@</Attendee_Name>", [[attendee cn] activeSyncRepresentationInContext: context]]; [s appendFormat: @"<Attendee_Name xmlns=\"Calendar:\">%@</Attendee_Name>", [[attendee cn] activeSyncRepresentationInContext: context]];
attendee_status = [self _attendeeStatus: attendee]; attendee_status = [self _attendeeStatus: attendee];
@ -177,6 +178,10 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
} }
[s appendString: @"</Attendees>"]; [s appendString: @"</Attendees>"];
} }
else
{
meetingStatus = 0; // appointment
}
// This depends on the 'NEEDS-ACTION' parameter. // This depends on the 'NEEDS-ACTION' parameter.
// This will trigger the SendMail command // This will trigger the SendMail command
@ -186,18 +191,21 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
int attendee_status; int attendee_status;
meetingStatus = 3; // event is a meeting, and the user is not the meeting organizer
attendee = [self userAsAttendee: [context activeUser]]; attendee = [self userAsAttendee: [context activeUser]];
attendee_status = [self _attendeeStatus: attendee]; attendee_status = [self _attendeeStatus: attendee];
[s appendFormat: @"<ResponseRequested xmlns=\"Calendar:\">%d</ResponseRequested>", 1]; [s appendFormat: @"<ResponseRequested xmlns=\"Calendar:\">%d</ResponseRequested>", 1];
[s appendFormat: @"<ResponseType xmlns=\"Calendar:\">%d</ResponseType>", attendee_status]; [s appendFormat: @"<ResponseType xmlns=\"Calendar:\">%d</ResponseType>", attendee_status];
[s appendFormat: @"<MeetingStatus xmlns=\"Calendar:\">%d</MeetingStatus>", 3];
[s appendFormat: @"<DisallowNewTimeProposal xmlns=\"Calendar:\">%d</DisallowNewTimeProposal>", 1]; [s appendFormat: @"<DisallowNewTimeProposal xmlns=\"Calendar:\">%d</DisallowNewTimeProposal>", 1];
// BusyStatus -- http://msdn.microsoft.com/en-us/library/ee202290(v=exchg.80).aspx // BusyStatus -- http://msdn.microsoft.com/en-us/library/ee202290(v=exchg.80).aspx
[s appendFormat: @"<BusyStatus xmlns=\"Calendar:\">%d</BusyStatus>", 2]; [s appendFormat: @"<BusyStatus xmlns=\"Calendar:\">%d</BusyStatus>", 2];
} }
[s appendFormat: @"<MeetingStatus xmlns=\"Calendar:\">%d</MeetingStatus>", meetingStatus];
// Subject -- http://msdn.microsoft.com/en-us/library/ee157192(v=exchg.80).aspx // Subject -- http://msdn.microsoft.com/en-us/library/ee157192(v=exchg.80).aspx
if ([[self summary] length]) if ([[self summary] length])
[s appendFormat: @"<Subject xmlns=\"Calendar:\">%@</Subject>", [[self summary] activeSyncRepresentationInContext: context]]; [s appendFormat: @"<Subject xmlns=\"Calendar:\">%@</Subject>", [[self summary] activeSyncRepresentationInContext: context]];
@ -223,12 +231,25 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
// Sensitivity // Sensitivity
if ([[self accessClass] isEqualToString: @"PRIVATE"]) if ([[self accessClass] isEqualToString: @"PRIVATE"])
v = 2; v = 2;
if ([[self accessClass] isEqualToString: @"CONFIDENTIAL"]) else if ([[self accessClass] isEqualToString: @"CONFIDENTIAL"])
v = 3; v = 3;
else else
v = 0; v = 0;
[s appendFormat: @"<Sensitivity xmlns=\"Calendar:\">%d</Sensitivity>", v]; [s appendFormat: @"<Sensitivity xmlns=\"Calendar:\">%d</Sensitivity>", v];
categories = [self categories];
if ([categories count])
{
[s appendFormat: @"<Categories xmlns=\"Calendar:\">"];
for (i = 0; i < [categories count]; i++)
{
[s appendFormat: @"<Category xmlns=\"Calendar:\">%@</Category>", [[categories objectAtIndex: i] activeSyncRepresentationInContext: context]];
}
[s appendFormat: @"</Categories>"];
}
// Reminder -- http://msdn.microsoft.com/en-us/library/ee219691(v=exchg.80).aspx // Reminder -- http://msdn.microsoft.com/en-us/library/ee219691(v=exchg.80).aspx
// TODO: improve this to handle more alarm types // TODO: improve this to handle more alarm types
@ -250,11 +271,12 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
o = [self comment]; o = [self comment];
if ([o length]) if ([o length])
{ {
// 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]; o = [o activeSyncRepresentationInContext: context];
[s appendString: @"<Body xmlns=\"AirSyncBase:\">"]; [s appendString: @"<Body xmlns=\"AirSyncBase:\">"];
[s appendFormat: @"<Type>%d</Type>", 1]; [s appendFormat: @"<Type>%d</Type>", 1];
[s appendFormat: @"<EstimatedDataSize>%d</EstimatedDataSize>", [o length]]; [s appendFormat: @"<EstimatedDataSize>%d</EstimatedDataSize>", [o length]];
[s appendFormat: @"<Truncated>%d</Truncated>", 0];
[s appendFormat: @"<Data>%@</Data>", o]; [s appendFormat: @"<Data>%@</Data>", o];
[s appendString: @"</Body>"]; [s appendString: @"</Body>"];
} }
@ -310,7 +332,6 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
iCalTimeZone *tz; iCalTimeZone *tz;
id o; id o;
NSInteger tzOffset;
BOOL isAllDay; BOOL isAllDay;
if ((o = [theValues objectForKey: @"UID"])) if ((o = [theValues objectForKey: @"UID"]))
@ -334,18 +355,10 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
[o intValue]; [o intValue];
} }
//
//
//
if ((o = [theValues objectForKey: @"MeetingStatus"]))
{
[o intValue];
}
// //
// 0- normal, 1- personal, 2- private and 3-confidential // 0- normal, 1- personal, 2- private and 3-confidential
// //
if ((o = [theValues objectForKey: @"Sensitivy"])) if ((o = [theValues objectForKey: @"Sensitivity"]))
{ {
switch ([o intValue]) switch ([o intValue])
{ {
@ -362,9 +375,19 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
} }
} }
if ((o = [theValues objectForKey: @"TimeZone"])) // Categories
if ((o = [theValues objectForKey: @"Categories"]) && [o length])
[self setCategories: o];
// We ignore TimeZone sent by mobile devices for now.
// Some Windows devices don't send during event updates.
//if ((o = [theValues objectForKey: @"TimeZone"]))
// {
// }
//else
{ {
// Ugh, we ignore it for now. // We haven't received a timezone, let's use the user's timezone
// specified in SOGo for now.
userTimeZone = [[[context activeUser] userDefaults] timeZone]; userTimeZone = [[[context activeUser] userDefaults] timeZone];
tz = [iCalTimeZone timeZoneForName: [userTimeZone name]]; tz = [iCalTimeZone timeZoneForName: [userTimeZone name]];
[(iCalCalendar *) parent addTimeZone: tz]; [(iCalCalendar *) parent addTimeZone: tz];
@ -383,21 +406,13 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
start = (iCalDateTime *) [self uniqueChildWithTag: @"dtstart"]; start = (iCalDateTime *) [self uniqueChildWithTag: @"dtstart"];
[start setTimeZone: tz]; [start setTimeZone: tz];
if (isAllDay) if (isAllDay)
{ {
tzOffset = [userTimeZone secondsFromGMTForDate: o];
o = [o dateByAddingYears: 0 months: 0 days: 0
hours: 0 minutes: 0
seconds: tzOffset];
[start setDate: o]; [start setDate: o];
[start setTimeZone: nil]; [start setTimeZone: nil];
} }
else else
{ {
tzOffset = [userTimeZone secondsFromGMTForDate: o];
o = [o dateByAddingYears: 0 months: 0 days: 0
hours: 0 minutes: 0
seconds: tzOffset];
[start setDateTime: o]; [start setDateTime: o];
} }
} }
@ -410,19 +425,11 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
if (isAllDay) if (isAllDay)
{ {
tzOffset = [userTimeZone secondsFromGMTForDate: o];
o = [o dateByAddingYears: 0 months: 0 days: 0
hours: 0 minutes: 0
seconds: tzOffset];
[end setDate: o]; [end setDate: o];
[end setTimeZone: nil]; [end setTimeZone: nil];
} }
else else
{ {
tzOffset = [userTimeZone secondsFromGMTForDate: o];
o = [o dateByAddingYears: 0 months: 0 days: 0
hours: 0 minutes: 0
seconds: tzOffset];
[end setDateTime: o]; [end setDateTime: o];
} }
} }
@ -481,11 +488,33 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
[self setOrganizer: person]; [self setOrganizer: person];
} }
//
// iOS is plain stupid here. It seends event invitations with no Organizer.
// We check this corner-case and if MeetingStatus == 1 (see http://msdn.microsoft.com/en-us/library/ee219342(v=exchg.80).aspx or details)
// and there's no organizer, we fake one.
//
if ((o = [theValues objectForKey: @"MeetingStatus"]))
{
if ([o intValue] == 1 && ![theValues objectForKey: @"Organizer_Email"])
{
iCalPerson *person;
person = [iCalPerson elementWithTag: @"organizer"];
[person setEmail: [[[context activeUser] primaryIdentity] objectForKey: @"email"]];
[person setCn: [[context activeUser] cn]];
[person setPartStat: @"ACCEPTED"];
[self setOrganizer: person];
}
}
// Attendees - we don't touch the values if we're an attendee. This is gonna // Attendees - we don't touch the values if we're an attendee. This is gonna
// be done automatically by the ActiveSync client when invoking MeetingResponse. // be done automatically by the ActiveSync client when invoking MeetingResponse.
if (![self userIsAttendee: [context activeUser]]) if (![self userIsAttendee: [context activeUser]])
{ {
if ((o = [theValues objectForKey: @"Attendees"])) // Windows phones sens sometimes an empty Attendees tag.
// We check it's an array before processing it.
if ((o = [theValues objectForKey: @"Attendees"])&& [o isKindOfClass: [NSArray class]])
{ {
NSMutableArray *attendees; NSMutableArray *attendees;
NSDictionary *attendee; NSDictionary *attendee;

View File

@ -76,15 +76,17 @@ struct SYSTEMTIME {
byMonth = [rrule byMonth]; byMonth = [rrule byMonth];
if ([byMonth count] > 0) if ([byMonth count] > 0)
{ {
tzData->wYear = 0;
tzData->wMonth = [[byMonth objectAtIndex: 0] intValue]; tzData->wMonth = [[byMonth objectAtIndex: 0] intValue];
mask = [rrule byDayMask]; mask = [rrule byDayMask];
tzData->wDayOfWeek = [mask firstDay]; tzData->wDayOfWeek = [mask firstDay];
tzData->wDay = [mask firstOccurrence]; tzData->wDay = ([mask firstOccurrence] == -1) ? 5 : [mask firstOccurrence];
dateValue = [self startDate]; dateValue = [self startDate];
tzData->wHour = [dateValue hourOfDay]; tzData->wHour = [dateValue hourOfDay];
tzData->wMinute = [dateValue minuteOfHour]; tzData->wMinute = [dateValue minuteOfHour];
tzData->wSecond = [dateValue secondOfMinute]; tzData->wSecond = [dateValue secondOfMinute];
tzData->wMilliseconds = 0;
} }
} }

View File

@ -54,9 +54,10 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- (NSString *) activeSyncRepresentationInContext: (WOContext *) context - (NSString *) activeSyncRepresentationInContext: (WOContext *) context
{ {
NSMutableString *s; NSMutableString *s;
NSArray *categories;
id o; id o;
int v; int v, i;
s = [NSMutableString string]; s = [NSMutableString string];
@ -96,9 +97,25 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
// Reminder - FIXME // Reminder - FIXME
[s appendFormat: @"<ReminderSet xmlns=\"Tasks:\">%d</ReminderSet>", 0]; [s appendFormat: @"<ReminderSet xmlns=\"Tasks:\">%d</ReminderSet>", 0];
// Sensitivity - FIXME if ([[self accessClass] isEqualToString: @"PRIVATE"])
[s appendFormat: @"<Sensitivity xmlns=\"Tasks:\">%d</Sensitivity>", 0]; v = 2;
else if ([[self accessClass] isEqualToString: @"CONFIDENTIAL"])
v = 3;
else
v = 0;
categories = [self categories];
if ([categories count])
{
[s appendFormat: @"<Categories xmlns=\"Tasks:\">"];
for (i = 0; i < [categories count]; i++)
{
[s appendFormat: @"<Category xmlns=\"Tasks:\">%@</Category>", [[categories objectAtIndex: i] activeSyncRepresentationInContext: context]];
}
[s appendFormat: @"</Categories>"];
}
// Subject // Subject
o = [self summary]; o = [self summary];
if ([o length]) if ([o length])
@ -106,11 +123,12 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
if ((o = [self comment])) if ((o = [self comment]))
{ {
// 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]; o = [o activeSyncRepresentationInContext: context];
[s appendString: @"<Body xmlns=\"AirSyncBase:\">"]; [s appendString: @"<Body xmlns=\"AirSyncBase:\">"];
[s appendFormat: @"<Type>%d</Type>", 1]; [s appendFormat: @"<Type>%d</Type>", 1];
[s appendFormat: @"<EstimatedDataSize>%d</EstimatedDataSize>", [o length]]; [s appendFormat: @"<EstimatedDataSize>%d</EstimatedDataSize>", [o length]];
[s appendFormat: @"<Truncated>%d</Truncated>", 0];
[s appendFormat: @"<Data>%@</Data>", o]; [s appendFormat: @"<Data>%@</Data>", o];
[s appendString: @"</Body>"]; [s appendString: @"</Body>"];
} }
@ -125,8 +143,6 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
iCalTimeZone *tz; iCalTimeZone *tz;
id o; id o;
NSInteger tzOffset;
userTimeZone = [[[context activeUser] userDefaults] timeZone]; userTimeZone = [[[context activeUser] userDefaults] timeZone];
tz = [iCalTimeZone timeZoneForName: [userTimeZone name]]; tz = [iCalTimeZone timeZoneForName: [userTimeZone name]];
[(iCalCalendar *) parent addTimeZone: tz]; [(iCalCalendar *) parent addTimeZone: tz];
@ -147,10 +163,6 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
o = [o calendarDate]; o = [o calendarDate];
completed = (iCalDateTime *) [self uniqueChildWithTag: @"completed"]; 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]; [completed setDate: o];
[self setStatus: @"COMPLETED"]; [self setStatus: @"COMPLETED"];
} }
@ -159,15 +171,9 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
{ {
iCalDateTime *due; iCalDateTime *due;
o = [o calendarDate]; o = [o calendarDate];
due = (iCalDateTime *) [self uniqueChildWithTag: @"due"]; due = (iCalDateTime *) [self uniqueChildWithTag: @"due"];
[due setTimeZone: tz]; [due setTimeZone: tz];
tzOffset = [userTimeZone secondsFromGMTForDate: o];
o = [o dateByAddingYears: 0 months: 0 days: 0
hours: 0 minutes: 0
seconds: tzOffset];
[due setDateTime: o]; [due setDateTime: o];
} }
@ -182,6 +188,29 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
[self setPriority: @"9"]; [self setPriority: @"9"];
} }
//
// 0- normal, 1- personal, 2- private and 3-confidential
//
if ((o = [theValues objectForKey: @"Sensitivity"]))
{
switch ([o intValue])
{
case 2:
[self setAccessClass: @"PRIVATE"];
break;
case 3:
[self setAccessClass: @"CONFIDENTIAL"];
break;
case 0:
case 1:
default:
[self setAccessClass: @"PUBLIC"];
}
}
// Categories
if ((o = [theValues objectForKey: @"Categories"]) && [o length])
[self setCategories: o];
if ((o = [theValues objectForKey: @"ReminderTime"])) if ((o = [theValues objectForKey: @"ReminderTime"]))
{ {

View File

@ -26,12 +26,23 @@ Alias /SOGo/WebServerResources/ \
## need to set the "SOGoTrustProxyAuthentication" SOGo user default to YES and ## need to set the "SOGoTrustProxyAuthentication" SOGo user default to YES and
## adjust the "x-webobjects-remote-user" proxy header in the "Proxy" section ## adjust the "x-webobjects-remote-user" proxy header in the "Proxy" section
## below. ## below.
#
## For full proxy-side authentication:
#<Location /SOGo> #<Location /SOGo>
# AuthType XXX # AuthType XXX
# Require valid-user # Require valid-user
# SetEnv proxy-nokeepalive 1 # SetEnv proxy-nokeepalive 1
# Allow from all # Allow from all
#</Location> #</Location>
#
## For proxy-side authentication only for CardDAV and GroupDAV from external
## clients:
#<Location /SOGo/dav>
# AuthType XXX
# Require valid-user
# SetEnv proxy-nokeepalive 1
# Allow from all
#</Location>
ProxyRequests Off ProxyRequests Off
SetEnv proxy-nokeepalive 1 SetEnv proxy-nokeepalive 1
@ -64,7 +75,8 @@ ProxyPass /SOGo http://127.0.0.1:20000/SOGo retry=0
## When using proxy-side autentication, you need to uncomment and ## When using proxy-side autentication, you need to uncomment and
## adjust the following line: ## adjust the following line:
# RequestHeader set "x-webobjects-remote-user" "%{REMOTE_USER}e" RequestHeader unset "x-webobjects-remote-user"
# RequestHeader set "x-webobjects-remote-user" "%{REMOTE_USER}e" env=REMOTE_USER
RequestHeader set "x-webobjects-server-protocol" "HTTP/1.0" RequestHeader set "x-webobjects-server-protocol" "HTTP/1.0"

2960
ChangeLog

File diff suppressed because it is too large Load Diff

View File

@ -1,7 +1,7 @@
all: $(patsubst %.asciidoc,%.pdf,$(wildcard *.asciidoc)) all: $(patsubst %.asciidoc,%.pdf,$(wildcard *.asciidoc))
%.pdf : %.asciidoc %.pdf : %.asciidoc
asciidoc -a docinfo1 -b docbook -d book -d book -o $<.docbook $< asciidoc -a docinfo1 -b docbook -d book -o $<.docbook $<
fop -c fonts/fop-config.xml -xsl docbook/xsl/sogo-fo.xsl -xml $<.docbook -pdf $@ fop -c fonts/fop-config.xml -xsl docbook/xsl/sogo-fo.xsl -xml $<.docbook -pdf $@
clean: clean:

View File

@ -80,6 +80,12 @@ and others) 
* SMTP server (Postfix, Sendmail and others) * SMTP server (Postfix, Sendmail and others)
* IMAP server (Courier, Cyrus IMAP Server, Dovecot and others) * IMAP server (Courier, Cyrus IMAP Server, Dovecot and others)
If you plan to use ActiveSync, an IMAP server supporting the ACL,
UIDPLUS, QRESYNC, ANNOTATE (or X-GUID) IMAP extensions is required,
such as Cyrus IMAP version 2.4 or later, or Dovecot version
2.1 or later. If your current IMAP server does not support these
extensions, you can use Dovecot's proxying capabilities.
In this guide, we assume that all those components are running on the In this guide, we assume that all those components are running on the
same server (i.e., `localhost` or `127.0.0.1`) that SOGo will be same server (i.e., `localhost` or `127.0.0.1`) that SOGo will be
installed on. installed on.
@ -193,7 +199,7 @@ installation for a Red Hat or CentOS distribution.
Software Downloads Software Downloads
~~~~~~~~~~~~~~~~~~ ~~~~~~~~~~~~~~~~~~
SOGo can be installed using the+yum+utility. To do so, first create SOGo can be installed using the `yum` utility. To do so, first create
the `/etc/yum.repos.d/inverse.repo` configuration file with the following the `/etc/yum.repos.d/inverse.repo` configuration file with the following
content: content:
@ -258,13 +264,13 @@ In SOGo, the user's applications settings are stored
in `/etc/sogo/sogo.conf`. You can use your favourite text editor to in `/etc/sogo/sogo.conf`. You can use your favourite text editor to
modify the file. modify the file.
The +sogo.conf+ file is a serialized _property list_. This simple format The `sogo.conf` file is a serialized _property list_. This simple format
encapsulates four basic data types: arrays, dictionaries (or hashes), encapsulates four basic data types: arrays, dictionaries (or hashes),
strings and numbers. Numbers are represented as-is, except for booleans strings and numbers. Numbers are represented as-is, except for booleans
which can take the unquoted values `YES` and `NO`. Strings are not which can take the unquoted values `YES` and `NO`. Strings are not
mandatorily quoted, but doing so will avoid you many problems. A mandatorily quoted, but doing so will avoid you many problems. A
dictionary is a sequence of key and value pairs separated in their dictionary is a sequence of key and value pairs separated in their
middle with a `=` sign. It starts with a `\{` and ends with a middle with a `=` sign. It starts with a `{` and ends with a
corresponding `}`. Each value definition in a dictionary ends with a corresponding `}`. Each value definition in a dictionary ends with a
semicolon. An array is a chain of values starting with `(` and ending semicolon. An array is a chain of values starting with `(` and ending
with `)`, where the values are separated with a `,`. Also, the file with `)`, where the values are separated with a `,`. Also, the file
@ -273,6 +279,11 @@ is not required, only recommended. Block comments are delimited by `/*`
and `*/` and can span multiple lines while line comments must start with and `*/` and can span multiple lines while line comments must start with
`//`. `//`.
The configuration must be contained in a root dictionary, thus be completely
wrapped within curly brackets `{ [configuration] }`. If SOGo refuses to
start due to syntax errors in its configuration file, `plparse` is helpful
for finding these, as it indicates the line containing the problem.
Preferences Hierarchy Preferences Hierarchy
~~~~~~~~~~~~~~~~~~~~~ ~~~~~~~~~~~~~~~~~~~~~
@ -425,29 +436,42 @@ Defaults to `YES` when unset.
|The location of the SSL private key file on the filesystem that is used |The location of the SSL private key file on the filesystem that is used
by SOGo to sign and encrypt communications with the SAML2 identity by SOGo to sign and encrypt communications with the SAML2 identity
provider. This file must be generated for each running SOGo service provider. This file must be generated for each running SOGo service
(rather than host). (rather than host). Make sure this file is readable by the SOGo user.
|S |SOGoSAML2CertiticateLocation |S |SOGoSAML2CertiticateLocation
|The location of the SSL certificate file. This file must be generated |The location of the SSL certificate file. This file must be generated
for each running SOGo service. for each running SOGo service. Make sure this file is readable by the SOGo user.
|S |SOGoSAML2IdpMetadataLocation |S |SOGoSAML2IdpMetadataLocation
|The location of the metadata file that describes the services available |The location of the metadata file that describes the services available
on the SAML2 identify provider. on the SAML2 identify provider. The content of this file is usually generated
directly by your SAML 2.0 IdP solution. For example, using SimpleSAMLphp, you
can get the metadata directly from https://MYSERVER/simplesaml/saml2/idp/metadata.php
Make sure this file is readable by the SOGo user.
|S |SOGoSAML2IdpPublicKeyLocation |S |SOGoSAML2IdpPublicKeyLocation
|The location of the SSL public key file on the filesystem that is used |The location of the SSL public key file on the filesystem that is used
by SOGo to sign and encrypt communications with the SAML2 identity by SOGo to sign and encrypt communications with the SAML2 identity
provider. This file should be part of the setup of your identity provider. This file should be part of the setup of your identity
provider. provider. Make sure this file is readable by the SOGo user.
|S |SOGoSAML2IdpCertificateLocation |S |SOGoSAML2IdpCertificateLocation
|The location of the SSL certificate file. This file should be part of |The location of the SSL certificate file. This file should be part of
the setup of your identity provider. the setup of your identity provider. Make sure this file is readable by the SOGo user.
|S |SOGoSAML2LoginAttribute
|The attribute provided by the IdP to identify the user in SOGo.
|S |SOGoSAML2LogoutEnabled |S |SOGoSAML2LogoutEnabled
|Boolean value indicated whether the "Logout" link is enabled when using |Boolean value indicated whether the "Logout" link is enabled when using
SAML2 as authentication mechanism. SAML2 as authentication mechanism. When using this feature, SOGo will invoke
the IdP to proceed with the logout procedure. When the user clicks on the logout
button, a redirection will be made to the IdP to trigger the logout.
|S |SOGoSAML2LogoutURL
|The URL to which redirect the user after the "Logout" link is clicked.
SOGoSAML2LogoutEnabled must be set to YES. If unset, the user will be
redirected to a blank page.
|D |SOGoTimeZone |D |SOGoTimeZone
|Parameter used to set a default time zone for users. The default |Parameter used to set a default time zone for users. The default
@ -887,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. 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 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) |filter (optional)
|The filter to use for LDAP queries, it should be defined as an |The filter to use for LDAP queries, it should be defined as an
@ -974,7 +999,9 @@ context as Dovecot stores in its database.
|passwordPolicy |passwordPolicy
|If set to `YES`, SOGo will use the extended LDAP Password Policies |If set to `YES`, SOGo will use the extended LDAP Password Policies
attributes. If you LDAP server does not support those and you activate 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.
|isAddressBook |isAddressBook
|If set to `YES`, this LDAP source is used as a shared address book |If set to `YES`, this LDAP source is used as a shared address book
@ -1167,7 +1194,7 @@ keytool -import -keystore /etc/ssl/certs/java/cacerts \
*The certificate used by the CAS server must also be trusted by SOGo.* *The certificate used by the CAS server must also be trusted by SOGo.*
In case of a self-signed certificate, this means exporting tomcat's In case of a self-signed certificate, this means exporting tomcat's
certificate using the +keytool+utility, converting it to PEM format and certificate using the `keytool` utility, converting it to PEM format and
appending it to the `ca-certificates.crt` file (the name and location of appending it to the `ca-certificates.crt` file (the name and location of
that file differs between distributions). Basically: that file differs between distributions). Basically:
@ -1230,13 +1257,32 @@ documentation of your identity provider and the SAML2 configuration keys
that are listed above for proper setup. Once a SOGo instance is that are listed above for proper setup. Once a SOGo instance is
configured properly, the metadata for that instance can be retrieved configured properly, the metadata for that instance can be retrieved
from `http://<hostname>/SOGo/saml2-metadata` for registration with the from `http://<hostname>/SOGo/saml2-metadata` for registration with the
identity provider. identity provider. SOGo will dynamically generate the metadata based on
the SOGoSAML2CertificateLocation's content and the SOGo server name.
When using SimpleSAMLphp, make sure the convert OID to names by modifying your
`metadata/saml20-idp-hosted.php` to contain something like this:
----
'attributes.NameFormat' => 'urn:oasis:names:tc:SAML:2.0:attrname-format:uri',
'authproc' => array(
100 => array('class' => 'core:AttributeMap', 'oid2name'),
),
----
If you want to test the IdP-initiated logout using SimpleSAMLphp, you can do so by opening
the following URL:
----
https://idp.example.org/simplesaml/saml2/idp/SingleLogoutService.php?ReturnTo=www.sogo.nu
----
In order to relay authentication information to your IMAP server and if In order to relay authentication information to your IMAP server and if
you make use of the CrudeSAML SASL plugin, you need to make sure that you make use of the CrudeSAML SASL plugin, you need to make sure that
_NGImap4AuthMechanism_ is configured to use the `SAML` mechanism. If you _NGImap4AuthMechanism_ is configured to use the `SAML` mechanism. If you
make use of the CrudeSAML PAM plugin, this value may be left empty. make use of the CrudeSAML PAM plugin, this value may be left empty.
Database Configuration Database Configuration
~~~~~~~~~~~~~~~~~~~~~~ ~~~~~~~~~~~~~~~~~~~~~~
@ -1433,7 +1479,10 @@ case, SOGo will consider the returned entry to be a resource.
which a resource can be part of at any point in time. 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 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) |DomainFieldName (optional)
|If set, SOGo will use the value of that field as the domain associated |If set, SOGo will use the value of that field as the domain associated
@ -1581,8 +1630,8 @@ Note that TLS is supported but SSL is not.
|D |SOGoSieveFolderEncoding |D |SOGoSieveFolderEncoding
|Parameter used to specify which encoding is used for IMAP folder names |Parameter used to specify which encoding is used for IMAP folder names
in Sieve filters. Defaults to "UTF-7". The other possible value is in Sieve filters. Defaults to `UTF-7`. The other possible value is
"UTF-8". `UTF-8`.
|U |SOGoMailShowSubscribedFoldersOnly |U |SOGoMailShowSubscribedFoldersOnly
|Parameter used to specify if the Web interface should only show |Parameter used to specify if the Web interface should only show
@ -1632,6 +1681,12 @@ cronjob `sogo-tmpwatch`.
Defaults to `/var/spool/sogo`. Defaults to `/var/spool/sogo`.
|S |NGImap4DisableIMAP4Pooling
|Disables IMAP pooling when set to `YES`. Enable pooling by setting to
`NO` or using a caching proxy like imapproxy.
The default value is `YES`.
|S |NGImap4ConnectionStringSeparator |S |NGImap4ConnectionStringSeparator
|Parameter used to set the IMAP mailbox separator. Setting this will |Parameter used to set the IMAP mailbox separator. Setting this will
also have an impact on the mailbox separator used by Sieve filters. also have an impact on the mailbox separator used by Sieve filters.
@ -1644,7 +1699,7 @@ SASL mechanism. Please note that feature might be limited at this time.
|D |NGImap4ConnectionGroupIdPrefix |D |NGImap4ConnectionGroupIdPrefix
|Prefix to prepend to names in IMAP ACL transactions, to indicate the |Prefix to prepend to names in IMAP ACL transactions, to indicate the
name is a group name not a user name. name is a group name, not a user name.
RFC4314 gives examples where group names are prefixed with `$`. Dovecot, RFC4314 gives examples where group names are prefixed with `$`. Dovecot,
for one, follows this scheme, and will, for example, apply permissions for one, follows this scheme, and will, for example, apply permissions
@ -1993,7 +2048,7 @@ Defaults to `NO` when unset.
SOGo Configuration Summary SOGo Configuration Summary
~~~~~~~~~~~~~~~~~~~~~~~~~~ ~~~~~~~~~~~~~~~~~~~~~~~~~~
The complete SOGo configuration file+/etc/sogo/sogo.conf+should look The complete SOGo configuration file `/etc/sogo/sogo.conf` should look
like this: like this:
---- ----
@ -2325,7 +2380,7 @@ ActiveSync:
|Parameter used to set the maximum amount of time, in seconds, SOGo will |Parameter used to set the maximum amount of time, in seconds, SOGo will
wait before replying to a Ping command. wait before replying to a Ping command.
If not set, it defaults to `5` seconds. If not set, it defaults to `10` seconds.
|S |SOGoMaximumSyncInterval |S |SOGoMaximumSyncInterval
|Parameter used to set the maximum amount of time, in seconds, SOGo will |Parameter used to set the maximum amount of time, in seconds, SOGo will
@ -2336,10 +2391,20 @@ If not set, it defaults to `30` seconds.
|S |SOGoInternalSyncInterval |S |SOGoInternalSyncInterval
|Parameter used to set the maximum amount of time, in seconds, SOGo will |Parameter used to set the maximum amount of time, in seconds, SOGo will
wait before doing an internal check for data changes (add, delete, and wait before doing an internal check for data changes (add, delete, and
update). This parameter must be lower than _SOGoMaximumSyncInterval_. update). This parameter must be lower than _SOGoMaximumSyncInterval_ and
_SOGoMaximumPingInterval_.
If not set, it defaults to `10` seconds. 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 |S |SOGoMaximumSyncWindowSize
|Parameter used to overwrite the maximum number of items returned during |Parameter used to overwrite the maximum number of items returned during
a Sync operation. a Sync operation.
@ -2352,24 +2417,18 @@ have unexpected behaviour with various ActiveSync clients.
Please be aware of the following limitations: 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 * Outlook 2013 does not search the GAL. One possible alternative
solution is to configure Outlook to use a LDAP server (over SSL) with solution is to configure Outlook to use a LDAP server (over SSL) with
authentication. Alternatively, when supporting more than just the authentication. Outlook 2013 also does not seem to support multiple
personal address book, we'll also be able to expose the LDAP/SQL based address books over ActiveSync.
address books in SOGo over ActiveSync.
* Make sure you do not use a self-signed certificate. While this will * Make sure you do not use a self-signed certificate. While this will
work, Outlook will work intermittently as it will raise popups for work, Outlook will work intermittently as it will raise popups for
certificate validation, sometimes in background, preventing the user to certificate validation, sometimes in background, preventing the user to
see the warning and thus, preventing any synchronization to happen. see the warning and thus, preventing any synchronization to happen.
* ActiveSync clients keep connections open for a while. Each connection * 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 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 to handle many clients. Make sure you tune your SOGo server when having
SOGo. lots of ActiveSync clients.
* Repetitive events with occurrences exceptions are currently not * Repetitive events with occurrences exceptions are currently not
supported. supported.
* Outlook 2013 Autodiscovery is currently not supported. * Outlook 2013 Autodiscovery is currently not supported.
@ -2379,6 +2438,10 @@ see http://support.microsoft.com/kb/291621 for configuration
instructions. On the SOGo side, _SOGoEnablePublicAccess_ must be set to instructions. On the SOGo side, _SOGoEnablePublicAccess_ must be set to
`YES` and the URL to use must be of the following format: `YES` and the URL to use must be of the following format:
`http://<hostname>/SOGo/dav/public/%NAME%/freebusy.ifb` `http://<hostname>/SOGo/dav/public/%NAME%/freebusy.ifb`
* If you have very large mail folders (thousands of messages), you will
need to adjust the word size of your IMAP server. In Dovecot, the parameter
to increase is "imap_max_line_length" while under Cyrus IMAP Server, the
parameter is "maxword". We suggest a buffer of 2MB.
In order to use the SOGo ActiveSync support code in production In order to use the SOGo ActiveSync support code in production
environments, you need to get a proper usage license from Microsoft. environments, you need to get a proper usage license from Microsoft.
@ -2387,7 +2450,7 @@ user base.
To contact Microsoft, please visit: 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 and send an email to iplicreq@microsoft.com
@ -2513,7 +2576,7 @@ any mobile devices that support Microsoft ActiveSync. Microsoft Outlook
2013 is also supported. 2013 is also supported.
The Microsoft ActiveSync server URL is generally something The Microsoft ActiveSync server URL is generally something
like: `http://localhost/Microsoft-Active-Sync`. like: `http://localhost/Microsoft-Server-ActiveSync`.
Upgrading Upgrading
--------- ---------

View File

@ -233,13 +233,12 @@ Installation
This section will guide you through the installation of the native This section will guide you through the installation of the native
Microsoft Outlook compatibility layer SOGo offers. Microsoft Outlook compatibility layer SOGo offers.
Red Hat Enterprise Linux v5 and v6 Red Hat Enterprise Linux v6 x86_64
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
If you are using Red Hat Enterprise Linux (or CentOS) version 5 or If you are using Red Hat Enterprise Linux version 6 x86_64, packages
version 6, packages for Samba 4, OpenChange and SOGo and the SOGo for Samba 4, OpenChange and SOGo and the SOGo OpenChange backend are
OpenChange backend are available from SOGo's web site. Please follow the available from SOGo's web site. Please follow the instructions from
instructions from
http://www.sogo.nu/english/downloads/backend_nightly.html. http://www.sogo.nu/english/downloads/backend_nightly.html.
In order to satisfy certain dependencies, you should also add the EPEL In order to satisfy certain dependencies, you should also add the EPEL
@ -253,53 +252,48 @@ installation:
---- ----
yum clean all && yum makecache yum clean all && yum makecache
yum install samba4 \ yum install samba \
openchange \ openchange \
sogo-openchange-backend \ sogo-openchange-backend \
openchange-ocsmanager \ openchange-ocsmanager \
openchange-rpcproxy openchange-rpcproxy \
mysql-server \
MySQL-python
---- ----
Once the packages are installed, refer to the _Configuration_ chapter Once the packages are installed, refer to the _Configuration_ chapter
from this guide. from this guide.
Debian 6.0 (Squeeze) and Ubuntu 12.04 (Precise Pangolin) [NOTE]
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Samba4/OpenChange are not available for now on CentOS 5 i386/x86_64,
CentOS 6 i386 and CentOS 7.
Samba 4, OpenChange, SOGo and the SOGo OpenChange backend are now Debian 7.0 (Wheezy) and Ubuntu 12.04 (Precise Pangolin)
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
SOGo, OpenChange and the SOGo OpenChange backend are now
available from SOGo's web site. Please follow the instructions from available from SOGo's web site. Please follow the instructions from
http://www.sogo.nu/english/downloads/backend_nightly.html to setup your http://www.sogo.nu/english/downloads/backend.html to setup your
apt sources. apt sources.
Debian Squeeze ships an older version of some libraries required by For Samba 4, you need to use the _wheezy-backports_ repository. To do so, create
Samba 4. In order to workaround this, users of this distribution will
have to use the _squeeze-backports_ repository. To do so, create
`/etc/apt/sources.list.d/backports.list`: `/etc/apt/sources.list.d/backports.list`:
deb http://backports.debian.org/debian-backports squeeze-backports main deb http://http.debian.net/debian wheezy-backports main
Then install the dependencies on Debian Squeeze, do: On Ubuntu 12.04, you will also have to add the Wheezy sources:
---- deb http://ftp.us.debian.org/debian wheezy main
apt-get update deb http://security.debian.org/ wheezy/updates main
apt-get install -t squeeze-backports libwbclient-dev samba-common smbclient libsmbclient libsmbclient-dev
----
Once ready, install the `samba4` package on top of an existing SOGo Then install Samba 4 on top of an existing SOGo
installation: installation:
---- ----
apt-get update apt-get update
apt-get install samba4 apt-get -t wheezy-backports install samba samba-dev
---- ----
The current post installation script shipped with the Samba 4 package is
far from perfect and might fail even on a fresh install. The following
command is needed to let dpkg know that everything is fine about Samba 4
if the post install script fails.
sed --in-place 'N; s/Package: samba4\nStatus: install ok half-configured/Package: samba4\nStatus: install ok installed/;' /var/lib/dpkg/status
Once completed, install the packages related to OpenChange and the SOGo Once completed, install the packages related to OpenChange and the SOGo
provider: provider:
@ -307,13 +301,53 @@ provider:
apt-get install openchangeserver \ apt-get install openchangeserver \
sogo-openchange \ sogo-openchange \
openchangeproxy \ openchangeproxy \
openchange-ocsmanager \ python-ocsmanager \
openchange-rpcproxy mysql-server \
python-mysqldb
---- ----
Once the packages are installed, refer to the _Configuration_ chapter Once the packages are installed, refer to the _Configuration_ chapter
from this guide. from this guide.
[NOTE]
On Ubuntu 12.04, the Samba init scripts need to be modified to
disable the upstart check. For more details, refer to:
https://wiki.samba.org/index.php/Samba4/InitScript
Ubuntu 14.04 (Trusty Tahr)
~~~~~~~~~~~~~~~~~~~~~~~~~~
For Ubuntu 14.04, you must not use the Debian Wheezy backports.
Please follow the instructions from
http://www.sogo.nu/english/downloads/backend.html to setup your
apt sources.
Then install Samba 4 on top of an existing SOGo
installation:
----
apt-get update
apt-get install samba samba-dev
----
Once completed, install the packages related to OpenChange and the SOGo
provider:
----
apt-get install openchangeserver \
sogo-openchange \
openchangeproxy \
python-ocsmanager \
mysql-server \
python-mysqldb
----
Once the packages are installed, refer to the _Configuration_ chapter
from this guide.
Configuration Configuration
------------- -------------
@ -349,16 +383,14 @@ You might consider changing the realm and domain used, to suit your
enviroment. enviroment.
You might also have to You might also have to
remove `/etc/samba4/smb.conf` (or `/etc/samba/smb.conf` on Debian-based remove `/etc/samba/smb.conf` prior running this command.
distributions) prior running this command.
Add the following parameters to the `[global]` section of the Add the following parameters to the `[global]` section of the
`/etc/samba4/smb.conf` (`/samba/smb.conf` if you use a Debian-based `/etc/samba/smb.conf` configuration file:
distribution) configuration file:
---- ----
### Configuration required by OpenChange server ### ### Configuration required by OpenChange server ###
dcerpc endpoint servers = +epmapper, +mapiproxy dcerpc endpoint servers = epmapper, mapiproxy, dnsserver
dcerpc_mapiproxy:server = true dcerpc_mapiproxy:server = true
dcerpc_mapiproxy:interfaces = exchange_emsmdb, exchange_nsp, exchange_ds_rfr dcerpc_mapiproxy:interfaces = exchange_emsmdb, exchange_nsp, exchange_ds_rfr
### Configuration required by OpenChange server ### ### Configuration required by OpenChange server ###
@ -392,11 +424,22 @@ Your Samba 4 configuration file should look like this:
OpenChange Configuration OpenChange Configuration
~~~~~~~~~~~~~~~~~~~~~~~~ ~~~~~~~~~~~~~~~~~~~~~~~~
OpenChange 2.2 stores its metadata in MySQL so you need to have it installed.
First, create the OpenChange MySQL user:
----
$ mysql -u root -p
mysql> CREATE USER 'openchange-user'@'localhost' IDENTIFIED BY 'openchange$123';
mysql> GRANT ALL PRIVILEGES ON `openchange`.* TO 'openchange-user'@'localhost' WITH GRANT OPTION;
mysql> FLUSH PRIVILEGES;
----
The Samba AD schema needs to be filled with additional object The Samba AD schema needs to be filled with additional object
definitions by running the following commands:  definitions by running the following commands: 
---- ----
openchange_provision openchange_provision --standalone
NOTE: This operation can take several minutes NOTE: This operation can take several minutes
[+] Step 1: Register Exchange OIDs [+] Step 1: Register Exchange OIDs
@ -410,38 +453,59 @@ NOTE: This operation can take several minutes
[+] Step 9: Add Exchange classes to Samba schema [+] Step 9: Add Exchange classes to Samba schema
[+] Step 10: Add possSuperior attributes to Exchange classes [+] Step 10: Add possSuperior attributes to Exchange classes
[+] Step 11: Extend existing Samba classes and attributes [+] Step 11: Extend existing Samba classes and attributes
[+] Step 12: Exchange Samba with Exchange configuration objects [+] Step 12: Generic Exchange configuration objects
[+] Step 13: Finalize generic Exchange configuration objects
[SUCCESS] Done!
[+] Step 1: Exchange Samba registration
[SUCCESS] Done!
[+] Step 1: Register Exchange Samba as the main server
[SUCCESS] Done! [SUCCESS] Done!
---- ----
You can safely ignore the "`ERROR: no subClassOf 'serviceAdministrationPoint' for 'rRASAdministrationConnectionPoint'`" message when running the `openchange_provision` command. Create the OpenChange database: 
Provision the OpenChange database: 
---- ----
openchange_provision --openchangedb openchange_provision --openchangedb --openchangedb-uri 'mysql://openchange-user:openchange$123@localhost/openchange'
Setting up openchange db Setting up openchange db
[+] Public Folders [+] Public Folders
=================== ===================
* Public Folder Root 0x0100000000000001 * Public Folder Root : 0x0100000000000001 (72057594037927937)
* IPM_SUBTREE 0x0200000000000001 * IPM_SUBTREE : 0x0200000000000001 (144115188075855873)
* NON_IPM_SUBTREE 0x0300000000000001 * NON_IPM_SUBTREE : 0x0300000000000001 (216172782113783809)
* EFORMS REGISTRY 0x0400000000000001 * EFORMS REGISTRY : 0x0400000000000001 (288230376151711745)
* OFFLINE ADDRESS BOOK 0x0500000000000001 * OFFLINE ADDRESS BOOK : 0x0500000000000001 (360287970189639681)
* /o=First Organization/cn=addrlists/cn=oabs/cn=Default Offline Address Book 0x0600000000000001 * /o=First Organization/cn=addrlists/cn=oabs/cn=Default Offline Address Book: 0x0600000000000001 (432345564227567617)
* SCHEDULE+ FREE BUSY 0x0700000000000001 * SCHEDULE+ FREE BUSY : 0x0700000000000001 (504403158265495553)
* EX:/o=First Organization/ou=Exchange Administrative Group (UBUNTU-OC) 0x0800000000000001 * EX:/o=first organization/ou=first administrative group: 0x0800000000000001 (576460752303423489)
* Events Root 0x0900000000000001 * Events Root : 0x0900000000000001 (648518346341351425)
----
Finally, modify `/etc/samba/smb.conf` to specify OpenChange connection information
for its indexing database. Add the following at the end of the `[global]` section:
----
mapistore:namedproperties = mysql
namedproperties:mysql_user = openchange-user
namedproperties:mysql_pass = openchange$123
namedproperties:mysql_host = localhost
namedproperties:mysql_db = openchange
mapistore:indexing_backend = mysql://openchange-user:openchange$123@localhost/openchange
mapiproxy:openchangedb = mysql://openchange-user:openchange$123@localhost/openchange
---- ----
On RHEL, make sure SELinux is disabled: On RHEL, make sure SELinux is disabled:
setenforce 0 setenforce 0
Next, you can start Samba using the usual command : Next, you can start Samba using the usual command:
/etc/init.d/samba4 start /etc/init.d/samba start
On upstart-based distributions, use:
start samba-ad-dc
You can also launch the OpenChange web services: You can also launch the OpenChange web services:
@ -503,6 +567,10 @@ On Debian-based distributions, do:
update-rc.d apache2 defaults && /etc/init.d/apache2 restart update-rc.d apache2 defaults && /etc/init.d/apache2 restart
[NOTE]
Debian-based distributions are not supported anymore for
OCSManager/rpcproxy. Support will soon resume.
Name Service Configuration for Web Services Name Service Configuration for Web Services
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
@ -540,14 +608,13 @@ samba-tool domain passwordsettings set --complexity=off
samba-tool domain passwordsettings set --min-pwd-length=1 samba-tool domain passwordsettings set --min-pwd-length=1
samba-tool user add <username> samba-tool user add <username>
samba-tool user setexpiry <username> --noexpiry samba-tool user setexpiry <username> --noexpiry
# create user in openchange+ +openchange_newuser --create <username> # create user in openchange
openchange_newuser --create <username>
---- ----
If you don't have a trust between your IMAP server and SOGo, you must at If you don't have a trust between your IMAP server and SOGo, you must at
this point set the cleartext password of the newly created user in this point set the cleartext password of the newly created user in
`/var/lib/samba4/private/mapistore/<username/password` (or `/var/lib/samba/private/mapistore/<username/password`.
`/var/lib/samba/private/mapistore/<username/password` on Debian-based
distributions).
This per-user file contains the cleartext password of the user as a This per-user file contains the cleartext password of the user as a
UTF-8 string, on a single line. This password will be used to UTF-8 string, on a single line. This password will be used to

View File

@ -1,193 +0,0 @@
<?xml version='1.0'?>
<xsl:stylesheet
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:fo="http://www.w3.org/1999/XSL/Format"
version="1.0">
<!-- ********************************************************************
SOGo Documentation Docbook FO Parameters
This file is part of the SOGo project.
Authors:
- Inverse inc. <info@inverse.ca>
Copyright (C) 2011-2014 Inverse inc.
License: GFDL 1.2 or later. http://www.gnu.org/licenses/fdl.html
******************************************************************** -->
<!--
Global Tasks
TODO prettier revhistory
TODO prettier Table of Contents
TODO generate PDF table of contents (like OSX's Preview shows on the right hand side)
TODO title 2
- align with text?
- more above whitespace
TODO change the bullet for a prettier one
TODO title 3 and 4
- align with text?
- should be easier to differentiate (check network guide)
TODO icon on line wrap in monospace boxes
TODO caution, notes, warnings, etc.
- box around it
- sexy icon
TODO -> is converted into an arrow but it's not pretty (is it font or docbook-thingy?)
-->
<!--
Load default values
Real upstream schema is at:
<xsl:import href="http://docbook.sourceforge.net/release/xsl/current/fo/docbook.xsl"/>
but we decided to load all sensible local xsd since it only produce a warning on missing imports.
-->
<!-- CentOS / RHEL -->
<xsl:import href="/usr/share/sgml/docbook/xsl-stylesheets/fo/docbook.xsl"/>
<!-- Debian / Ubuntu -->
<xsl:import href="/usr/share/xml/docbook/stylesheet/docbook-xsl/fo/docbook.xsl"/>
<!-- OSX through mac ports -->
<xsl:import href="/opt/local/share/xsl/docbook-xsl/fo/docbook.xsl"/>
<!-- title page extra styling -->
<xsl:import href="titlepage-fo.xsl"/>
<!-- header / footer extra styling -->
<xsl:import href="headerfooter-fo.xsl"/>
<!-- attaching an image to the verso legalnotice component -->
<xsl:template match="legalnotice" mode="book.titlepage.verso.mode">
<xsl:apply-templates mode="titlepage.mode"/>
<fo:block text-align="right">
<fo:external-graphic src="url('images/inverse-logo.jpg')" width="3in" content-width="scale-to-fit"/>
</fo:block>
</xsl:template>
<!-- stylesheet options -->
<xsl:param name="title.font.family">Lato-Medium</xsl:param>
<xsl:param name="chapter.autolabel" select="0"/>
<xsl:attribute-set name="component.title.properties">
<xsl:attribute name="padding-bottom">0.5em</xsl:attribute>
<xsl:attribute name="border-bottom">solid 2px</xsl:attribute>
<xsl:attribute name="margin-bottom">1em</xsl:attribute>
</xsl:attribute-set>
<xsl:attribute-set name="section.title.level1.properties">
<xsl:attribute name="border-bottom">solid 1px</xsl:attribute>
<xsl:attribute name="margin-top">1em</xsl:attribute>
<xsl:attribute name="margin-bottom">1em</xsl:attribute>
</xsl:attribute-set>
<xsl:attribute-set name="section.title.level2.properties">
<xsl:attribute name="margin-top">1em</xsl:attribute>
<xsl:attribute name="margin-bottom">1em</xsl:attribute>
</xsl:attribute-set>
<xsl:attribute-set name="section.title.level3.properties">
<xsl:attribute name="margin-top">1em</xsl:attribute>
</xsl:attribute-set>
<xsl:attribute-set name="section.title.level4.properties">
<xsl:attribute name="margin-top">1em</xsl:attribute>
</xsl:attribute-set>
<!-- paragraph spacing -->
<xsl:attribute-set name="normal.para.spacing">
<xsl:attribute name="space-before.optimum">1.5em</xsl:attribute>
<xsl:attribute name="space-before.minimum">1.5em</xsl:attribute>
<xsl:attribute name="space-before.maximum">2.2em</xsl:attribute>
</xsl:attribute-set>
<!-- default fonts -->
<xsl:param name="body.font.family">Lato-Light</xsl:param>
<xsl:param name="body.font.master">10</xsl:param>
<xsl:param name="monospace.font.family">Incosolata</xsl:param>
<!-- revision table layout -->
<xsl:attribute-set name="revhistory.title.properties">
<xsl:attribute name="font-size">12pt</xsl:attribute>
<xsl:attribute name="font-weight">bold</xsl:attribute>
<xsl:attribute name="text-align">center</xsl:attribute>
</xsl:attribute-set>
<xsl:attribute-set name="revhistory.table.properties">
<xsl:attribute name="break-before">page</xsl:attribute>
</xsl:attribute-set>
<xsl:attribute-set name="revhistory.table.cell.properties">
<xsl:attribute name="border-bottom">1px solid</xsl:attribute>
</xsl:attribute-set>
<!-- Table Of Contents (TOC) options -->
<!-- We only want 2 level of ToC depth -->
<xsl:param name="toc.section.depth" select="2"/>
<!-- titles left margin -->
<xsl:attribute-set name="section.title.properties">
<xsl:attribute name="start-indent"><xsl:value-of select="$body.start.indent"/></xsl:attribute>
</xsl:attribute-set>
<xsl:attribute-set name="list.item.spacing">
<xsl:attribute name="space-before.optimum">0em</xsl:attribute>
<xsl:attribute name="space-before.minimum">0em</xsl:attribute>
<xsl:attribute name="space-before.maximum">0.2em</xsl:attribute>
</xsl:attribute-set>
<!-- lists type -->
<xsl:template name="itemizedlist.label.markup">
<xsl:param name="itemsymbol" select="'square'"/>
<xsl:choose>
<xsl:when test="$itemsymbol='square'"><fo:inline font-family="Lato">&#x220f;</fo:inline></xsl:when>
</xsl:choose>
</xsl:template>
<xsl:template name="next.itemsymbol">
<xsl:param name="itemsymbol" select="'default'"/>
<xsl:choose>
<xsl:otherwise>square</xsl:otherwise>
</xsl:choose>
</xsl:template>
<!-- admonition -->
<xsl:param name="admon.graphics" select="1"></xsl:param>
<xsl:param name="admon.graphics.path">images/</xsl:param>
<xsl:param name="admon.graphics.extension">.png</xsl:param>
<xsl:attribute-set name="graphical.admonition.properties">
<xsl:attribute name="border-top">1px solid</xsl:attribute>
<xsl:attribute name="border-bottom">1px solid</xsl:attribute>
<xsl:attribute name="padding-top">0.5em</xsl:attribute>
<xsl:attribute name="padding-bottom">0.5em</xsl:attribute>
<xsl:attribute name="margin-left">2em</xsl:attribute>
</xsl:attribute-set>
<!-- grey boxes around code (screen, programlisting) -->
<xsl:param name="shade.verbatim" select="1"/>
<xsl:attribute-set name="shade.verbatim.style">
<xsl:attribute name="background-color">#E0E0E0</xsl:attribute>
<xsl:attribute name="border">thin #9F9F9F solid</xsl:attribute>
<xsl:attribute name="margin">0pt</xsl:attribute>
<xsl:attribute name="padding">0.5em</xsl:attribute>
<!-- prevent page breaks in screen and programlisting tags -->
<xsl:attribute name="keep-together.within-column">always</xsl:attribute>
</xsl:attribute-set>
<!-- breaking long lines in code (screen, programlisting) -->
<xsl:attribute-set name="monospace.verbatim.properties">
<xsl:attribute name="wrap-option">wrap</xsl:attribute>
</xsl:attribute-set>
<!-- don't show raw links in [ .. ] after a link -->
<xsl:param name="ulink.show" select="0"/>
<!-- blue underlined hyperlink -->
<xsl:attribute-set name="xref.properties">
<xsl:attribute name="color">blue</xsl:attribute>
<xsl:attribute name="text-decoration">underline</xsl:attribute>
</xsl:attribute-set>
<!-- copyright in range instead of seperated years -->
<xsl:param name="make.year.ranges" select="1" />
<!-- variablelist behavior (asciidoc's term:: lists) -->
<!-- <xsl:param name="variablelist.term.break.after" select="1" /> -->
</xsl:stylesheet>
<!-- vim: set shiftwidth=2 tabstop=2 expandtab: -->

View File

@ -165,6 +165,13 @@
<xsl:attribute name="text-decoration">underline</xsl:attribute> <xsl:attribute name="text-decoration">underline</xsl:attribute>
</xsl:attribute-set> </xsl:attribute-set>
<!-- strong emphasis in bold -->
<xsl:template match="emphasis[@role='strong']">
<fo:inline font-family="Lato" font-weight="normal">
<xsl:apply-templates/>
</fo:inline>
</xsl:template>
<!-- copyright in range instead of seperated years --> <!-- copyright in range instead of seperated years -->
<xsl:param name="make.year.ranges" select="1" /> <xsl:param name="make.year.ranges" select="1" />

View File

@ -1,7 +1,7 @@
<!-- TODO have the build system take care of this --> <!-- TODO have the build system take care of this -->
<releaseinfo>Version 2.2.9 - September 2014</releaseinfo> <releaseinfo>Version 2.2.15 - January 2015</releaseinfo>
<subtitle>for version 2.2.9</subtitle> <subtitle>for version 2.2.15</subtitle>
<date>2014-09-26</date> <date>2015-01-30</date>
<legalnotice> <legalnotice>
<para>Permission is granted to copy, distribute and/or modify this document under the terms of the GNU Free Documentation License, Version 1.2 or any later version published by the Free Software Foundation; with no Invariant Sections, no Front-Cover Texts, and no Back-Cover Texts. A copy of the license is included in the section entitled "GNU Free Documentation License".</para> <para>Permission is granted to copy, distribute and/or modify this document under the terms of the GNU Free Documentation License, Version 1.2 or any later version published by the Free Software Foundation; with no Invariant Sections, no Front-Cover Texts, and no Back-Cover Texts. A copy of the license is included in the section entitled "GNU Free Documentation License".</para>

View File

@ -6,13 +6,13 @@
Authors: Authors:
- Inverse inc. <info@inverse.ca> - 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 License: GFDL 1.2 or later. http://www.gnu.org/licenses/fdl.html
//// ////
// TODO have the build system take care of this // TODO have the build system take care of this
:release_version: 2.2.9 :release_version: 2.2.15
// vim: set syntax=asciidoc tabstop=2 shiftwidth=2 expandtab: // vim: set syntax=asciidoc tabstop=2 shiftwidth=2 expandtab:

View File

@ -6,8 +6,7 @@ include ../Version
ADDITIONAL_OBJCFLAGS += -fPIE ADDITIONAL_OBJCFLAGS += -fPIE
ADDITIONAL_INCLUDE_DIRS += ADDITIONAL_INCLUDE_DIRS +=
ADDITIONAL_LIB_DIRS += -L../SOPE/GDLContentStore/obj/ ADDITIONAL_LDFLAGS += -Wl,--no-as-needed -fPIE -pie -Wl,--rpath,$(SOGO_SYSLIBDIR)/sogo
ADDITIONAL_LDFLAGS += -Wl,--no-as-needed -fPIE -pie
SOGOD = sogod SOGOD = sogod
TOOL_NAME = $(SOGOD) TOOL_NAME = $(SOGOD)

View File

@ -9,10 +9,9 @@ ADDITIONAL_INCLUDE_DIRS += \
-I.. -I..
ADDITIONAL_LIB_DIRS += \ ADDITIONAL_LIB_DIRS += \
-L../SoObjects/SOGo/SOGo.framework \ -L../SoObjects/SOGo/SOGo.framework/sogo \
-L../SOPE/NGCards/$(GNUSTEP_OBJ_DIR)/ -L../SOPE/NGCards/$(GNUSTEP_OBJ_DIR)/ \
-L../SOPE/GDLContentStore/$(GNUSTEP_OBJ_DIR)/
SYSTEM_LIB_DIR += -L/usr/local/lib -L/usr/lib
$(SOGOD)_TOOL_LIBS += \ $(SOGOD)_TOOL_LIBS += \
-lSOGo \ -lSOGo \

View File

@ -245,7 +245,7 @@ static BOOL debugLeaks;
} }
else else
{ {
NSLog (@"No value specified for '%@'", *urlString); [self errorWithFormat: @"No value specified for '%@'", *urlString];
ok = NO; ok = NO;
} }
} }
@ -283,7 +283,7 @@ static BOOL debugLeaks;
{ {
id authenticator; id authenticator;
if (trustProxyAuthentication) if (trustProxyAuthentication && [[context request] headerForKey: @"x-webobjects-remote-user"])
authenticator = [SOGoProxyAuthenticator sharedSOGoProxyAuthenticator]; authenticator = [SOGoProxyAuthenticator sharedSOGoProxyAuthenticator];
else else
{ {
@ -441,7 +441,7 @@ static BOOL debugLeaks;
if (debugLeaks) if (debugLeaks)
{ {
if (debugOn) if (debugOn)
NSLog (@"allocated classes:\n%s", GSDebugAllocationList (YES)); [self logWithFormat: @"allocated classes:\n%s", GSDebugAllocationList (YES)];
else else
{ {
debugOn = YES; debugOn = YES;

View File

@ -1,15 +1,15 @@
/* /*
Copyright (C) 2004-2005 SKYRIX Software AG 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 the terms of the GNU Lesser General Public License as published by the
Free Software Foundation; either version 2, or (at your option) any Free Software Foundation; either version 2, or (at your option) any
later version. 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 WARRANTY; without even the implied warranty of MERCHANTABILITY or
FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
License for more details. License for more details.

127
NEWS
View File

@ -1,3 +1,126 @@
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)
-------------------
New features
- allow including or not freebusy info from subscribed calendars
- now possible to set an autosave timer for draft messages
- now possible to set alarms on event invitations (#76)
Enhancements
- updated CKEditor to version 4.4.6 and added the 'Source Area' plugin
- avoid testing for IMAP ANNOTATION when X-GUID is available (#3018)
- updated Czech, Dutch, Finnish, French, German, Polish and Spanish (Spain) translations
Bug fixes
- fixed for privacy and categories for EAS (#3022)
- correctly set MeetingStatus for EAS on iOS devices
- Ubuntu Lucid fixes for EAS
- fixed calendar reminders for future events (#3008)
- make sure all text parts are UTF-8 re-encoded for Outlook 2013 over EAS (#3003)
- fixed task description truncation affecting WP8 phones over EAS (#3028)
2.2.11a (2014-12-10)
--------------------
Bug fixes
- make sure all address books returned using EAS are GCS ones
2.2.11 (2014-12-09)
-------------------
New features
- sogo-tool can now be used to manage EAS metadata for all devices
Enhancements
- improved the SAML2 documentation
- radically reduced AES memory usage
Bug fixes
- now possible to specify the username attribute for SAML2 (SOGoSAML2LoginAttribute) (#2381)
- added support for IdP-initiated SAML2 logout (#2377)
- we now generate SAML2 metadata on the fly (#2378)
- we now handle correctly the SOGo logout when using SAML (#2376 and #2379)
- fixed freebusy lookups going off bounds for resources (#3010)
- fixed EAS clients moving mails between folders but disconnecting before receiving server's response (#2982)
2.2.10 (2014-11-21)
-------------------
Enhancements
- no longer leaking database passwords in the logs (#2953)
- added support for multiple calendars and address books over ActiveSync
- updated timezone information (#2968)
- updated Brazilian Portuguese, Czech, Dutch, Finnish, French, German, Hungarian, Polish,
Russian, Spanish (Argentina), and Spanish (Spain) translations
- updated CKEditor to version 4.4.5
Bug fixes
- fixed freebusy lookup with "Show time as busy" (#2930)
- don't escape <br>'s in a card's note field
- fixed folder's display name when subscribing to a folder
- fixed folder's display name when the active user subscribes another user to one of her/his folders
- fixed error with new user default sorting value for the mailer module (#2952)
- fixed ActiveSync PING command flooding the server (#2940)
- fixed many interop issues with Windows Phones over ActiveSync
- fixed automatic return receipts crash when not in the recepient list (#2965)
- fixed support for Sieve folder encoding parameter (#2622)
- fixed rename of subscribed addressbooks
- sanitize strings before escaping them when using EAS
- fixed handling of event invitations on iOS/EAS with no organizer (#2978)
- fixed corrupted png files (#2975)
- improved dramatically the BSON decoding speed
- added WindowSize support for GCS collections when using EAS
- fixed IMAP search with non-ASCII folder names
- fixed extraction of email addresses when pasting text with tabs (#2945)
- fixed Outlook attachment corruption issues when using AES (#2957)
2.2.9a (2014-09-29) 2.2.9a (2014-09-29)
------------------- -------------------
@ -11,7 +134,7 @@ New features
- support for recurrent tasks (#2160) - support for recurrent tasks (#2160)
- support for alarms on recurrent events / tasks - support for alarms on recurrent events / tasks
Enchancements Enhancements
- alarms can now be snoozed for 1 day - alarms can now be snoozed for 1 day
- better iOS/Mac OS X Calendar compability regarding alarms (#1920) - better iOS/Mac OS X Calendar compability regarding alarms (#1920)
- force default classification over CalDAV if none is set (#2326) - force default classification over CalDAV if none is set (#2326)
@ -36,7 +159,7 @@ New features
- new user settings for threads collapsing - new user settings for threads collapsing
- IMAP global search support (#2670) - IMAP global search support (#2670)
Enchancements Enhancements
- major refactoring of the GCS component saving code (dropped OGoContentStore) - major refactoring of the GCS component saving code (dropped OGoContentStore)
- printing calendars in colors is now possible in all views; list, daily, weekly and multicolumns - printing calendars in colors is now possible in all views; list, daily, weekly and multicolumns
- new option to print calendars events and tasks with a background color or with a border color - new option to print calendars events and tasks with a background color or with a border color

View File

@ -141,7 +141,7 @@ $(DBMSGREADER_TOOL)_OBJC_FILES += \
NSObject+PropertyList.m NSObject+PropertyList.m
$(DBMSGREADER_TOOL)_LIB_DIRS += \ $(DBMSGREADER_TOOL)_LIB_DIRS += \
-L../SoObjects/SOGo/SOGo.framework/ -lSOGo \ -L../SoObjects/SOGo/SOGo.framework/sogo -lSOGo \
-L../SOPE/GDLContentStore/obj/ -lGDLContentStore \ -L../SOPE/GDLContentStore/obj/ -lGDLContentStore \
-L../SOPE/NGCards/obj/ -lNGCards \ -L../SOPE/NGCards/obj/ -lNGCards \
-lNGObjWeb -lNGObjWeb
@ -165,12 +165,12 @@ LIBMAPISTORE_LIBS = $(shell pkg-config libmapistore --libs) -lmapiproxy -lWEExte
$(MAPISTORESOGO)_INSTALL_DIR = $(DESTDIR)/$(SAMBA_LIB_DIR)/mapistore_backends $(MAPISTORESOGO)_INSTALL_DIR = $(DESTDIR)/$(SAMBA_LIB_DIR)/mapistore_backends
$(MAPISTORESOGO)_LIB_DIRS += \ $(MAPISTORESOGO)_LIB_DIRS += \
-L../SoObjects/SOGo/SOGo.framework/ -lSOGo -lgnustep-base -lobjc -lNGObjWeb \ -L../SoObjects/SOGo/SOGo.framework/sogo/ -lSOGo -lgnustep-base -lobjc -lNGObjWeb \
$(LIBMAPI_LIBS) \ $(LIBMAPI_LIBS) \
$(LIBMAPISTORE_LIBS) $(LIBMAPISTORE_LIBS)
$(SOGOBACKEND)_LIB_DIRS += \ $(SOGOBACKEND)_LIB_DIRS += \
-L../SoObjects/SOGo/SOGo.framework/ -lSOGo \ -L../SoObjects/SOGo/SOGo.framework/sogo/ -lSOGo \
$(LIBMAPI_LIBS) \ $(LIBMAPI_LIBS) \
$(LIBMAPISTORE_LIBS) $(LIBMAPISTORE_LIBS)
@ -183,6 +183,8 @@ ADDITIONAL_INCLUDE_DIRS += \
-DBACKEND_BUNDLE_NAME="@\"$(BUNDLE_NAME)$(BUNDLE_EXTENSION)\"" \ -DBACKEND_BUNDLE_NAME="@\"$(BUNDLE_NAME)$(BUNDLE_EXTENSION)\"" \
-DSOGO_BUNDLES_DIR="@\"$(BUNDLE_INSTALL_DIR)\"" -DSOGO_BUNDLES_DIR="@\"$(BUNDLE_INSTALL_DIR)\""
ADDITIONAL_LDFLAGS += -Wl,--rpath,$(SOGO_SYSLIBDIR)/sogo
-include GNUmakefile.preamble -include GNUmakefile.preamble
include $(GNUSTEP_MAKEFILES)/bundle.make include $(GNUSTEP_MAKEFILES)/bundle.make
include $(GNUSTEP_MAKEFILES)/library.make include $(GNUSTEP_MAKEFILES)/library.make

View File

@ -276,7 +276,7 @@ static NSTimeInterval ChannelCollectionTimer = 5 * 60;
EOAdaptorChannel *channel; EOAdaptorChannel *channel;
GCSChannelHandle *handle; GCSChannelHandle *handle;
NSCalendarDate *now, *lastFailure; NSCalendarDate *now, *lastFailure;
NSString *urlId; NSString *urlId, *url;
channel = nil; channel = nil;
urlId = [_url gcsURLId]; urlId = [_url gcsURLId];
@ -304,10 +304,10 @@ static NSTimeInterval ChannelCollectionTimer = 5 * 60;
} }
else else
{ {
url = [NSString stringWithFormat: @"%@://%@%@", [_url scheme], [_url host], [_url path]];
if (debugPools) if (debugPools)
{ {
[self logWithFormat: @"DBPOOL: create new DB channel for URL: %@", [self logWithFormat: @"DBPOOL: create new DB channel for %@", url];
[_url absoluteString]];
} }
/* create channel */ /* create channel */
@ -330,15 +330,13 @@ static NSTimeInterval ChannelCollectionTimer = 5 * 60;
if (lastFailure) if (lastFailure)
{ {
[self logWithFormat: @"db for %@ is now back up", [self logWithFormat: @"db for %@ is now back up", url];
[_url absoluteString]];
[lastFailures removeObjectForKey: urlId]; [lastFailures removeObjectForKey: urlId];
} }
} }
else else
{ {
[self errorWithFormat: @"could not open channel %@ for URL: %@", [self errorWithFormat: @"could not open channel %@ for %@", channel, url];
channel, [_url absoluteString]];
channel = nil; channel = nil;
[lastFailures setObject: now forKey: urlId]; [lastFailures setObject: now forKey: urlId];
[self warnWithFormat: @" will prevent opening of this" [self warnWithFormat: @" will prevent opening of this"

View File

@ -20,6 +20,7 @@
*/ */
#import <Foundation/NSDictionary.h> #import <Foundation/NSDictionary.h>
#import <Foundation/NSKeyValueCoding.h>
#import <Foundation/NSSet.h> #import <Foundation/NSSet.h>
#import <Foundation/NSString.h> #import <Foundation/NSString.h>
@ -147,7 +148,7 @@
if ([keys count] == 0) if ([keys count] == 0)
return folderQualifier; return folderQualifier;
bindings = [_folder valuesForKeys:keys]; bindings = [_folder dictionaryWithValuesForKeys:keys];
return [folderQualifier qualifierWithBindings:bindings return [folderQualifier qualifierWithBindings:bindings
requiresAllVariables:NO]; requiresAllVariables:NO];
} }

View File

@ -74,6 +74,7 @@ ifneq ($(frameworks),yes)
ifneq ($(FHS_INSTALL_ROOT),) ifneq ($(FHS_INSTALL_ROOT),)
GNUSTEP_HEADERS=$(DESTDIR)$(FHS_INSTALL_ROOT)/include GNUSTEP_HEADERS=$(DESTDIR)$(FHS_INSTALL_ROOT)/include
endif endif
GNUSTEP_TARGET_LDIR=sogo
include $(GNUSTEP_MAKEFILES)/library.make include $(GNUSTEP_MAKEFILES)/library.make
else else
include $(GNUSTEP_MAKEFILES)/framework.make include $(GNUSTEP_MAKEFILES)/framework.make

View File

@ -59,8 +59,10 @@ static NGCardsSaxHandler *sax = nil;
[parser setErrorHandler:sax]; [parser setErrorHandler:sax];
} }
else 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; return parser;
@ -170,8 +172,8 @@ static NGCardsSaxHandler *sax = nil;
{ {
if (![aChild isKindOfClass: mappedClass]) if (![aChild isKindOfClass: mappedClass])
{ {
NSLog (@"warning: new child to entity '%@': '%@' converted to '%@'", //NSLog (@"warning: new child to entity '%@': '%@' converted to '%@'",
tag, childTag, NSStringFromClass(mappedClass)); // tag, childTag, NSStringFromClass(mappedClass));
newChild = [aChild elementWithClass: mappedClass]; newChild = [aChild elementWithClass: mappedClass];
} }
} }

View File

@ -131,6 +131,7 @@ ifneq ($(frameworks),yes)
ifneq ($(FHS_INSTALL_ROOT),) ifneq ($(FHS_INSTALL_ROOT),)
GNUSTEP_HEADERS=$(DESTDIR)$(FHS_INSTALL_ROOT)/include GNUSTEP_HEADERS=$(DESTDIR)$(FHS_INSTALL_ROOT)/include
endif endif
GNUSTEP_TARGET_LDIR=sogo
include $(GNUSTEP_MAKEFILES)/library.make include $(GNUSTEP_MAKEFILES)/library.make
else else
include $(GNUSTEP_MAKEFILES)/framework.make include $(GNUSTEP_MAKEFILES)/framework.make

View File

@ -54,7 +54,7 @@
- (BOOL)appendLine:(NSString *)_line { - (BOOL)appendLine:(NSString *)_line {
if (self->isFinished) { if (self->isFinished) {
NSLog(@"WARNING[%s]: already finished!", __PRETTY_FUNCTION__); //NSLog(@"WARNING[%s]: already finished!", __PRETTY_FUNCTION__);
return NO; return NO;
} }
// limit length to 75 chars // limit length to 75 chars

View File

@ -77,8 +77,8 @@ static NSString *gmtcalfmt = @"%Y%m%dT%H%M%SZ";
return [self icalStringInGMT]; return [self icalStringInGMT];
else { else {
/* not in GMT */ /* not in GMT */
NSLog(@"WARNING(%s): arbitary timezones not supported yet: %@", //NSLog(@"WARNING(%s): arbitary timezones not supported yet: %@",
__PRETTY_FUNCTION__, _tz); // __PRETTY_FUNCTION__, _tz);
return [self icalStringInGMT]; return [self icalStringInGMT];
} }
} }

View File

@ -276,7 +276,9 @@
} }
} }
else else
NSLog(@"Cannot parse iCal duration value: '%@'", self); {
//NSLog(@"Cannot parse iCal duration value: '%@'", self);
}
if (isNegative) if (isNegative)
ti = -ti; ti = -ti;

View File

@ -1,5 +1,5 @@
BEGIN:VCALENDAR BEGIN:VCALENDAR
PRODID:-//Inverse inc.//NONSGML Olson 2012j//EN PRODID:-//Inverse inc.//NONSGML Olson 2014g//EN
VERSION:2.0 VERSION:2.0
BEGIN:VTIMEZONE BEGIN:VTIMEZONE
TZID:Africa/Abidjan TZID:Africa/Abidjan

View File

@ -1,5 +1,5 @@
BEGIN:VCALENDAR BEGIN:VCALENDAR
PRODID:-//Inverse inc.//NONSGML Olson 2012j//EN PRODID:-//Inverse inc.//NONSGML Olson 2014g//EN
VERSION:2.0 VERSION:2.0
BEGIN:VTIMEZONE BEGIN:VTIMEZONE
TZID:Africa/Accra TZID:Africa/Accra

View File

@ -1,5 +1,5 @@
BEGIN:VCALENDAR BEGIN:VCALENDAR
PRODID:-//Inverse inc.//NONSGML Olson 2012j//EN PRODID:-//Inverse inc.//NONSGML Olson 2014g//EN
VERSION:2.0 VERSION:2.0
BEGIN:VTIMEZONE BEGIN:VTIMEZONE
TZID:Africa/Addis_Ababa TZID:Africa/Addis_Ababa

View File

@ -1,5 +1,5 @@
BEGIN:VCALENDAR BEGIN:VCALENDAR
PRODID:-//Inverse inc.//NONSGML Olson 2012j//EN PRODID:-//Inverse inc.//NONSGML Olson 2014g//EN
VERSION:2.0 VERSION:2.0
BEGIN:VTIMEZONE BEGIN:VTIMEZONE
TZID:Africa/Algiers TZID:Africa/Algiers

View File

@ -1,5 +1,5 @@
BEGIN:VCALENDAR BEGIN:VCALENDAR
PRODID:-//Inverse inc.//NONSGML Olson 2012j//EN PRODID:-//Inverse inc.//NONSGML Olson 2014g//EN
VERSION:2.0 VERSION:2.0
BEGIN:VTIMEZONE BEGIN:VTIMEZONE
TZID:Africa/Asmara TZID:Africa/Asmara

View File

@ -1,5 +1,5 @@
BEGIN:VCALENDAR BEGIN:VCALENDAR
PRODID:-//Inverse inc.//NONSGML Olson 2012j//EN PRODID:-//Inverse inc.//NONSGML Olson 2014g//EN
VERSION:2.0 VERSION:2.0
BEGIN:VTIMEZONE BEGIN:VTIMEZONE
TZID:Africa/Bamako TZID:Africa/Bamako

View File

@ -1,5 +1,5 @@
BEGIN:VCALENDAR BEGIN:VCALENDAR
PRODID:-//Inverse inc.//NONSGML Olson 2012j//EN PRODID:-//Inverse inc.//NONSGML Olson 2014g//EN
VERSION:2.0 VERSION:2.0
BEGIN:VTIMEZONE BEGIN:VTIMEZONE
TZID:Africa/Bangui TZID:Africa/Bangui

View File

@ -1,5 +1,5 @@
BEGIN:VCALENDAR BEGIN:VCALENDAR
PRODID:-//Inverse inc.//NONSGML Olson 2012j//EN PRODID:-//Inverse inc.//NONSGML Olson 2014g//EN
VERSION:2.0 VERSION:2.0
BEGIN:VTIMEZONE BEGIN:VTIMEZONE
TZID:Africa/Banjul TZID:Africa/Banjul

View File

@ -1,5 +1,5 @@
BEGIN:VCALENDAR BEGIN:VCALENDAR
PRODID:-//Inverse inc.//NONSGML Olson 2012j//EN PRODID:-//Inverse inc.//NONSGML Olson 2014g//EN
VERSION:2.0 VERSION:2.0
BEGIN:VTIMEZONE BEGIN:VTIMEZONE
TZID:Africa/Bissau TZID:Africa/Bissau

View File

@ -1,5 +1,5 @@
BEGIN:VCALENDAR BEGIN:VCALENDAR
PRODID:-//Inverse inc.//NONSGML Olson 2012j//EN PRODID:-//Inverse inc.//NONSGML Olson 2014g//EN
VERSION:2.0 VERSION:2.0
BEGIN:VTIMEZONE BEGIN:VTIMEZONE
TZID:Africa/Blantyre TZID:Africa/Blantyre

View File

@ -1,5 +1,5 @@
BEGIN:VCALENDAR BEGIN:VCALENDAR
PRODID:-//Inverse inc.//NONSGML Olson 2012j//EN PRODID:-//Inverse inc.//NONSGML Olson 2014g//EN
VERSION:2.0 VERSION:2.0
BEGIN:VTIMEZONE BEGIN:VTIMEZONE
TZID:Africa/Brazzaville TZID:Africa/Brazzaville

View File

@ -1,5 +1,5 @@
BEGIN:VCALENDAR BEGIN:VCALENDAR
PRODID:-//Inverse inc.//NONSGML Olson 2012j//EN PRODID:-//Inverse inc.//NONSGML Olson 2014g//EN
VERSION:2.0 VERSION:2.0
BEGIN:VTIMEZONE BEGIN:VTIMEZONE
TZID:Africa/Bujumbura TZID:Africa/Bujumbura

View File

@ -1,14 +1,22 @@
BEGIN:VCALENDAR BEGIN:VCALENDAR
PRODID:-//Inverse inc.//NONSGML Olson 2012j//EN PRODID:-//Inverse inc.//NONSGML Olson 2014g//EN
VERSION:2.0 VERSION:2.0
BEGIN:VTIMEZONE BEGIN:VTIMEZONE
TZID:Africa/Cairo TZID:Africa/Cairo
X-LIC-LOCATION:Africa/Cairo X-LIC-LOCATION:Africa/Cairo
BEGIN:STANDARD BEGIN:STANDARD
TZOFFSETFROM:+0200 TZOFFSETFROM:+0300
TZOFFSETTO:+0200 TZOFFSETTO:+0200
TZNAME:EET TZNAME:EET
DTSTART:19700101T000000 DTSTART:19700924T235959
RRULE:FREQ=YEARLY;BYMONTH=9;BYDAY=-1TH
END:STANDARD END:STANDARD
BEGIN:DAYLIGHT
TZOFFSETFROM:+0300
TZOFFSETTO:+0300
TZNAME:EEST
DTSTART:19700424T010000
RRULE:FREQ=YEARLY;BYMONTH=4;BYDAY=-1FR
END:DAYLIGHT
END:VTIMEZONE END:VTIMEZONE
END:VCALENDAR END:VCALENDAR

View File

@ -1,22 +1,22 @@
BEGIN:VCALENDAR BEGIN:VCALENDAR
PRODID:-//Inverse inc.//NONSGML Olson 2012j//EN PRODID:-//Inverse inc.//NONSGML Olson 2014g//EN
VERSION:2.0 VERSION:2.0
BEGIN:VTIMEZONE BEGIN:VTIMEZONE
TZID:Africa/Casablanca TZID:Africa/Casablanca
X-LIC-LOCATION:Africa/Casablanca X-LIC-LOCATION:Africa/Casablanca
BEGIN:DAYLIGHT
TZOFFSETFROM:+0000
TZOFFSETTO:+0100
TZNAME:WEST
DTSTART:19700426T020000
RRULE:FREQ=YEARLY;BYMONTH=4;BYDAY=-1SU
END:DAYLIGHT
BEGIN:STANDARD BEGIN:STANDARD
TZOFFSETFROM:+0100 TZOFFSETFROM:+0100
TZOFFSETTO:+0000 TZOFFSETTO:+0000
TZNAME:WET TZNAME:WET
DTSTART:19700927T030000 DTSTART:19701025T030000
RRULE:FREQ=YEARLY;BYMONTH=9;BYDAY=-1SU RRULE:FREQ=YEARLY;BYMONTH=10;BYDAY=-1SU
END:STANDARD END:STANDARD
BEGIN:DAYLIGHT
TZOFFSETFROM:+0100
TZOFFSETTO:+0100
TZNAME:WEST
DTSTART:19700329T020000
RRULE:FREQ=YEARLY;BYMONTH=3;BYDAY=-1SU
END:DAYLIGHT
END:VTIMEZONE END:VTIMEZONE
END:VCALENDAR END:VCALENDAR

View File

@ -1,5 +1,5 @@
BEGIN:VCALENDAR BEGIN:VCALENDAR
PRODID:-//Inverse inc.//NONSGML Olson 2012j//EN PRODID:-//Inverse inc.//NONSGML Olson 2014g//EN
VERSION:2.0 VERSION:2.0
BEGIN:VTIMEZONE BEGIN:VTIMEZONE
TZID:Africa/Ceuta TZID:Africa/Ceuta

View File

@ -1,5 +1,5 @@
BEGIN:VCALENDAR BEGIN:VCALENDAR
PRODID:-//Inverse inc.//NONSGML Olson 2012j//EN PRODID:-//Inverse inc.//NONSGML Olson 2014g//EN
VERSION:2.0 VERSION:2.0
BEGIN:VTIMEZONE BEGIN:VTIMEZONE
TZID:Africa/Conakry TZID:Africa/Conakry

View File

@ -1,5 +1,5 @@
BEGIN:VCALENDAR BEGIN:VCALENDAR
PRODID:-//Inverse inc.//NONSGML Olson 2012j//EN PRODID:-//Inverse inc.//NONSGML Olson 2014g//EN
VERSION:2.0 VERSION:2.0
BEGIN:VTIMEZONE BEGIN:VTIMEZONE
TZID:Africa/Dakar TZID:Africa/Dakar

View File

@ -1,5 +1,5 @@
BEGIN:VCALENDAR BEGIN:VCALENDAR
PRODID:-//Inverse inc.//NONSGML Olson 2012j//EN PRODID:-//Inverse inc.//NONSGML Olson 2014g//EN
VERSION:2.0 VERSION:2.0
BEGIN:VTIMEZONE BEGIN:VTIMEZONE
TZID:Africa/Dar_es_Salaam TZID:Africa/Dar_es_Salaam

View File

@ -1,5 +1,5 @@
BEGIN:VCALENDAR BEGIN:VCALENDAR
PRODID:-//Inverse inc.//NONSGML Olson 2012j//EN PRODID:-//Inverse inc.//NONSGML Olson 2014g//EN
VERSION:2.0 VERSION:2.0
BEGIN:VTIMEZONE BEGIN:VTIMEZONE
TZID:Africa/Djibouti TZID:Africa/Djibouti

View File

@ -1,5 +1,5 @@
BEGIN:VCALENDAR BEGIN:VCALENDAR
PRODID:-//Inverse inc.//NONSGML Olson 2012j//EN PRODID:-//Inverse inc.//NONSGML Olson 2014g//EN
VERSION:2.0 VERSION:2.0
BEGIN:VTIMEZONE BEGIN:VTIMEZONE
TZID:Africa/Douala TZID:Africa/Douala

View File

@ -1,14 +1,22 @@
BEGIN:VCALENDAR BEGIN:VCALENDAR
PRODID:-//Inverse inc.//NONSGML Olson 2012j//EN PRODID:-//Inverse inc.//NONSGML Olson 2014g//EN
VERSION:2.0 VERSION:2.0
BEGIN:VTIMEZONE BEGIN:VTIMEZONE
TZID:Africa/El_Aaiun TZID:Africa/El_Aaiun
X-LIC-LOCATION:Africa/El_Aaiun X-LIC-LOCATION:Africa/El_Aaiun
BEGIN:STANDARD BEGIN:STANDARD
TZOFFSETFROM:+0000 TZOFFSETFROM:+0100
TZOFFSETTO:+0000 TZOFFSETTO:+0000
TZNAME:WET TZNAME:WET
DTSTART:19700101T000000 DTSTART:19701025T030000
RRULE:FREQ=YEARLY;BYMONTH=10;BYDAY=-1SU
END:STANDARD END:STANDARD
BEGIN:DAYLIGHT
TZOFFSETFROM:+0100
TZOFFSETTO:+0100
TZNAME:WEST
DTSTART:19700329T020000
RRULE:FREQ=YEARLY;BYMONTH=3;BYDAY=-1SU
END:DAYLIGHT
END:VTIMEZONE END:VTIMEZONE
END:VCALENDAR END:VCALENDAR

View File

@ -1,5 +1,5 @@
BEGIN:VCALENDAR BEGIN:VCALENDAR
PRODID:-//Inverse inc.//NONSGML Olson 2012j//EN PRODID:-//Inverse inc.//NONSGML Olson 2014g//EN
VERSION:2.0 VERSION:2.0
BEGIN:VTIMEZONE BEGIN:VTIMEZONE
TZID:Africa/Freetown TZID:Africa/Freetown

View File

@ -1,5 +1,5 @@
BEGIN:VCALENDAR BEGIN:VCALENDAR
PRODID:-//Inverse inc.//NONSGML Olson 2012j//EN PRODID:-//Inverse inc.//NONSGML Olson 2014g//EN
VERSION:2.0 VERSION:2.0
BEGIN:VTIMEZONE BEGIN:VTIMEZONE
TZID:Africa/Gaborone TZID:Africa/Gaborone

View File

@ -1,5 +1,5 @@
BEGIN:VCALENDAR BEGIN:VCALENDAR
PRODID:-//Inverse inc.//NONSGML Olson 2012j//EN PRODID:-//Inverse inc.//NONSGML Olson 2014g//EN
VERSION:2.0 VERSION:2.0
BEGIN:VTIMEZONE BEGIN:VTIMEZONE
TZID:Africa/Harare TZID:Africa/Harare

View File

@ -1,5 +1,5 @@
BEGIN:VCALENDAR BEGIN:VCALENDAR
PRODID:-//Inverse inc.//NONSGML Olson 2012j//EN PRODID:-//Inverse inc.//NONSGML Olson 2014g//EN
VERSION:2.0 VERSION:2.0
BEGIN:VTIMEZONE BEGIN:VTIMEZONE
TZID:Africa/Johannesburg TZID:Africa/Johannesburg

View File

@ -1,5 +1,5 @@
BEGIN:VCALENDAR BEGIN:VCALENDAR
PRODID:-//Inverse inc.//NONSGML Olson 2012j//EN PRODID:-//Inverse inc.//NONSGML Olson 2014g//EN
VERSION:2.0 VERSION:2.0
BEGIN:VTIMEZONE BEGIN:VTIMEZONE
TZID:Africa/Juba TZID:Africa/Juba

View File

@ -1,5 +1,5 @@
BEGIN:VCALENDAR BEGIN:VCALENDAR
PRODID:-//Inverse inc.//NONSGML Olson 2012j//EN PRODID:-//Inverse inc.//NONSGML Olson 2014g//EN
VERSION:2.0 VERSION:2.0
BEGIN:VTIMEZONE BEGIN:VTIMEZONE
TZID:Africa/Kampala TZID:Africa/Kampala

View File

@ -1,5 +1,5 @@
BEGIN:VCALENDAR BEGIN:VCALENDAR
PRODID:-//Inverse inc.//NONSGML Olson 2012j//EN PRODID:-//Inverse inc.//NONSGML Olson 2014g//EN
VERSION:2.0 VERSION:2.0
BEGIN:VTIMEZONE BEGIN:VTIMEZONE
TZID:Africa/Khartoum TZID:Africa/Khartoum

View File

@ -1,5 +1,5 @@
BEGIN:VCALENDAR BEGIN:VCALENDAR
PRODID:-//Inverse inc.//NONSGML Olson 2012j//EN PRODID:-//Inverse inc.//NONSGML Olson 2014g//EN
VERSION:2.0 VERSION:2.0
BEGIN:VTIMEZONE BEGIN:VTIMEZONE
TZID:Africa/Kigali TZID:Africa/Kigali

View File

@ -1,5 +1,5 @@
BEGIN:VCALENDAR BEGIN:VCALENDAR
PRODID:-//Inverse inc.//NONSGML Olson 2012j//EN PRODID:-//Inverse inc.//NONSGML Olson 2014g//EN
VERSION:2.0 VERSION:2.0
BEGIN:VTIMEZONE BEGIN:VTIMEZONE
TZID:Africa/Kinshasa TZID:Africa/Kinshasa

View File

@ -1,5 +1,5 @@
BEGIN:VCALENDAR BEGIN:VCALENDAR
PRODID:-//Inverse inc.//NONSGML Olson 2012j//EN PRODID:-//Inverse inc.//NONSGML Olson 2014g//EN
VERSION:2.0 VERSION:2.0
BEGIN:VTIMEZONE BEGIN:VTIMEZONE
TZID:Africa/Lagos TZID:Africa/Lagos

View File

@ -1,5 +1,5 @@
BEGIN:VCALENDAR BEGIN:VCALENDAR
PRODID:-//Inverse inc.//NONSGML Olson 2012j//EN PRODID:-//Inverse inc.//NONSGML Olson 2014g//EN
VERSION:2.0 VERSION:2.0
BEGIN:VTIMEZONE BEGIN:VTIMEZONE
TZID:Africa/Libreville TZID:Africa/Libreville

View File

@ -1,5 +1,5 @@
BEGIN:VCALENDAR BEGIN:VCALENDAR
PRODID:-//Inverse inc.//NONSGML Olson 2012j//EN PRODID:-//Inverse inc.//NONSGML Olson 2014g//EN
VERSION:2.0 VERSION:2.0
BEGIN:VTIMEZONE BEGIN:VTIMEZONE
TZID:Africa/Lome TZID:Africa/Lome

View File

@ -1,5 +1,5 @@
BEGIN:VCALENDAR BEGIN:VCALENDAR
PRODID:-//Inverse inc.//NONSGML Olson 2012j//EN PRODID:-//Inverse inc.//NONSGML Olson 2014g//EN
VERSION:2.0 VERSION:2.0
BEGIN:VTIMEZONE BEGIN:VTIMEZONE
TZID:Africa/Luanda TZID:Africa/Luanda

View File

@ -1,5 +1,5 @@
BEGIN:VCALENDAR BEGIN:VCALENDAR
PRODID:-//Inverse inc.//NONSGML Olson 2012j//EN PRODID:-//Inverse inc.//NONSGML Olson 2014g//EN
VERSION:2.0 VERSION:2.0
BEGIN:VTIMEZONE BEGIN:VTIMEZONE
TZID:Africa/Lubumbashi TZID:Africa/Lubumbashi

View File

@ -1,5 +1,5 @@
BEGIN:VCALENDAR BEGIN:VCALENDAR
PRODID:-//Inverse inc.//NONSGML Olson 2012j//EN PRODID:-//Inverse inc.//NONSGML Olson 2014g//EN
VERSION:2.0 VERSION:2.0
BEGIN:VTIMEZONE BEGIN:VTIMEZONE
TZID:Africa/Lusaka TZID:Africa/Lusaka

View File

@ -1,5 +1,5 @@
BEGIN:VCALENDAR BEGIN:VCALENDAR
PRODID:-//Inverse inc.//NONSGML Olson 2012j//EN PRODID:-//Inverse inc.//NONSGML Olson 2014g//EN
VERSION:2.0 VERSION:2.0
BEGIN:VTIMEZONE BEGIN:VTIMEZONE
TZID:Africa/Malabo TZID:Africa/Malabo

View File

@ -1,5 +1,5 @@
BEGIN:VCALENDAR BEGIN:VCALENDAR
PRODID:-//Inverse inc.//NONSGML Olson 2012j//EN PRODID:-//Inverse inc.//NONSGML Olson 2014g//EN
VERSION:2.0 VERSION:2.0
BEGIN:VTIMEZONE BEGIN:VTIMEZONE
TZID:Africa/Maputo TZID:Africa/Maputo

View File

@ -1,5 +1,5 @@
BEGIN:VCALENDAR BEGIN:VCALENDAR
PRODID:-//Inverse inc.//NONSGML Olson 2012j//EN PRODID:-//Inverse inc.//NONSGML Olson 2014g//EN
VERSION:2.0 VERSION:2.0
BEGIN:VTIMEZONE BEGIN:VTIMEZONE
TZID:Africa/Maseru TZID:Africa/Maseru

View File

@ -1,5 +1,5 @@
BEGIN:VCALENDAR BEGIN:VCALENDAR
PRODID:-//Inverse inc.//NONSGML Olson 2012j//EN PRODID:-//Inverse inc.//NONSGML Olson 2014g//EN
VERSION:2.0 VERSION:2.0
BEGIN:VTIMEZONE BEGIN:VTIMEZONE
TZID:Africa/Mbabane TZID:Africa/Mbabane

View File

@ -1,5 +1,5 @@
BEGIN:VCALENDAR BEGIN:VCALENDAR
PRODID:-//Inverse inc.//NONSGML Olson 2012j//EN PRODID:-//Inverse inc.//NONSGML Olson 2014g//EN
VERSION:2.0 VERSION:2.0
BEGIN:VTIMEZONE BEGIN:VTIMEZONE
TZID:Africa/Mogadishu TZID:Africa/Mogadishu

View File

@ -1,5 +1,5 @@
BEGIN:VCALENDAR BEGIN:VCALENDAR
PRODID:-//Inverse inc.//NONSGML Olson 2012j//EN PRODID:-//Inverse inc.//NONSGML Olson 2014g//EN
VERSION:2.0 VERSION:2.0
BEGIN:VTIMEZONE BEGIN:VTIMEZONE
TZID:Africa/Monrovia TZID:Africa/Monrovia

View File

@ -1,5 +1,5 @@
BEGIN:VCALENDAR BEGIN:VCALENDAR
PRODID:-//Inverse inc.//NONSGML Olson 2012j//EN PRODID:-//Inverse inc.//NONSGML Olson 2014g//EN
VERSION:2.0 VERSION:2.0
BEGIN:VTIMEZONE BEGIN:VTIMEZONE
TZID:Africa/Nairobi TZID:Africa/Nairobi

View File

@ -1,5 +1,5 @@
BEGIN:VCALENDAR BEGIN:VCALENDAR
PRODID:-//Inverse inc.//NONSGML Olson 2012j//EN PRODID:-//Inverse inc.//NONSGML Olson 2014g//EN
VERSION:2.0 VERSION:2.0
BEGIN:VTIMEZONE BEGIN:VTIMEZONE
TZID:Africa/Ndjamena TZID:Africa/Ndjamena

View File

@ -1,5 +1,5 @@
BEGIN:VCALENDAR BEGIN:VCALENDAR
PRODID:-//Inverse inc.//NONSGML Olson 2012j//EN PRODID:-//Inverse inc.//NONSGML Olson 2014g//EN
VERSION:2.0 VERSION:2.0
BEGIN:VTIMEZONE BEGIN:VTIMEZONE
TZID:Africa/Niamey TZID:Africa/Niamey

View File

@ -1,5 +1,5 @@
BEGIN:VCALENDAR BEGIN:VCALENDAR
PRODID:-//Inverse inc.//NONSGML Olson 2012j//EN PRODID:-//Inverse inc.//NONSGML Olson 2014g//EN
VERSION:2.0 VERSION:2.0
BEGIN:VTIMEZONE BEGIN:VTIMEZONE
TZID:Africa/Nouakchott TZID:Africa/Nouakchott

View File

@ -1,5 +1,5 @@
BEGIN:VCALENDAR BEGIN:VCALENDAR
PRODID:-//Inverse inc.//NONSGML Olson 2012j//EN PRODID:-//Inverse inc.//NONSGML Olson 2014g//EN
VERSION:2.0 VERSION:2.0
BEGIN:VTIMEZONE BEGIN:VTIMEZONE
TZID:Africa/Ouagadougou TZID:Africa/Ouagadougou

View File

@ -1,5 +1,5 @@
BEGIN:VCALENDAR BEGIN:VCALENDAR
PRODID:-//Inverse inc.//NONSGML Olson 2012j//EN PRODID:-//Inverse inc.//NONSGML Olson 2014g//EN
VERSION:2.0 VERSION:2.0
BEGIN:VTIMEZONE BEGIN:VTIMEZONE
TZID:Africa/Porto-Novo TZID:Africa/Porto-Novo

View File

@ -1,5 +1,5 @@
BEGIN:VCALENDAR BEGIN:VCALENDAR
PRODID:-//Inverse inc.//NONSGML Olson 2012j//EN PRODID:-//Inverse inc.//NONSGML Olson 2014g//EN
VERSION:2.0 VERSION:2.0
BEGIN:VTIMEZONE BEGIN:VTIMEZONE
TZID:Africa/Sao_Tome TZID:Africa/Sao_Tome

View File

@ -1,22 +1,14 @@
BEGIN:VCALENDAR BEGIN:VCALENDAR
PRODID:-//Inverse inc.//NONSGML Olson 2012j//EN PRODID:-//Inverse inc.//NONSGML Olson 2014g//EN
VERSION:2.0 VERSION:2.0
BEGIN:VTIMEZONE BEGIN:VTIMEZONE
TZID:Africa/Tripoli TZID:Africa/Tripoli
X-LIC-LOCATION:Africa/Tripoli X-LIC-LOCATION:Africa/Tripoli
BEGIN:DAYLIGHT
TZOFFSETFROM:+0100
TZOFFSETTO:+0200
TZNAME:CEST
DTSTART:19700327T010000
RRULE:FREQ=YEARLY;BYMONTH=3;BYDAY=-1FR
END:DAYLIGHT
BEGIN:STANDARD BEGIN:STANDARD
TZOFFSETFROM:+0200 TZOFFSETFROM:+0200
TZOFFSETTO:+0100 TZOFFSETTO:+0200
TZNAME:CET TZNAME:EET
DTSTART:19701030T020000 DTSTART:19700101T000000
RRULE:FREQ=YEARLY;BYMONTH=10;BYDAY=-1FR
END:STANDARD END:STANDARD
END:VTIMEZONE END:VTIMEZONE
END:VCALENDAR END:VCALENDAR

View File

@ -1,5 +1,5 @@
BEGIN:VCALENDAR BEGIN:VCALENDAR
PRODID:-//Inverse inc.//NONSGML Olson 2012j//EN PRODID:-//Inverse inc.//NONSGML Olson 2014g//EN
VERSION:2.0 VERSION:2.0
BEGIN:VTIMEZONE BEGIN:VTIMEZONE
TZID:Africa/Tunis TZID:Africa/Tunis

View File

@ -1,5 +1,5 @@
BEGIN:VCALENDAR BEGIN:VCALENDAR
PRODID:-//Inverse inc.//NONSGML Olson 2012j//EN PRODID:-//Inverse inc.//NONSGML Olson 2014g//EN
VERSION:2.0 VERSION:2.0
BEGIN:VTIMEZONE BEGIN:VTIMEZONE
TZID:Africa/Windhoek TZID:Africa/Windhoek

View File

@ -1,5 +1,5 @@
BEGIN:VCALENDAR BEGIN:VCALENDAR
PRODID:-//Inverse inc.//NONSGML Olson 2012j//EN PRODID:-//Inverse inc.//NONSGML Olson 2014g//EN
VERSION:2.0 VERSION:2.0
BEGIN:VTIMEZONE BEGIN:VTIMEZONE
TZID:America/Adak TZID:America/Adak

View File

@ -1,5 +1,5 @@
BEGIN:VCALENDAR BEGIN:VCALENDAR
PRODID:-//Inverse inc.//NONSGML Olson 2012j//EN PRODID:-//Inverse inc.//NONSGML Olson 2014g//EN
VERSION:2.0 VERSION:2.0
BEGIN:VTIMEZONE BEGIN:VTIMEZONE
TZID:America/Anchorage TZID:America/Anchorage

View File

@ -1,5 +1,5 @@
BEGIN:VCALENDAR BEGIN:VCALENDAR
PRODID:-//Inverse inc.//NONSGML Olson 2012j//EN PRODID:-//Inverse inc.//NONSGML Olson 2014g//EN
VERSION:2.0 VERSION:2.0
BEGIN:VTIMEZONE BEGIN:VTIMEZONE
TZID:America/Anguilla TZID:America/Anguilla

View File

@ -1,5 +1,5 @@
BEGIN:VCALENDAR BEGIN:VCALENDAR
PRODID:-//Inverse inc.//NONSGML Olson 2012j//EN PRODID:-//Inverse inc.//NONSGML Olson 2014g//EN
VERSION:2.0 VERSION:2.0
BEGIN:VTIMEZONE BEGIN:VTIMEZONE
TZID:America/Antigua TZID:America/Antigua

View File

@ -1,22 +1,14 @@
BEGIN:VCALENDAR BEGIN:VCALENDAR
PRODID:-//Inverse inc.//NONSGML Olson 2012j//EN PRODID:-//Inverse inc.//NONSGML Olson 2014g//EN
VERSION:2.0 VERSION:2.0
BEGIN:VTIMEZONE BEGIN:VTIMEZONE
TZID:America/Araguaina TZID:America/Araguaina
X-LIC-LOCATION:America/Araguaina X-LIC-LOCATION:America/Araguaina
BEGIN:DAYLIGHT
TZOFFSETFROM:-0300
TZOFFSETTO:-0200
TZNAME:BRST
DTSTART:19701018T000000
RRULE:FREQ=YEARLY;BYMONTH=10;BYDAY=3SU
END:DAYLIGHT
BEGIN:STANDARD BEGIN:STANDARD
TZOFFSETFROM:-0300 TZOFFSETFROM:-0300
TZOFFSETTO:-0300 TZOFFSETTO:-0300
TZNAME:BRT TZNAME:BRT
DTSTART:19700215T000000 DTSTART:19700101T000000
RRULE:FREQ=YEARLY;BYMONTH=2;BYDAY=3SU
END:STANDARD END:STANDARD
END:VTIMEZONE END:VTIMEZONE
END:VCALENDAR END:VCALENDAR

View File

@ -1,5 +1,5 @@
BEGIN:VCALENDAR BEGIN:VCALENDAR
PRODID:-//Inverse inc.//NONSGML Olson 2012j//EN PRODID:-//Inverse inc.//NONSGML Olson 2014g//EN
VERSION:2.0 VERSION:2.0
BEGIN:VTIMEZONE BEGIN:VTIMEZONE
TZID:America/Argentina/Buenos_Aires TZID:America/Argentina/Buenos_Aires

View File

@ -1,5 +1,5 @@
BEGIN:VCALENDAR BEGIN:VCALENDAR
PRODID:-//Inverse inc.//NONSGML Olson 2012j//EN PRODID:-//Inverse inc.//NONSGML Olson 2014g//EN
VERSION:2.0 VERSION:2.0
BEGIN:VTIMEZONE BEGIN:VTIMEZONE
TZID:America/Argentina/Catamarca TZID:America/Argentina/Catamarca

View File

@ -1,5 +1,5 @@
BEGIN:VCALENDAR BEGIN:VCALENDAR
PRODID:-//Inverse inc.//NONSGML Olson 2012j//EN PRODID:-//Inverse inc.//NONSGML Olson 2014g//EN
VERSION:2.0 VERSION:2.0
BEGIN:VTIMEZONE BEGIN:VTIMEZONE
TZID:America/Argentina/Cordoba TZID:America/Argentina/Cordoba

View File

@ -1,5 +1,5 @@
BEGIN:VCALENDAR BEGIN:VCALENDAR
PRODID:-//Inverse inc.//NONSGML Olson 2012j//EN PRODID:-//Inverse inc.//NONSGML Olson 2014g//EN
VERSION:2.0 VERSION:2.0
BEGIN:VTIMEZONE BEGIN:VTIMEZONE
TZID:America/Argentina/Jujuy TZID:America/Argentina/Jujuy

View File

@ -1,5 +1,5 @@
BEGIN:VCALENDAR BEGIN:VCALENDAR
PRODID:-//Inverse inc.//NONSGML Olson 2012j//EN PRODID:-//Inverse inc.//NONSGML Olson 2014g//EN
VERSION:2.0 VERSION:2.0
BEGIN:VTIMEZONE BEGIN:VTIMEZONE
TZID:America/Argentina/La_Rioja TZID:America/Argentina/La_Rioja

View File

@ -1,5 +1,5 @@
BEGIN:VCALENDAR BEGIN:VCALENDAR
PRODID:-//Inverse inc.//NONSGML Olson 2012j//EN PRODID:-//Inverse inc.//NONSGML Olson 2014g//EN
VERSION:2.0 VERSION:2.0
BEGIN:VTIMEZONE BEGIN:VTIMEZONE
TZID:America/Argentina/Mendoza TZID:America/Argentina/Mendoza

View File

@ -1,5 +1,5 @@
BEGIN:VCALENDAR BEGIN:VCALENDAR
PRODID:-//Inverse inc.//NONSGML Olson 2012j//EN PRODID:-//Inverse inc.//NONSGML Olson 2014g//EN
VERSION:2.0 VERSION:2.0
BEGIN:VTIMEZONE BEGIN:VTIMEZONE
TZID:America/Argentina/Rio_Gallegos TZID:America/Argentina/Rio_Gallegos

Some files were not shown because too many files have changed in this diff Show More