2014-01-10 20:12:53 +01:00
|
|
|
/*
|
|
|
|
|
|
|
|
Copyright (c) 2014, Inverse inc.
|
|
|
|
All rights reserved.
|
|
|
|
|
2014-01-13 17:46:32 +01:00
|
|
|
Redistribution and use in source and binary forms, with or without
|
|
|
|
modification, are permitted provided that the following conditions are met:
|
|
|
|
|
|
|
|
* Redistributions of source code must retain the above copyright
|
|
|
|
notice, this list of conditions and the following disclaimer.
|
|
|
|
* Redistributions in binary form must reproduce the above copyright
|
|
|
|
notice, this list of conditions and the following disclaimer in the
|
|
|
|
documentation and/or other materials provided with the distribution.
|
|
|
|
* Neither the name of the Inverse inc. nor the
|
|
|
|
names of its contributors may be used to endorse or promote products
|
|
|
|
derived from this software without specific prior written permission.
|
|
|
|
|
|
|
|
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
|
|
|
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
|
|
|
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
|
|
|
DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT HOLDER> BE LIABLE FOR ANY
|
|
|
|
DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
|
|
|
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
|
|
|
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
|
|
|
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
|
|
|
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
|
|
|
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
2014-01-10 20:12:53 +01:00
|
|
|
|
|
|
|
*/
|
|
|
|
#import "SOGoActiveSyncDispatcher+Sync.h"
|
|
|
|
|
|
|
|
|
|
|
|
#import <Foundation/NSArray.h>
|
|
|
|
#import <Foundation/NSCalendarDate.h>
|
|
|
|
#import <Foundation/NSProcessInfo.h>
|
|
|
|
#import <Foundation/NSTimeZone.h>
|
|
|
|
#import <Foundation/NSURL.h>
|
|
|
|
|
|
|
|
#import <NGObjWeb/NSException+HTTP.h>
|
|
|
|
#import <NGObjWeb/SoApplication.h>
|
|
|
|
#import <NGObjWeb/SoObject.h>
|
|
|
|
#import <NGObjWeb/WOContext.h>
|
|
|
|
#import <NGObjWeb/WOContext+SoObjects.h>
|
|
|
|
#import <NGObjWeb/WOCookie.h>
|
|
|
|
#import <NGObjWeb/WODirectAction.h>
|
|
|
|
#import <NGObjWeb/WORequest.h>
|
|
|
|
#import <NGObjWeb/WOResponse.h>
|
|
|
|
|
2014-02-03 16:24:33 +01:00
|
|
|
#import <NGCards/iCalCalendar.h>
|
2014-01-10 20:12:53 +01:00
|
|
|
#import <NGCards/iCalEntityObject.h>
|
|
|
|
#import <NGCards/iCalEvent.h>
|
|
|
|
#import <NGCards/iCalToDo.h>
|
|
|
|
#import <NGCards/NGVCard.h>
|
|
|
|
|
|
|
|
#import <NGExtensions/NSCalendarDate+misc.h>
|
|
|
|
#import <NGExtensions/NSString+misc.h>
|
|
|
|
|
|
|
|
#import <NGImap4/NSString+Imap4.h>
|
|
|
|
|
|
|
|
#import <NGMime/NGMimeBodyPart.h>
|
|
|
|
#import <NGMime/NGMimeMultipartBody.h>
|
|
|
|
#import <NGMail/NGMimeMessageParser.h>
|
|
|
|
#import <NGMail/NGMimeMessage.h>
|
|
|
|
#import <NGMail/NGMimeMessageGenerator.h>
|
|
|
|
|
|
|
|
#import <DOM/DOMElement.h>
|
|
|
|
#import <DOM/DOMProtocols.h>
|
|
|
|
|
|
|
|
#import <EOControl/EOQualifier.h>
|
|
|
|
|
|
|
|
#import <SOGo/NSArray+DAV.h>
|
|
|
|
#import <SOGo/NSDictionary+DAV.h>
|
|
|
|
#import <SOGo/SOGoDAVAuthenticator.h>
|
|
|
|
#import <SOGo/SOGoDomainDefaults.h>
|
|
|
|
#import <SOGo/SOGoMailer.h>
|
2014-02-11 02:16:43 +01:00
|
|
|
#import <SOGo/SOGoSystemDefaults.h>
|
2014-01-10 20:12:53 +01:00
|
|
|
#import <SOGo/SOGoUser.h>
|
|
|
|
#import <SOGo/SOGoUserSettings.h>
|
|
|
|
|
|
|
|
#import <Appointments/SOGoAppointmentObject.h>
|
|
|
|
#import <Appointments/SOGoAppointmentFolder.h>
|
|
|
|
#import <Appointments/SOGoAppointmentFolders.h>
|
|
|
|
#import <Appointments/SOGoTaskObject.h>
|
|
|
|
|
|
|
|
#import <Contacts/SOGoContactGCSEntry.h>
|
|
|
|
#import <Contacts/SOGoContactGCSFolder.h>
|
|
|
|
#import <Contacts/SOGoContactFolders.h>
|
|
|
|
#import <Contacts/SOGoContactSourceFolder.h>
|
|
|
|
|
|
|
|
#import <Mailer/SOGoMailAccount.h>
|
|
|
|
#import <Mailer/SOGoMailAccounts.h>
|
|
|
|
#import <Mailer/SOGoMailObject.h>
|
|
|
|
|
|
|
|
#import <Foundation/NSObject.h>
|
|
|
|
#import <Foundation/NSString.h>
|
|
|
|
|
|
|
|
#include "iCalEvent+ActiveSync.h"
|
|
|
|
#include "iCalToDo+ActiveSync.h"
|
|
|
|
#include "NGDOMElement+ActiveSync.h"
|
|
|
|
#include "NGVCard+ActiveSync.h"
|
2014-01-20 16:13:16 +01:00
|
|
|
#include "NSCalendarDate+ActiveSync.h"
|
2014-01-16 21:13:09 +01:00
|
|
|
#include "NSDate+ActiveSync.h"
|
2014-01-10 20:12:53 +01:00
|
|
|
#include "NSData+ActiveSync.h"
|
|
|
|
#include "NSString+ActiveSync.h"
|
|
|
|
#include "SOGoActiveSyncConstants.h"
|
|
|
|
#include "SOGoMailObject+ActiveSync.h"
|
|
|
|
|
2014-02-04 17:19:33 +01:00
|
|
|
#include <unistd.h>
|
|
|
|
|
2014-01-10 20:12:53 +01:00
|
|
|
@implementation SOGoActiveSyncDispatcher (Sync)
|
|
|
|
|
|
|
|
//
|
|
|
|
// <?xml version="1.0"?>
|
|
|
|
// <!DOCTYPE ActiveSync PUBLIC "-//MICROSOFT//DTD ActiveSync//EN" "http://www.microsoft.com/">
|
|
|
|
// <Sync xmlns="AirSync:">
|
|
|
|
// <Collections>
|
|
|
|
// <Collection>
|
|
|
|
// <SyncKey>1388757902</SyncKey>
|
|
|
|
// <CollectionId>vcard/personal</CollectionId>
|
|
|
|
// <GetChanges/>
|
|
|
|
// <WindowSize>25</WindowSize>
|
|
|
|
// <Options>
|
|
|
|
// <BodyPreference xmlns="AirSyncBase:">
|
|
|
|
// <Type>1</Type>
|
|
|
|
// <TruncationSize>32768</TruncationSize>
|
|
|
|
// </BodyPreference>
|
|
|
|
// </Options>
|
|
|
|
// <Commands>
|
|
|
|
// <Add>
|
|
|
|
// <ClientId>16</ClientId>
|
|
|
|
// <ApplicationData>
|
|
|
|
// <Body xmlns="AirSyncBase:">
|
|
|
|
// <Type>1</Type>
|
|
|
|
// <Data/>
|
|
|
|
// </Body>
|
|
|
|
// <CompanyName xmlns="Contacts:">Goo Inc.</CompanyName>
|
|
|
|
// <Email1Address xmlns="Contacts:">annie@broccoli.com</Email1Address>
|
|
|
|
// <FileAs xmlns="Contacts:">Broccoli, Annie</FileAs>
|
|
|
|
// <FirstName xmlns="Contacts:">Annie</FirstName>
|
|
|
|
// <LastName xmlns="Contacts:">Broccoli</LastName>
|
|
|
|
// <Picture xmlns="Contacts:"/>
|
|
|
|
// </ApplicationData>
|
|
|
|
// </Add>
|
|
|
|
// </Commands>
|
|
|
|
// </Collection>
|
|
|
|
// </Collections>
|
|
|
|
// </Sync>
|
|
|
|
//
|
|
|
|
- (void) processSyncAddCommand: (id <DOMElement>) theDocumentElement
|
|
|
|
inCollection: (id) theCollection
|
|
|
|
withType: (SOGoMicrosoftActiveSyncFolderType) theFolderType
|
|
|
|
inBuffer: (NSMutableString *) theBuffer
|
|
|
|
{
|
2014-01-10 22:48:39 +01:00
|
|
|
NSMutableDictionary *allValues;
|
2014-01-10 20:12:53 +01:00
|
|
|
NSString *clientId, *serverId;
|
2014-01-10 22:48:39 +01:00
|
|
|
NSArray *additions;
|
|
|
|
|
2014-01-10 20:12:53 +01:00
|
|
|
id anAddition, sogoObject, o;
|
2014-02-11 02:16:43 +01:00
|
|
|
BOOL is_new;
|
2014-01-10 20:12:53 +01:00
|
|
|
int i;
|
|
|
|
|
|
|
|
additions = (id)[theDocumentElement getElementsByTagName: @"Add"];
|
|
|
|
if ([additions count])
|
|
|
|
{
|
|
|
|
for (i = 0; i < [additions count]; i++)
|
|
|
|
{
|
|
|
|
anAddition = [additions objectAtIndex: i];
|
2014-02-11 02:16:43 +01:00
|
|
|
is_new = YES;
|
2014-01-10 20:12:53 +01:00
|
|
|
|
2014-01-10 21:29:57 +01:00
|
|
|
clientId = [[(id)[anAddition getElementsByTagName: @"ClientId"] lastObject] textValue];
|
2014-01-10 22:48:39 +01:00
|
|
|
allValues = [NSMutableDictionary dictionaryWithDictionary: [[(id)[anAddition getElementsByTagName: @"ApplicationData"] lastObject] applicationData]];
|
2014-01-10 20:12:53 +01:00
|
|
|
|
|
|
|
switch (theFolderType)
|
|
|
|
{
|
|
|
|
case ActiveSyncContactFolder:
|
|
|
|
{
|
|
|
|
serverId = [NSString stringWithFormat: @"%@.vcf", [theCollection globallyUniqueObjectId]];
|
|
|
|
sogoObject = [[SOGoContactGCSEntry alloc] initWithName: serverId
|
|
|
|
inContainer: theCollection];
|
|
|
|
o = [sogoObject vCard];
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case ActiveSyncEventFolder:
|
|
|
|
{
|
2014-02-11 02:16:43 +01:00
|
|
|
// Before adding a new appointment, we check if one is already present with the same UID. If that's
|
|
|
|
// the case, let's just update it. This can happen if for example, an iOS based device receives the
|
|
|
|
// invitation email and choses "Add to calendar" BEFORE actually syncing the calendar. That would
|
|
|
|
// create a duplicate on the server.
|
|
|
|
if ([allValues objectForKey: @"UID"])
|
|
|
|
serverId = [NSString stringWithFormat: @"%@.ics", [allValues objectForKey: @"UID"]];
|
|
|
|
else
|
|
|
|
serverId = [NSString stringWithFormat: @"%@.ics", [theCollection globallyUniqueObjectId]];
|
|
|
|
|
2014-01-10 22:48:39 +01:00
|
|
|
[allValues setObject: [[[context activeUser] userDefaults] timeZone] forKey: @"SOGoUserTimeZone"];
|
2014-02-11 02:16:43 +01:00
|
|
|
|
|
|
|
sogoObject = [theCollection lookupName: serverId
|
|
|
|
inContext: context
|
|
|
|
acquire: NO];
|
|
|
|
|
|
|
|
// If object isn't found, we 'create' a new one
|
|
|
|
if ([sogoObject isKindOfClass: [NSException class]])
|
|
|
|
{
|
|
|
|
sogoObject = [[SOGoAppointmentObject alloc] initWithName: serverId
|
|
|
|
inContainer: theCollection];
|
|
|
|
o = [sogoObject component: YES secure: NO];
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
o = [sogoObject component: NO secure: NO];
|
|
|
|
is_new = NO;
|
|
|
|
}
|
2014-01-10 20:12:53 +01:00
|
|
|
}
|
|
|
|
break;
|
|
|
|
case ActiveSyncTaskFolder:
|
|
|
|
{
|
|
|
|
serverId = [NSString stringWithFormat: @"%@.ics", [theCollection globallyUniqueObjectId]];
|
|
|
|
sogoObject = [[SOGoTaskObject alloc] initWithName: serverId
|
|
|
|
inContainer: theCollection];
|
2014-01-10 22:48:39 +01:00
|
|
|
[allValues setObject: [[[context activeUser] userDefaults] timeZone] forKey: @"SOGoUserTimeZone"];
|
2014-01-10 20:12:53 +01:00
|
|
|
o = [sogoObject component: YES secure: NO];
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case ActiveSyncMailFolder:
|
|
|
|
default:
|
|
|
|
{
|
|
|
|
// FIXME
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
[o takeActiveSyncValues: allValues];
|
2014-02-11 02:16:43 +01:00
|
|
|
[sogoObject setIsNew: is_new];
|
2014-01-10 20:12:53 +01:00
|
|
|
[sogoObject saveComponent: o];
|
|
|
|
|
|
|
|
// Everything is fine, lets generate our response
|
|
|
|
[theBuffer appendString: @"<Add>"];
|
|
|
|
[theBuffer appendFormat: @"<ClientId>%@</ClientId>", clientId];
|
|
|
|
[theBuffer appendFormat: @"<ServerId>%@</ServerId>", serverId];
|
|
|
|
[theBuffer appendFormat: @"<Status>%d</Status>", 1];
|
|
|
|
[theBuffer appendString: @"</Add>"];
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
//
|
|
|
|
// <?xml version="1.0"?>
|
|
|
|
// <!DOCTYPE ActiveSync PUBLIC "-//MICROSOFT//DTD ActiveSync//EN" "http://www.microsoft.com/">
|
|
|
|
// <Sync xmlns="AirSync:">
|
|
|
|
// <Collections>
|
|
|
|
// <Collection>
|
|
|
|
// <SyncKey>1387546048</SyncKey>
|
|
|
|
// <CollectionId>vtodo/personal</CollectionId>
|
|
|
|
// <GetChanges/>
|
|
|
|
// <WindowSize>25</WindowSize>
|
|
|
|
// <Options>
|
|
|
|
// <BodyPreference xmlns="AirSyncBase:">
|
|
|
|
// <Type>1</Type>
|
|
|
|
// <TruncationSize>32768</TruncationSize>
|
|
|
|
// </BodyPreference>
|
|
|
|
// </Options>
|
|
|
|
// <Commands>
|
|
|
|
// <Change>
|
|
|
|
// <ServerId>36C5-52B36280-1-27B38F40.ics</ServerId>
|
|
|
|
// <ApplicationData>
|
|
|
|
// <Body xmlns="AirSyncBase:">
|
|
|
|
// <Type>1</Type>
|
|
|
|
// <Data/>
|
|
|
|
// </Body>
|
|
|
|
// <Subject xmlns="Tasks:">foobar1</Subject>
|
|
|
|
// <Importance xmlns="Tasks:">1</Importance>
|
|
|
|
// <Complete xmlns="Tasks:">0</Complete>
|
|
|
|
// <Sensitivity xmlns="Tasks:">0</Sensitivity>
|
|
|
|
// <ReminderSet xmlns="Tasks:">0</ReminderSet>
|
|
|
|
// </ApplicationData>
|
|
|
|
// </Change>
|
|
|
|
// </Commands>
|
|
|
|
// </Collection>
|
|
|
|
// </Collections>
|
|
|
|
// </Sync>
|
|
|
|
//
|
|
|
|
- (void) processSyncChangeCommand: (id <DOMElement>) theDocumentElement
|
|
|
|
inCollection: (id) theCollection
|
|
|
|
withType: (SOGoMicrosoftActiveSyncFolderType) theFolderType
|
|
|
|
inBuffer: (NSMutableString *) theBuffer
|
|
|
|
{
|
|
|
|
NSDictionary *allChanges;
|
|
|
|
NSString *serverId;
|
|
|
|
NSArray *changes;
|
|
|
|
id aChange, o, sogoObject;
|
|
|
|
|
|
|
|
int i;
|
|
|
|
|
|
|
|
changes = (id)[theDocumentElement getElementsByTagName: @"Change"];
|
|
|
|
|
|
|
|
if ([changes count])
|
|
|
|
{
|
|
|
|
for (i = 0; i < [changes count]; i++)
|
|
|
|
{
|
|
|
|
aChange = [changes objectAtIndex: i];
|
|
|
|
|
|
|
|
serverId = [[(id)[aChange getElementsByTagName: @"ServerId"] lastObject] textValue];
|
|
|
|
allChanges = [[(id)[aChange getElementsByTagName: @"ApplicationData"] lastObject] applicationData];
|
|
|
|
|
|
|
|
// Fetch the object and apply the changes
|
|
|
|
sogoObject = [theCollection lookupName: serverId
|
|
|
|
inContext: context
|
|
|
|
acquire: NO];
|
|
|
|
|
2014-01-13 22:24:15 +01:00
|
|
|
// Object was removed inbetween sync/commands?
|
|
|
|
if ([sogoObject isKindOfClass: [NSException class]])
|
|
|
|
{
|
|
|
|
// FIXME - return status == 8
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
2014-01-10 20:12:53 +01:00
|
|
|
switch (theFolderType)
|
|
|
|
{
|
|
|
|
case ActiveSyncContactFolder:
|
|
|
|
{
|
|
|
|
o = [sogoObject vCard];
|
|
|
|
[o takeActiveSyncValues: allChanges];
|
|
|
|
[sogoObject saveComponent: o];
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case ActiveSyncEventFolder:
|
|
|
|
case ActiveSyncTaskFolder:
|
|
|
|
{
|
|
|
|
o = [sogoObject component: NO secure: NO];
|
|
|
|
[o takeActiveSyncValues: allChanges];
|
|
|
|
[sogoObject saveComponent: o];
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case ActiveSyncMailFolder:
|
|
|
|
default:
|
|
|
|
{
|
|
|
|
[sogoObject takeActiveSyncValues: allChanges];
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
//
|
|
|
|
// <?xml version="1.0"?>
|
|
|
|
// <!DOCTYPE ActiveSync PUBLIC "-//MICROSOFT//DTD ActiveSync//EN" "http://www.microsoft.com/">
|
|
|
|
// <Sync xmlns="AirSync:">
|
|
|
|
// <Collections>
|
|
|
|
// <Collection>
|
|
|
|
// <SyncKey>1388764784</SyncKey>
|
|
|
|
// <CollectionId>vtodo/personal</CollectionId>
|
|
|
|
// <GetChanges/>
|
|
|
|
// <WindowSize>25</WindowSize>
|
|
|
|
// <Options>
|
|
|
|
// <BodyPreference xmlns="AirSyncBase:">
|
|
|
|
// <Type>1</Type>
|
|
|
|
// <TruncationSize>32768</TruncationSize>
|
|
|
|
// </BodyPreference>
|
|
|
|
// </Options>
|
|
|
|
// <Commands>
|
|
|
|
// <Delete>
|
|
|
|
// <ServerId>2CB5-52B36080-1-1C1D0240.ics</ServerId>
|
|
|
|
// </Delete>
|
|
|
|
// </Commands>
|
|
|
|
// </Collection>
|
|
|
|
// </Collections>
|
|
|
|
// </Sync>
|
|
|
|
//
|
|
|
|
- (void) processSyncDeleteCommand: (id <DOMElement>) theDocumentElement
|
|
|
|
inCollection: (id) theCollection
|
|
|
|
withType: (SOGoMicrosoftActiveSyncFolderType) theFolderType
|
|
|
|
inBuffer: (NSMutableString *) theBuffer
|
|
|
|
{
|
|
|
|
NSArray *deletions;
|
|
|
|
NSString *serverId;
|
|
|
|
|
|
|
|
id aDelete, sogoObject;
|
|
|
|
int i;
|
|
|
|
|
|
|
|
deletions = (id)[theDocumentElement getElementsByTagName: @"Delete"];
|
|
|
|
|
|
|
|
if ([deletions count])
|
|
|
|
{
|
|
|
|
for (i = 0; i < [deletions count]; i++)
|
|
|
|
{
|
|
|
|
aDelete = [deletions objectAtIndex: i];
|
|
|
|
|
|
|
|
serverId = [[(id)[aDelete getElementsByTagName: @"ServerId"] lastObject] textValue];
|
|
|
|
|
|
|
|
sogoObject = [theCollection lookupName: serverId
|
|
|
|
inContext: context
|
|
|
|
acquire: NO];
|
|
|
|
|
|
|
|
[sogoObject delete];
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
//
|
|
|
|
// <Fetch>
|
|
|
|
// <ServerId>91</ServerId>
|
|
|
|
// </Fetch>
|
|
|
|
//
|
|
|
|
- (void) processSyncFetchCommand: (id <DOMElement>) theDocumentElement
|
|
|
|
inCollection: (id) theCollection
|
|
|
|
withType: (SOGoMicrosoftActiveSyncFolderType) theFolderType
|
|
|
|
inBuffer: (NSMutableString *) theBuffer
|
|
|
|
{
|
|
|
|
NSString *serverId;
|
|
|
|
id o;
|
|
|
|
|
|
|
|
serverId = [[(id)[theDocumentElement getElementsByTagName: @"ServerId"] lastObject] textValue];
|
|
|
|
|
|
|
|
o = [theCollection lookupName: serverId
|
|
|
|
inContext: context
|
|
|
|
acquire: NO];
|
|
|
|
|
|
|
|
// FIXME - error handling
|
|
|
|
[theBuffer appendString: @"<Fetch>"];
|
|
|
|
[theBuffer appendFormat: @"<ServerId>%@</ServerId>", serverId];
|
|
|
|
[theBuffer appendFormat: @"<Status>%d</Status>", 1];
|
|
|
|
[theBuffer appendString: @"<ApplicationData>"];
|
|
|
|
[theBuffer appendString: [o activeSyncRepresentation]];
|
|
|
|
[theBuffer appendString: @"</ApplicationData>"];
|
|
|
|
[theBuffer appendString: @"</Fetch>"];
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
//
|
|
|
|
// The method handles <GetChanges/>
|
|
|
|
//
|
|
|
|
- (void) processSyncGetChanges: (id <DOMElement>) theDocumentElement
|
|
|
|
inCollection: (id) theCollection
|
|
|
|
withSyncKey: (NSString *) theSyncKey
|
2014-01-16 21:13:09 +01:00
|
|
|
withFolderType: (SOGoMicrosoftActiveSyncFolderType) theFolderType
|
2014-01-20 16:13:16 +01:00
|
|
|
withFilterType: (NSCalendarDate *) theFilterType
|
2014-01-10 20:12:53 +01:00
|
|
|
inBuffer: (NSMutableString *) theBuffer
|
|
|
|
{
|
2014-01-22 17:02:12 +01:00
|
|
|
NSMutableString *s;
|
2014-01-10 20:12:53 +01:00
|
|
|
int i;
|
|
|
|
|
|
|
|
//
|
|
|
|
// No changes in the collection - 2.2.2.19.1.1 Empty Sync Request.
|
|
|
|
// We check this and we don't generate any commands if we don't have to.
|
|
|
|
//
|
|
|
|
if ([theSyncKey isEqualToString: [theCollection davCollectionTag]])
|
|
|
|
return;
|
|
|
|
|
2014-01-22 17:02:12 +01:00
|
|
|
s = [NSMutableString string];
|
2014-01-10 20:12:53 +01:00
|
|
|
|
|
|
|
switch (theFolderType)
|
|
|
|
{
|
2014-01-20 16:13:16 +01:00
|
|
|
// Handle all the GCS components
|
2014-01-10 20:12:53 +01:00
|
|
|
case ActiveSyncContactFolder:
|
|
|
|
case ActiveSyncEventFolder:
|
|
|
|
case ActiveSyncTaskFolder:
|
|
|
|
{
|
2014-01-20 16:13:16 +01:00
|
|
|
id sogoObject, componentObject;
|
|
|
|
NSString *uid, *component_name;
|
|
|
|
NSDictionary *component;
|
|
|
|
NSArray *allComponents;
|
|
|
|
|
|
|
|
BOOL updated;
|
|
|
|
int deleted;
|
|
|
|
|
|
|
|
if (theFolderType == ActiveSyncContactFolder)
|
|
|
|
component_name = @"vcard";
|
|
|
|
else if (theFolderType == ActiveSyncEventFolder)
|
|
|
|
component_name = @"vevent";
|
|
|
|
else
|
|
|
|
component_name = @"vtodo";
|
|
|
|
|
|
|
|
allComponents = [theCollection syncTokenFieldsWithProperties: nil matchingSyncToken: theSyncKey fromDate: theFilterType];
|
2014-01-10 20:12:53 +01:00
|
|
|
|
2014-01-20 16:13:16 +01:00
|
|
|
for (i = 0; i < [allComponents count]; i++)
|
2014-01-10 20:12:53 +01:00
|
|
|
{
|
2014-01-20 16:13:16 +01:00
|
|
|
component = [allComponents objectAtIndex: i];
|
|
|
|
deleted = [[component objectForKey: @"c_deleted"] intValue];
|
2014-01-10 20:12:53 +01:00
|
|
|
|
2014-01-20 16:13:16 +01:00
|
|
|
if (!deleted && ![[component objectForKey: @"c_component"] isEqualToString: component_name])
|
2014-01-10 20:12:53 +01:00
|
|
|
continue;
|
|
|
|
|
2014-01-20 16:13:16 +01:00
|
|
|
uid = [component objectForKey: @"c_name"];
|
2014-01-10 20:12:53 +01:00
|
|
|
|
|
|
|
if (deleted)
|
|
|
|
{
|
2014-01-22 17:02:12 +01:00
|
|
|
[s appendString: @"<Delete xmlns=\"AirSync:\">"];
|
|
|
|
[s appendFormat: @"<ServerId xmlns=\"AirSync:\">%@</ServerId>", uid];
|
|
|
|
[s appendString: @"</Delete>"];
|
2014-01-10 20:12:53 +01:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
updated = YES;
|
|
|
|
|
2014-01-20 16:13:16 +01:00
|
|
|
if ([[component objectForKey: @"c_creationdate"] intValue] > [theSyncKey intValue])
|
2014-01-10 20:12:53 +01:00
|
|
|
updated = NO;
|
|
|
|
|
|
|
|
if (updated)
|
2014-01-22 17:02:12 +01:00
|
|
|
[s appendString: @"<Change xmlns=\"AirSync:\">"];
|
2014-01-10 20:12:53 +01:00
|
|
|
else
|
2014-01-22 17:02:12 +01:00
|
|
|
[s appendString: @"<Add xmlns=\"AirSync:\">"];
|
2014-01-10 20:12:53 +01:00
|
|
|
|
2014-01-22 17:02:12 +01:00
|
|
|
[s appendFormat: @"<ServerId xmlns=\"AirSync:\">%@</ServerId>", uid];
|
|
|
|
[s appendString: @"<ApplicationData xmlns=\"AirSync:\">"];
|
2014-01-10 20:12:53 +01:00
|
|
|
|
2014-01-20 16:13:16 +01:00
|
|
|
sogoObject = [theCollection lookupName: uid
|
|
|
|
inContext: context
|
|
|
|
acquire: 0];
|
2014-01-10 20:12:53 +01:00
|
|
|
|
2014-01-20 16:13:16 +01:00
|
|
|
if (theFolderType == ActiveSyncContactFolder)
|
|
|
|
componentObject = [sogoObject vCard];
|
|
|
|
else
|
|
|
|
componentObject = [sogoObject component: NO secure: NO];
|
2014-01-10 20:12:53 +01:00
|
|
|
|
2014-01-22 17:02:12 +01:00
|
|
|
[s appendString: [componentObject activeSyncRepresentation]];
|
2014-01-10 20:12:53 +01:00
|
|
|
|
2014-01-22 17:02:12 +01:00
|
|
|
[s appendString: @"</ApplicationData>"];
|
2014-01-10 20:12:53 +01:00
|
|
|
|
|
|
|
if (updated)
|
2014-01-22 17:02:12 +01:00
|
|
|
[s appendString: @"</Change>"];
|
2014-01-10 20:12:53 +01:00
|
|
|
else
|
2014-01-22 17:02:12 +01:00
|
|
|
[s appendString: @"</Add>"];
|
2014-01-10 20:12:53 +01:00
|
|
|
}
|
|
|
|
} // for ...
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case ActiveSyncMailFolder:
|
|
|
|
default:
|
|
|
|
{
|
2014-01-20 16:13:16 +01:00
|
|
|
SOGoMailObject *mailObject;
|
|
|
|
NSString *uid, *command;
|
2014-01-10 20:12:53 +01:00
|
|
|
NSDictionary *aMessage;
|
|
|
|
NSArray *allMessages;
|
|
|
|
|
2014-01-20 16:13:16 +01:00
|
|
|
allMessages = [theCollection syncTokenFieldsWithProperties: nil matchingSyncToken: theSyncKey fromDate: theFilterType];
|
2014-01-10 20:12:53 +01:00
|
|
|
|
|
|
|
for (i = 0; i < [allMessages count]; i++)
|
|
|
|
{
|
|
|
|
aMessage = [allMessages objectAtIndex: i];
|
|
|
|
|
|
|
|
uid = [[[aMessage allKeys] lastObject] stringValue];
|
|
|
|
command = [[aMessage allValues] lastObject];
|
|
|
|
|
|
|
|
if ([command isEqualToString: @"deleted"])
|
|
|
|
{
|
2014-01-22 17:02:12 +01:00
|
|
|
[s appendString: @"<Delete xmlns=\"AirSync:\">"];
|
|
|
|
[s appendFormat: @"<ServerId xmlns=\"AirSync:\">%@</ServerId>", uid];
|
|
|
|
[s appendString: @"</Delete>"];
|
2014-01-10 20:12:53 +01:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
if ([command isEqualToString: @"added"])
|
2014-01-22 17:02:12 +01:00
|
|
|
[s appendString: @"<Add xmlns=\"AirSync:\">"];
|
2014-01-10 20:12:53 +01:00
|
|
|
else
|
2014-01-22 17:02:12 +01:00
|
|
|
[s appendString: @"<Change xmlns=\"AirSync:\">"];
|
2014-01-10 20:12:53 +01:00
|
|
|
|
|
|
|
mailObject = [theCollection lookupName: uid
|
|
|
|
inContext: context
|
|
|
|
acquire: 0];
|
|
|
|
|
2014-01-22 17:02:12 +01:00
|
|
|
[s appendFormat: @"<ServerId xmlns=\"AirSync:\">%@</ServerId>", uid];
|
|
|
|
[s appendString: @"<ApplicationData xmlns=\"AirSync:\">"];
|
|
|
|
[s appendString: [mailObject activeSyncRepresentation]];
|
|
|
|
[s appendString: @"</ApplicationData>"];
|
2014-01-10 20:12:53 +01:00
|
|
|
|
|
|
|
if ([command isEqualToString: @"added"])
|
2014-01-22 17:02:12 +01:00
|
|
|
[s appendString: @"</Add>"];
|
2014-01-10 20:12:53 +01:00
|
|
|
else
|
2014-01-22 17:02:12 +01:00
|
|
|
[s appendString: @"</Change>"];
|
2014-01-10 20:12:53 +01:00
|
|
|
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
} // switch (folderType) ...
|
|
|
|
|
2014-01-22 17:02:12 +01:00
|
|
|
if ([s length])
|
|
|
|
{
|
|
|
|
[theBuffer appendString: @"<Commands>"];
|
|
|
|
[theBuffer appendString: s];
|
|
|
|
[theBuffer appendString: @"</Commands>"];
|
|
|
|
}
|
2014-01-10 20:12:53 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
//
|
|
|
|
// We have something like this:
|
|
|
|
//
|
|
|
|
// <Commands>
|
|
|
|
// <Fetch>
|
|
|
|
// <ServerId>91</ServerId>
|
|
|
|
// </Fetch>
|
|
|
|
// </Commands>
|
|
|
|
//
|
|
|
|
- (void) processSyncCommands: (id <DOMElement>) theDocumentElement
|
|
|
|
inCollection: (id) theCollection
|
|
|
|
withType: (SOGoMicrosoftActiveSyncFolderType) theFolderType
|
|
|
|
inBuffer: (NSMutableString *) theBuffer
|
|
|
|
processed: (BOOL *) processed
|
|
|
|
{
|
|
|
|
id <DOMNodeList> aCommandDetails;
|
|
|
|
id <DOMElement> aCommand, element;
|
|
|
|
NSArray *allCommands;
|
|
|
|
int i, j;
|
|
|
|
|
|
|
|
allCommands = (id)[theDocumentElement getElementsByTagName: @"Commands"];
|
|
|
|
|
|
|
|
for (i = 0; i < [allCommands count]; i++)
|
|
|
|
{
|
|
|
|
aCommand = [allCommands objectAtIndex: i];
|
|
|
|
aCommandDetails = [aCommand childNodes];
|
|
|
|
|
|
|
|
for (j = 0; j < [(id)aCommandDetails count]; j++)
|
|
|
|
{
|
|
|
|
element = [aCommandDetails objectAtIndex: j];
|
|
|
|
|
|
|
|
if ([element nodeType] == DOM_ELEMENT_NODE)
|
|
|
|
{
|
|
|
|
if ([[element tagName] isEqualToString: @"Add"])
|
|
|
|
{
|
|
|
|
// Add
|
|
|
|
[self processSyncAddCommand: aCommand
|
|
|
|
inCollection: theCollection
|
|
|
|
withType: theFolderType
|
|
|
|
inBuffer: theBuffer];
|
|
|
|
*processed = YES;
|
|
|
|
}
|
|
|
|
else if ([[element tagName] isEqualToString: @"Change"])
|
|
|
|
{
|
|
|
|
// Change
|
|
|
|
[self processSyncChangeCommand: aCommand
|
|
|
|
inCollection: theCollection
|
|
|
|
withType: theFolderType
|
|
|
|
inBuffer: theBuffer];
|
|
|
|
*processed = YES;
|
|
|
|
}
|
|
|
|
else if ([[element tagName] isEqualToString: @"Delete"])
|
|
|
|
{
|
|
|
|
// Delete
|
|
|
|
[self processSyncDeleteCommand: aCommand
|
|
|
|
inCollection: theCollection
|
|
|
|
withType: theFolderType
|
|
|
|
inBuffer: theBuffer];
|
|
|
|
}
|
|
|
|
else if ([[element tagName] isEqualToString: @"Fetch"])
|
|
|
|
{
|
|
|
|
// Fetch
|
|
|
|
[self processSyncFetchCommand: aCommand
|
|
|
|
inCollection: theCollection
|
|
|
|
withType: theFolderType
|
|
|
|
inBuffer: theBuffer];
|
2014-01-14 16:09:10 +01:00
|
|
|
*processed = YES;
|
2014-01-10 20:12:53 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
//
|
|
|
|
//
|
|
|
|
//
|
|
|
|
- (void) processSyncCollection: (id <DOMElement>) theDocumentElement
|
|
|
|
inBuffer: (NSMutableString *) theBuffer
|
2014-02-04 17:19:33 +01:00
|
|
|
changeDetected: (BOOL *) changeDetected
|
2014-01-10 20:12:53 +01:00
|
|
|
{
|
|
|
|
NSString *collectionId, *realCollectionId, *syncKey, *davCollectionTag, *bodyPreferenceType;
|
|
|
|
SOGoMicrosoftActiveSyncFolderType folderType;
|
2014-01-14 17:41:26 +01:00
|
|
|
id collection, value;
|
2014-01-28 19:51:21 +01:00
|
|
|
|
|
|
|
NSMutableString *changeBuffer, *commandsBuffer;
|
2014-01-10 20:12:53 +01:00
|
|
|
BOOL getChanges, first_sync;
|
2014-01-28 19:51:21 +01:00
|
|
|
|
|
|
|
changeBuffer = [NSMutableString string];
|
|
|
|
commandsBuffer = [NSMutableString string];
|
|
|
|
|
2014-01-10 20:12:53 +01:00
|
|
|
collectionId = [[(id)[theDocumentElement getElementsByTagName: @"CollectionId"] lastObject] textValue];
|
|
|
|
realCollectionId = [collectionId realCollectionIdWithFolderType: &folderType];
|
|
|
|
collection = [self collectionFromId: realCollectionId type: folderType];
|
|
|
|
|
2014-01-28 19:51:21 +01:00
|
|
|
syncKey = davCollectionTag = [[(id)[theDocumentElement getElementsByTagName: @"SyncKey"] lastObject] textValue];
|
2014-01-10 20:12:53 +01:00
|
|
|
|
2014-01-14 17:41:26 +01:00
|
|
|
// 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.
|
|
|
|
value = [theDocumentElement getElementsByTagName: @"GetChanges"];
|
|
|
|
getChanges = YES;
|
|
|
|
|
2014-01-16 21:13:09 +01:00
|
|
|
if ([value count] && [[[value lastObject] textValue] length])
|
2014-01-14 17:41:26 +01:00
|
|
|
getChanges = [[[value lastObject] textValue] boolValue];
|
2014-01-16 21:13:09 +01:00
|
|
|
|
2014-01-10 20:12:53 +01:00
|
|
|
first_sync = NO;
|
|
|
|
|
|
|
|
if ([syncKey isEqualToString: @"0"])
|
|
|
|
{
|
|
|
|
davCollectionTag = @"-1";
|
|
|
|
first_sync = YES;
|
2014-02-04 17:19:33 +01:00
|
|
|
*changeDetected = YES;
|
2014-01-10 20:12:53 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
// We check our sync preferences and we stash them
|
|
|
|
bodyPreferenceType = [[(id)[[(id)[theDocumentElement getElementsByTagName: @"BodyPreference"] lastObject] getElementsByTagName: @"Type"] lastObject] textValue];
|
|
|
|
|
|
|
|
if (!bodyPreferenceType)
|
|
|
|
bodyPreferenceType = @"1";
|
|
|
|
|
|
|
|
[context setObject: bodyPreferenceType forKey: @"BodyPreferenceType"];
|
|
|
|
|
|
|
|
|
|
|
|
// We generate the commands, if any, for the response. We might also have
|
|
|
|
// generated some in processSyncCommand:inResponse: as we could have
|
|
|
|
// received a Fetch command
|
|
|
|
if (getChanges && !first_sync)
|
|
|
|
{
|
|
|
|
[self processSyncGetChanges: theDocumentElement
|
|
|
|
inCollection: collection
|
|
|
|
withSyncKey: syncKey
|
2014-01-16 21:13:09 +01:00
|
|
|
withFolderType: folderType
|
2014-01-20 16:13:16 +01:00
|
|
|
withFilterType: [NSCalendarDate dateFromFilterType: [[(id)[theDocumentElement getElementsByTagName: @"FilterType"] lastObject] textValue]]
|
2014-01-28 19:51:21 +01:00
|
|
|
inBuffer: changeBuffer];
|
2014-01-10 20:12:53 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
//
|
|
|
|
// We process the commands from the request
|
|
|
|
//
|
|
|
|
if (!first_sync)
|
|
|
|
{
|
|
|
|
NSMutableString *s;
|
|
|
|
BOOL processed;
|
|
|
|
|
|
|
|
s = [NSMutableString string];
|
|
|
|
processed = NO;
|
|
|
|
|
|
|
|
[self processSyncCommands: theDocumentElement
|
|
|
|
inCollection: collection
|
|
|
|
withType: folderType
|
|
|
|
inBuffer: s
|
|
|
|
processed: &processed];
|
|
|
|
|
|
|
|
if (processed)
|
2014-01-28 19:51:21 +01:00
|
|
|
[commandsBuffer appendFormat: @"<Responses>%@</Responses>", s];
|
2014-01-10 20:12:53 +01:00
|
|
|
else
|
2014-01-28 19:51:21 +01:00
|
|
|
[commandsBuffer appendString: s];
|
2014-01-10 20:12:53 +01:00
|
|
|
}
|
|
|
|
|
2014-01-28 19:51:21 +01:00
|
|
|
// If we got any changes or if we have applied any commands
|
|
|
|
// let's regenerate our SyncKey based on the collection tag.
|
|
|
|
if ([changeBuffer length] || [commandsBuffer length])
|
2014-02-04 17:19:33 +01:00
|
|
|
{
|
|
|
|
davCollectionTag = [collection davCollectionTag];
|
|
|
|
*changeDetected = YES;
|
|
|
|
}
|
2014-01-28 19:51:21 +01:00
|
|
|
|
|
|
|
// Generate the response buffer
|
|
|
|
[theBuffer appendString: @"<Collection>"];
|
|
|
|
|
|
|
|
if (folderType == ActiveSyncMailFolder)
|
|
|
|
[theBuffer appendString: @"<Class>Email</Class>"];
|
|
|
|
else if (folderType == ActiveSyncContactFolder)
|
|
|
|
[theBuffer appendString: @"<Class>Contacts</Class>"];
|
|
|
|
else if (folderType == ActiveSyncEventFolder)
|
|
|
|
[theBuffer appendString: @"<Class>Calendar</Class>"];
|
|
|
|
else if (folderType == ActiveSyncTaskFolder)
|
|
|
|
[theBuffer appendString: @"<Class>Tasks</Class>"];
|
|
|
|
|
|
|
|
[theBuffer appendFormat: @"<SyncKey>%@</SyncKey>", davCollectionTag];
|
|
|
|
[theBuffer appendFormat: @"<CollectionId>%@</CollectionId>", collectionId];
|
|
|
|
[theBuffer appendFormat: @"<Status>%d</Status>", 1];
|
|
|
|
|
|
|
|
[theBuffer appendString: changeBuffer];
|
|
|
|
[theBuffer appendString: commandsBuffer];
|
|
|
|
|
2014-01-10 20:12:53 +01:00
|
|
|
[theBuffer appendString: @"</Collection>"];
|
|
|
|
}
|
|
|
|
|
|
|
|
//
|
|
|
|
// Initial folder sync:
|
|
|
|
//
|
|
|
|
// <?xml version="1.0"?>
|
|
|
|
// <!DOCTYPE ActiveSync PUBLIC "-//MICROSOFT//DTD ActiveSync//EN" "http://www.microsoft.com/">
|
|
|
|
// <Sync xmlns="AirSync:">
|
|
|
|
// <Collections>
|
|
|
|
// <Collection>
|
|
|
|
// <SyncKey>0</SyncKey>
|
|
|
|
// <CollectionId>folderINBOX</CollectionId>
|
|
|
|
// </Collection>
|
|
|
|
// </Collections>
|
|
|
|
// </Sync>
|
|
|
|
//
|
|
|
|
//
|
|
|
|
// Following this will be a GetItemEstimate call. Following our response to the GetItemEstimate, we'll
|
|
|
|
// have a new Sync call like this:
|
|
|
|
//
|
|
|
|
// <?xml version="1.0"?>
|
|
|
|
// <!DOCTYPE ActiveSync PUBLIC "-//MICROSOFT//DTD ActiveSync//EN" "http://www.microsoft.com/">
|
|
|
|
// <Sync xmlns="AirSync:">
|
|
|
|
// <Collections>
|
|
|
|
// <Collection>
|
|
|
|
// <SyncKey>1</SyncKey>
|
|
|
|
// <CollectionId>folderINBOX</CollectionId>
|
|
|
|
// <DeletesAsMoves>1</DeletesAsMoves>
|
|
|
|
// <GetChanges/>
|
|
|
|
// <WindowSize>50</WindowSize>
|
|
|
|
// <Options>
|
|
|
|
// <FilterType>5</FilterType> -- http://msdn.microsoft.com/en-us/library/gg709713(v=exchg.80).aspx
|
|
|
|
// <BodyPreference xmlns="AirSyncBase:"> -- http://msdn.microsoft.com/en-us/library/ee218197(v=exchg.80).aspx
|
|
|
|
// <Type>2</Type> --
|
|
|
|
// <TruncationSize>51200</TruncationSize>
|
|
|
|
// </BodyPreference>
|
|
|
|
// <BodyPreference xmlns="AirSyncBase:">
|
|
|
|
// <Type>4</Type>
|
|
|
|
// </BodyPreference>
|
|
|
|
// </Options>
|
|
|
|
// </Collection>
|
|
|
|
// </Collections>
|
|
|
|
// </Sync>
|
|
|
|
//
|
|
|
|
//
|
|
|
|
//
|
|
|
|
// When adding a new task, we might have something like this:
|
|
|
|
//
|
|
|
|
// <?xml version="1.0"?>
|
|
|
|
// <!DOCTYPE ActiveSync PUBLIC "-//MICROSOFT//DTD ActiveSync//EN" "http://www.microsoft.com/">
|
|
|
|
// <Sync xmlns="AirSync:">
|
|
|
|
// <Collections>
|
|
|
|
// <Collection>
|
|
|
|
// <SyncKey>1</SyncKey>
|
|
|
|
// <CollectionId>personal</CollectionId>
|
|
|
|
// <DeletesAsMoves/>
|
|
|
|
// <GetChanges/> -- http://msdn.microsoft.com/en-us/library/gg675447(v=exchg.80).aspx
|
|
|
|
// <WindowSize>5</WindowSize> -- http://msdn.microsoft.com/en-us/library/gg650865(v=exchg.80).aspx
|
|
|
|
// <Options>
|
|
|
|
// <BodyPreference xmlns="AirSyncBase:"> -- http://msdn.microsoft.com/en-us/library/ee218197(v=exchg.80).aspx
|
|
|
|
// <Type>1</Type>
|
|
|
|
// <TruncationSize>400000</TruncationSize>
|
|
|
|
// </BodyPreference>
|
|
|
|
// </Options>
|
|
|
|
// <Commands>
|
|
|
|
// <Add>
|
|
|
|
// <ClientId>new_task_1386614771261</ClientId>
|
|
|
|
// <ApplicationData>
|
|
|
|
// <Body xmlns="AirSyncBase:">
|
|
|
|
// <Type>1</Type>
|
|
|
|
// <EstimatedDataSize>6</EstimatedDataSize>
|
|
|
|
// <Data>tomate</Data>
|
|
|
|
// </Body>
|
|
|
|
// <Subject xmlns="Tasks:">test 1</Subject>
|
|
|
|
// <Importance xmlns="Tasks:">1</Importance>
|
|
|
|
// <UTCDueDate xmlns="Tasks:">2013-12-09T19:00:00.000Z</UTCDueDate>
|
|
|
|
// <Complete xmlns="Tasks:">0</Complete>
|
|
|
|
// <ReminderSet xmlns="Tasks:">0</ReminderSet>
|
|
|
|
// <DueDate xmlns="Tasks:">2013-12-09T19:00:00.000Z</DueDate>
|
|
|
|
// </ApplicationData>
|
|
|
|
// </Add>
|
|
|
|
// </Commands>
|
|
|
|
// </Collection>
|
|
|
|
// </Collections>
|
|
|
|
// </Sync>
|
|
|
|
//
|
|
|
|
// The algorithm here is pretty simple:
|
|
|
|
//
|
|
|
|
// 1. extract the list of collections
|
|
|
|
// 2. for each collection
|
|
|
|
// 2.1. extract the metadata (id, synckey, etc.)
|
|
|
|
// 2.2. extract the list of commands
|
|
|
|
// 2.3. for each command
|
|
|
|
// 2.3.1 process the command (add/change/delete/fetch)
|
|
|
|
// 2.3.2 build a response during the processsing
|
|
|
|
//
|
|
|
|
//
|
|
|
|
- (void) processSync: (id <DOMElement>) theDocumentElement
|
|
|
|
inResponse: (WOResponse *) theResponse
|
|
|
|
{
|
2014-02-11 02:16:43 +01:00
|
|
|
SOGoSystemDefaults *defaults;
|
2014-01-10 20:12:53 +01:00
|
|
|
id <DOMElement> aCollection;
|
2014-02-04 17:19:33 +01:00
|
|
|
NSMutableString *output, *s;
|
2014-02-11 02:16:43 +01:00
|
|
|
NSArray *allCollections;
|
2014-01-10 20:12:53 +01:00
|
|
|
NSData *d;
|
|
|
|
|
2014-02-11 02:16:43 +01:00
|
|
|
int i, j, defaultInterval, heartbeatInterval, internalInterval;
|
2014-02-04 17:19:33 +01:00
|
|
|
BOOL changeDetected;
|
2014-01-10 20:12:53 +01:00
|
|
|
|
|
|
|
// We initialize our output buffer
|
2014-02-04 17:19:33 +01:00
|
|
|
output = [NSMutableString string];
|
2014-01-10 20:12:53 +01:00
|
|
|
|
2014-02-04 17:19:33 +01:00
|
|
|
[output appendString: @"<?xml version=\"1.0\" encoding=\"utf-8\"?>"];
|
|
|
|
[output appendString: @"<!DOCTYPE ActiveSync PUBLIC \"-//MICROSOFT//DTD ActiveSync//EN\" \"http://www.microsoft.com/\">"];
|
|
|
|
[output appendString: @"<Sync xmlns=\"AirSync:\">"];
|
2014-02-11 02:16:43 +01:00
|
|
|
|
|
|
|
defaults = [SOGoSystemDefaults sharedSystemDefaults];
|
2014-02-04 17:19:33 +01:00
|
|
|
heartbeatInterval = [[[(id)[theDocumentElement getElementsByTagName: @"HeartbeatInterval"] lastObject] textValue] intValue];
|
2014-02-11 02:16:43 +01:00
|
|
|
defaultInterval = [defaults maximumSyncInterval];
|
|
|
|
internalInterval = [defaults internalSyncInterval];
|
2014-02-04 17:19:33 +01:00
|
|
|
|
|
|
|
// We check to see if our heartbeat interval falls into the supported ranges.
|
2014-02-11 02:16:43 +01:00
|
|
|
if (heartbeatInterval > defaultInterval || heartbeatInterval < 1)
|
2014-02-04 17:19:33 +01:00
|
|
|
{
|
|
|
|
// Interval is too long, inform the client.
|
2014-02-11 02:16:43 +01:00
|
|
|
heartbeatInterval = defaultInterval;
|
2014-02-04 17:19:33 +01:00
|
|
|
|
2014-02-11 02:16:43 +01:00
|
|
|
// Outlook doesn't like this...
|
|
|
|
//[output appendFormat: @"<Limit>%d</Limit>", defaultInterval];
|
2014-02-04 17:19:33 +01:00
|
|
|
//[output appendFormat: @"<Status>%d</Status>", 14];
|
|
|
|
}
|
|
|
|
|
|
|
|
[output appendString: @"<Collections>"];
|
|
|
|
|
2014-01-13 22:24:15 +01:00
|
|
|
allCollections = (id)[theDocumentElement getElementsByTagName: @"Collection"];
|
2014-01-10 20:12:53 +01:00
|
|
|
|
2014-02-04 17:19:33 +01:00
|
|
|
// We enter our loop detection change
|
2014-02-11 02:16:43 +01:00
|
|
|
for (i = 0; i < (defaultInterval/internalInterval); i++)
|
2014-01-10 20:12:53 +01:00
|
|
|
{
|
2014-02-04 17:19:33 +01:00
|
|
|
s = [NSMutableString string];
|
|
|
|
|
|
|
|
for (j = 0; j < [allCollections count]; j++)
|
|
|
|
{
|
|
|
|
aCollection = [allCollections objectAtIndex: j];
|
|
|
|
|
|
|
|
[self processSyncCollection: aCollection inBuffer: s changeDetected: &changeDetected];
|
|
|
|
}
|
2014-01-10 20:12:53 +01:00
|
|
|
|
2014-02-04 17:19:33 +01:00
|
|
|
if (changeDetected)
|
|
|
|
{
|
|
|
|
NSLog(@"Change detected, we push the content.");
|
|
|
|
[output appendString: s];
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2014-02-11 02:16:43 +01:00
|
|
|
NSLog(@"Sleeping %d seconds while detecting changes...", internalInterval);
|
|
|
|
sleep(internalInterval);
|
2014-02-04 17:19:33 +01:00
|
|
|
}
|
2014-01-10 20:12:53 +01:00
|
|
|
}
|
|
|
|
|
2014-02-04 17:19:33 +01:00
|
|
|
[output appendString: @"</Collections></Sync>"];
|
2014-01-10 20:12:53 +01:00
|
|
|
|
2014-02-04 17:19:33 +01:00
|
|
|
d = [[output dataUsingEncoding: NSUTF8StringEncoding] xml2wbxml];
|
2014-01-10 20:12:53 +01:00
|
|
|
|
|
|
|
[theResponse setContent: d];
|
|
|
|
}
|
|
|
|
|
|
|
|
@end
|