Merge branch 'master-upstream' into jjgarcia/merge-from-upstream
commit
12b159a6c0
|
@ -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]];
|
||||||
}
|
}
|
||||||
|
|
||||||
//
|
//
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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++)
|
||||||
{
|
{
|
||||||
|
|
|
@ -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
|
||||||
|
|
||||||
|
|
|
@ -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++)
|
||||||
{
|
{
|
||||||
|
@ -517,10 +591,10 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
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])
|
||||||
|
{
|
||||||
|
DESTROY(pool);
|
||||||
continue;
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
uid = [[component objectForKey: @"c_name"] sanitizedServerIdWithType: theFolderType];
|
uid = [[component objectForKey: @"c_name"] sanitizedServerIdWithType: theFolderType];
|
||||||
|
|
||||||
if (deleted)
|
if (deleted)
|
||||||
|
{
|
||||||
|
if ([syncCache objectForKey: uid])
|
||||||
{
|
{
|
||||||
[s appendString: @"<Delete xmlns=\"AirSync:\">"];
|
[s appendString: @"<Delete xmlns=\"AirSync:\">"];
|
||||||
[s appendFormat: @"<ServerId xmlns=\"AirSync:\">%@</ServerId>", uid];
|
[s appendFormat: @"<ServerId xmlns=\"AirSync:\">%@</ServerId>", uid];
|
||||||
[s appendString: @"</Delete>"];
|
[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)
|
||||||
|
{
|
||||||
|
DESTROY(pool);
|
||||||
continue;
|
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];
|
||||||
|
@ -966,6 +1084,19 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
|
||||||
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
|
||||||
|
{
|
||||||
|
// 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];
|
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,21 +1409,31 @@ 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);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Only send a response if there are changes otherwise send an empty response.
|
||||||
|
if (changeDetected)
|
||||||
|
{
|
||||||
// We always return the last generated response.
|
// We always return the last generated response.
|
||||||
// If we only return <Sync><Collections/></Sync>,
|
// If we only return <Sync><Collections/></Sync>,
|
||||||
// iOS powered devices will simply crash.
|
// iOS powered devices will simply crash.
|
||||||
|
@ -1247,9 +1441,14 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
|
||||||
[output appendString: @"</Collections></Sync>"];
|
[output appendString: @"</Collections></Sync>"];
|
||||||
|
|
||||||
d = [[output dataUsingEncoding: NSUTF8StringEncoding] xml2wbxml];
|
d = [output dataUsingEncoding: NSUTF8StringEncoding];
|
||||||
|
d = [d xml2wbxml];
|
||||||
[theResponse setContent: d];
|
[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
|
@ -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];
|
||||||
|
|
||||||
|
@ -743,8 +741,6 @@ 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])
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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"];
|
||||||
}
|
}
|
||||||
|
|
|
@ -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 \
|
||||||
|
|
|
@ -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,13 +231,26 @@ 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
|
||||||
if ([self hasAlarms])
|
if ([self hasAlarms])
|
||||||
|
@ -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];
|
||||||
|
@ -385,19 +408,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];
|
|
||||||
[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;
|
||||||
|
|
|
@ -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;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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,8 +97,24 @@ 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];
|
||||||
|
@ -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"]))
|
||||||
{
|
{
|
||||||
|
|
|
@ -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"
|
||||||
|
|
||||||
|
|
|
@ -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:
|
||||||
|
|
|
@ -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
|
||||||
---------
|
---------
|
||||||
|
|
|
@ -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,29 +453,46 @@ 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:
|
||||||
|
@ -441,7 +501,11 @@ On RHEL, make sure SELinux is disabled:
|
||||||
|
|
||||||
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
|
||||||
|
|
|
@ -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">∏</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: -->
|
|
|
@ -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" />
|
||||||
|
|
||||||
|
|
|
@ -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>
|
||||||
|
|
|
@ -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:
|
||||||
|
|
|
@ -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)
|
||||||
|
|
|
@ -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 \
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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
127
NEWS
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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"
|
||||||
|
|
|
@ -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];
|
||||||
}
|
}
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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
Loading…
Reference in New Issue