diff --git a/ActiveSync/NGDOMElement+ActiveSync.m b/ActiveSync/NGDOMElement+ActiveSync.m index 61799bfd2..7fb792e3c 100644 --- a/ActiveSync/NGDOMElement+ActiveSync.m +++ b/ActiveSync/NGDOMElement+ActiveSync.m @@ -31,6 +31,9 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #import #import +#import + +static NSArray *asElementArray = nil; @implementation NGDOMElement (ActiveSync) @@ -44,13 +47,40 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // // // +// and stuff like that: +// +// +// +// sogo1@example.com +// John Doe +// 5 +// 1 +// +// +// sogo2@example.com +// Balthazar César +// 5 +// 1 +// +// +// sogo3@example.com +// Wolfgang Fritz +// 5 +// 1 +// +// +// + - (NSDictionary *) applicationData { NSMutableDictionary *data; id children; id element; - int i; + int i, count; + if (!asElementArray) + asElementArray = [[NSArray alloc] initWithObjects: @"Attendee", nil]; + data = [NSMutableDictionary dictionary]; children = [self childNodes]; @@ -65,10 +95,55 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. id value; tag = [element tagName]; + count = [(NSArray *)[element childNodes] count]; - // Handle inner data - if ([(NSArray *)[element childNodes] count] > 2) - value = [(NGDOMElement *)element applicationData]; + // Handle inner data - see above for samples + if (count > 2) + { + NSMutableArray *innerElements; + id innerElement; + NSArray *childNodes; + NSString *innerTag; + BOOL same; + int j; + + childNodes = (NSArray *)[element childNodes]; + innerElements = [NSMutableArray array]; + innerTag = nil; + same = YES; + + for (j = 1; j < count; j++) + { + innerElement = [childNodes objectAtIndex: j]; + + if ([innerElement nodeType] == DOM_ELEMENT_NODE) + { + if (!innerTag) + innerTag = [innerElement tagName]; + + if ([innerTag isEqualToString: [innerElement tagName]]) + { + [innerElements addObject: [(NGDOMElement *)innerElement applicationData]]; + } + else + { + same = NO; + break; + } + } + } + + if (same && [asElementArray containsObject: innerTag]) + value = innerElements; + else + { + value = [(NGDOMElement *)element applicationData]; + + // Don't set empty values like Foo = {} + if (![value count]) + value = nil; + } + } else value = [[element firstChild] nodeValue]; diff --git a/ActiveSync/NSString+ActiveSync.h b/ActiveSync/NSString+ActiveSync.h index d3c0cd028..c1d32c0f7 100644 --- a/ActiveSync/NSString+ActiveSync.h +++ b/ActiveSync/NSString+ActiveSync.h @@ -35,6 +35,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #include "SOGoActiveSyncConstants.h" @class NSCalendarDate; +@class NSData; @interface NSString (ActiveSync) @@ -43,6 +44,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - (NSCalendarDate *) calendarDate; - (NSString *) deviceId; - (NSString *) command; +- (NSData *) convertHexStringToBytes; @end diff --git a/ActiveSync/NSString+ActiveSync.m b/ActiveSync/NSString+ActiveSync.m index b8cdac502..53637cec6 100644 --- a/ActiveSync/NSString+ActiveSync.m +++ b/ActiveSync/NSString+ActiveSync.m @@ -31,6 +31,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #include #include +#include #include #include @@ -53,30 +54,34 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - (NSString *) realCollectionIdWithFolderType: (SOGoMicrosoftActiveSyncFolderType *) folderType; { - NSString *realCollectionId; + NSString *realCollectionId, *v; *folderType = ActiveSyncGenericFolder; + v = [self stringByUnescapingURL]; - if ([self hasPrefix: @"vevent/"]) + if ([v hasPrefix: @"vevent/"]) { - realCollectionId = [self substringFromIndex: 7]; + realCollectionId = [v substringFromIndex: 7]; *folderType = ActiveSyncEventFolder; } - else if ([self hasPrefix: @"vtodo/"]) + else if ([v hasPrefix: @"vtodo/"]) { - realCollectionId = [self substringFromIndex: 6]; + realCollectionId = [v substringFromIndex: 6]; *folderType = ActiveSyncTaskFolder; } - else if ([self hasPrefix: @"vcard/"]) + else if ([v hasPrefix: @"vcard/"]) { - realCollectionId = [self substringFromIndex: 6]; + realCollectionId = [v substringFromIndex: 6]; *folderType = ActiveSyncContactFolder; } + else if ([v hasPrefix: @"mail/"]) + { + realCollectionId = [[v stringByUnescapingURL] substringFromIndex: 5]; + *folderType = ActiveSyncMailFolder; + } else { - // mail/ - realCollectionId = [[self stringByUnescapingURL] substringFromIndex: 5]; - *folderType = ActiveSyncMailFolder; + realCollectionId = nil; } return realCollectionId; @@ -150,4 +155,100 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. return s; } +// +// FIXME: combine with our OpenChange code. +// +- (char) _decodeHexByte: (char) byteChar +{ + char newByte; + + if (byteChar >= 48 && byteChar <= 57) + newByte = (uint8_t) byteChar - 48; + else if (byteChar >= 65 && byteChar <= 70) + newByte = (uint8_t) byteChar - 55; + else if (byteChar >= 97 && byteChar <= 102) + newByte = (uint8_t) byteChar - 87; + else + newByte = -1; + + return newByte; +} + +// +// FIXME: combine with our OpenChange code. +// +- (BOOL) _decodeHexByte: (uint8_t *) byte + atPos: (NSUInteger) pos +{ + BOOL error = NO; + char newByte; + unichar byteChar; + + byteChar = [self characterAtIndex: pos]; + if (byteChar < 256) + { + newByte = [self _decodeHexByte: (char) byteChar]; + if (newByte == -1) + error = YES; + else + *byte = newByte; + } + else + error = YES; + + return error; +} + +// +// FIXME: combine with our OpenChange code. +// +- (BOOL) _decodeHexPair: (uint8_t *) byte + atPos: (NSUInteger) pos +{ + BOOL error; + uint8_t lowValue, highValue; + + error = [self _decodeHexByte: &highValue atPos: pos]; + if (!error) + { + error = [self _decodeHexByte: &lowValue atPos: pos + 1]; + if (!error) + *byte = highValue << 4 | lowValue; + } + + return error; +} + +// +// FIXME: combine with our OpenChange code. +// +- (NSData *) convertHexStringToBytes +{ + NSUInteger count, strLen, bytesLen; + uint8_t *bytes, *currentByte; + NSData *decoded = nil; + BOOL error = NO; + + strLen = [self length]; + if ((strLen % 2) == 0) + { + bytesLen = strLen / 2; + bytes = NSZoneCalloc (NULL, bytesLen, sizeof (uint8_t)); + currentByte = bytes; + for (count = 0; !error && count < strLen; count += 2) + { + error = [self _decodeHexPair: currentByte atPos: count]; + currentByte++; + } + if (error) + NSZoneFree (NULL, bytes); + else + decoded = [NSData dataWithBytesNoCopy: bytes + length: bytesLen + freeWhenDone: YES]; + } + + return decoded; +} + @end diff --git a/ActiveSync/SOGoActiveSyncDispatcher+Sync.m b/ActiveSync/SOGoActiveSyncDispatcher+Sync.m index 442589bd4..e93fc3325 100644 --- a/ActiveSync/SOGoActiveSyncDispatcher+Sync.m +++ b/ActiveSync/SOGoActiveSyncDispatcher+Sync.m @@ -46,13 +46,12 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #import #import - +#import #import #import #import #import - #import #import @@ -107,46 +106,6 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. @implementation SOGoActiveSyncDispatcher (Sync) -- (id) collectionFromId: (NSString *) theCollectionId - type: (SOGoMicrosoftActiveSyncFolderType) theFolderType -{ - id collection; - - collection = nil; - - switch (theFolderType) - { - case ActiveSyncContactFolder: - { - collection = [[context activeUser] personalContactsFolderInContext: context]; - } - break; - case ActiveSyncEventFolder: - case ActiveSyncTaskFolder: - { - collection = [[context activeUser] personalCalendarFolderInContext: context]; - } - break; - case ActiveSyncMailFolder: - default: - { - SOGoMailAccounts *accountsFolder; - SOGoMailFolder *currentFolder; - SOGoUserFolder *userFolder; - - userFolder = [[context activeUser] homeFolderInContext: context]; - accountsFolder = [userFolder lookupName: @"Mail" inContext: context acquire: NO]; - currentFolder = [accountsFolder lookupName: @"0" inContext: context acquire: NO]; - - collection = [currentFolder lookupName: [NSString stringWithFormat: @"folder%@", theCollectionId] - inContext: context - acquire: NO]; - } - } - - return collection; -} - // // // diff --git a/ActiveSync/SOGoActiveSyncDispatcher.h b/ActiveSync/SOGoActiveSyncDispatcher.h index 44302d8c6..0c28ebced 100644 --- a/ActiveSync/SOGoActiveSyncDispatcher.h +++ b/ActiveSync/SOGoActiveSyncDispatcher.h @@ -29,6 +29,8 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #import +#include "SOGoActiveSyncConstants.h" + @class NSException; @interface SOGoActiveSyncDispatcher : NSObject @@ -36,6 +38,9 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. id context; } +- (id) collectionFromId: (NSString *) theCollectionId + type: (SOGoMicrosoftActiveSyncFolderType) theFolderType; + - (NSException *) dispatchRequest: (id) theRequest inResponse: (id) theResponse context: (id) theContext; diff --git a/ActiveSync/SOGoActiveSyncDispatcher.m b/ActiveSync/SOGoActiveSyncDispatcher.m index 99c09b2d6..a87c9ddb6 100644 --- a/ActiveSync/SOGoActiveSyncDispatcher.m +++ b/ActiveSync/SOGoActiveSyncDispatcher.m @@ -45,7 +45,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #import #import - +#import #import #import #import @@ -85,6 +85,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #import #import +#import #import #import @@ -127,6 +128,49 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. [[[context activeUser] userSettings] synchronize]; } +// +// +// +- (id) collectionFromId: (NSString *) theCollectionId + type: (SOGoMicrosoftActiveSyncFolderType) theFolderType +{ + id collection; + + collection = nil; + + switch (theFolderType) + { + case ActiveSyncContactFolder: + { + collection = [[context activeUser] personalContactsFolderInContext: context]; + } + break; + case ActiveSyncEventFolder: + case ActiveSyncTaskFolder: + { + collection = [[context activeUser] personalCalendarFolderInContext: context]; + } + break; + case ActiveSyncMailFolder: + default: + { + SOGoMailAccounts *accountsFolder; + SOGoMailFolder *currentFolder; + SOGoUserFolder *userFolder; + + userFolder = [[context activeUser] homeFolderInContext: context]; + accountsFolder = [userFolder lookupName: @"Mail" inContext: context acquire: NO]; + currentFolder = [accountsFolder lookupName: @"0" inContext: context acquire: NO]; + + collection = [currentFolder lookupName: [NSString stringWithFormat: @"folder%@", theCollectionId] + inContext: context + acquire: NO]; + } + } + + return collection; +} + // // // @@ -524,11 +568,9 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. { EOQualifier *notDeletedQualifier, *sinceDateQualifier; NSString *collectionId, *realCollectionId; - id currentFolder, currentCollection; - SOGoMailAccounts *accountsFolder; - SOGoUserFolder *userFolder; EOAndQualifier *qualifier; NSCalendarDate *filter; + id currentCollection; NSMutableString *s; NSArray *uids; NSData *d; @@ -541,15 +583,8 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. collectionId = [[(id)[theDocumentElement getElementsByTagName: @"CollectionId"] lastObject] textValue]; realCollectionId = [collectionId realCollectionIdWithFolderType: &folderType]; - - - userFolder = [[context activeUser] homeFolderInContext: context]; - accountsFolder = [userFolder lookupName: @"Mail" inContext: context acquire: NO]; - currentFolder = [accountsFolder lookupName: @"0" inContext: context acquire: NO]; + currentCollection = [self collectionFromId: realCollectionId type: folderType]; - currentCollection = [currentFolder lookupName: [NSString stringWithFormat: @"folder%@", realCollectionId] - inContext: context - acquire: NO]; // // For IMAP, we simply build a request like this: // @@ -677,12 +712,91 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// +// +// +// +// +// 1 +// mail%2FINBOX +// 283 +// +// // - (void) processMeetingResponse: (id ) theDocumentElement inResponse: (WOResponse *) theResponse { + NSString *realCollectionId, *requestId, *participationStatus; + NSMutableString *s; + NSData *d; + id collection; + + SOGoMicrosoftActiveSyncFolderType folderType; + int userResponse; + int status; + + s = [NSMutableString string]; + status = 1; + + realCollectionId = [[[(id)[theDocumentElement getElementsByTagName: @"CollectionId"] lastObject] textValue] realCollectionIdWithFolderType: &folderType]; + collection = [self collectionFromId: realCollectionId type: ActiveSyncMailFolder]; + + // 1 -> accepted, 2 -> tentative, 3 -> declined + userResponse = [[[(id)[theDocumentElement getElementsByTagName: @"UserResponse"] lastObject] textValue] intValue]; + requestId = [[(id)[theDocumentElement getElementsByTagName: @"RequestId"] lastObject] textValue]; + + // + // We fetch the calendar information based on the email (requestId) in the user's INBOX (or elsewhere) + // + // FIXME: that won't work too well for external invitations... + SOGoMailObject *mailObject; + + mailObject = [collection lookupName: requestId + inContext: context + acquire: 0]; + + if (![mailObject isKindOfClass: [NSException class]]) + { + SOGoAppointmentObject *appointmentObject; + iCalCalendar *calendar; + iCalEvent *event; + + calendar = [mailObject calendarFromIMIPMessage]; + event = [[calendar events] lastObject]; + + // Fetch the SOGoAppointmentObject + collection = [[context activeUser] personalCalendarFolderInContext: context]; + appointmentObject = [collection lookupName: [NSString stringWithFormat: @"%@.ics", [event uid]] + inContext: context + acquire: NO]; + if (userResponse == 1) + participationStatus = @"ACCEPTED"; + else if (userResponse == 2) + participationStatus = @"TENTATIVE"; + else + participationStatus = @"DECLINED"; + + [appointmentObject changeParticipationStatus: participationStatus + withDelegate: nil]; + + [s appendString: @""]; + [s appendString: @""]; + [s appendString: @""]; + [s appendString: @""]; + [s appendFormat: @"%@", requestId]; + [s appendFormat: @"%@", [event uid]]; + [s appendFormat: @"%d", status]; + [s appendString: @""]; + [s appendString: @""]; + + d = [[s dataUsingEncoding: NSUTF8StringEncoding] xml2wbxml]; + + [theResponse setContent: d]; + } + else + { + [theResponse setStatus: 500]; + } } @@ -710,22 +824,21 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // FIXME if (srcFolderType == ActiveSyncMailFolder && dstFolderType == ActiveSyncMailFolder) { - SOGoMailAccounts *accountsFolder; - SOGoMailFolder *currentFolder; - SOGoUserFolder *userFolder; NGImap4Client *client; id currentCollection; NSDictionary *response; NSString *v; - userFolder = [[context activeUser] homeFolderInContext: context]; - accountsFolder = [userFolder lookupName: @"Mail" inContext: context acquire: NO]; - currentFolder = [accountsFolder lookupName: @"0" inContext: context acquire: NO]; + // userFolder = [[context activeUser] homeFolderInContext: context]; + // accountsFolder = [userFolder lookupName: @"Mail" inContext: context acquire: NO]; + // currentFolder = [accountsFolder lookupName: @"0" inContext: context acquire: NO]; - currentCollection = [currentFolder lookupName: [NSString stringWithFormat: @"folder%@", srcFolderId] - inContext: context - acquire: NO]; + currentCollection = [self collectionFromId: srcFolderId type: srcFolderType]; + + // [currentFolder lookupName: [NSString stringWithFormat: @"folder%@", srcFolderId] + // inContext: context + // acquire: NO]; client = [[currentCollection imap4Connection] client]; [client select: srcFolderId]; @@ -747,7 +860,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. addOrRemove: YES]; if ([[response valueForKey: @"result"] boolValue]) - [currentCollection expunge]; + [(SOGoMailFolder *)currentCollection expunge]; } diff --git a/ActiveSync/SOGoMailObject+ActiveSync.h b/ActiveSync/SOGoMailObject+ActiveSync.h index 17e03be6b..4f7d751dc 100644 --- a/ActiveSync/SOGoMailObject+ActiveSync.h +++ b/ActiveSync/SOGoMailObject+ActiveSync.h @@ -32,10 +32,12 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #import +@class iCalCalendar; @class NSDictionary; @interface SOGoMailObject (ActiveSync) +- (iCalCalendar *) calendarFromIMIPMessage; - (NSString *) activeSyncRepresentation; - (void) takeActiveSyncValues: (NSDictionary *) theValues; diff --git a/ActiveSync/SOGoMailObject+ActiveSync.m b/ActiveSync/SOGoMailObject+ActiveSync.m index 00121b4b1..cb0f001c3 100644 --- a/ActiveSync/SOGoMailObject+ActiveSync.m +++ b/ActiveSync/SOGoMailObject+ActiveSync.m @@ -38,7 +38,6 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #import #import - #import #import #import @@ -46,14 +45,118 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #import #import +#include "iCalTimeZone+ActiveSync.h" #include "NSDate+ActiveSync.h" +#include "NSString+ActiveSync.h" #include "../SoObjects/Mailer/NSString+Mail.h" +#include "../SoObjects/Mailer/SOGoMailBodyPart.h" + #include +typedef struct { + uint32_t dwLowDateTime; + uint32_t dwHighDateTime; +} FILETIME; + +struct GlobalObjectId { + uint8_t ByteArrayID[16]; + uint8_t YH; + uint8_t YL; + uint8_t Month; + uint8_t D; + FILETIME CreationTime; + uint8_t X[8]; + uint32_t Size; + uint8_t* Data; +}; @implementation SOGoMailObject (ActiveSync) +// +// +// +- (void) _setInstanceDate: (struct GlobalObjectId *) newGlobalId + fromDate: (NSCalendarDate *) instanceDate +{ + uint16_t year; + + if (instanceDate) + { + //[instanceDate setTimeZone: timeZone]; + year = [instanceDate yearOfCommonEra]; + newGlobalId->YH = year >> 8; + newGlobalId->YL = year & 0xff; + newGlobalId->Month = [instanceDate monthOfYear]; + newGlobalId->D = [instanceDate dayOfMonth]; + } +} + +// +// The GlobalObjId is documented here: http://msdn.microsoft.com/en-us/library/ee160198(v=EXCHG.80).aspx +// +- (NSData *) _computeGlobalObjectIdFromEvent: (iCalEvent *) event +{ + NSData *binPrefix, *globalObjectId; + NSString *prefix, *uid; + + struct GlobalObjectId newGlobalId; + const char *uidAsUTF8; + + prefix = @"040000008200e00074c5b7101a82e008"; + + // dataPrefix is "vCal-Uid %x01 %x00 %x00 %x00" + uint8_t dataPrefix[] = { 0x76, 0x43, 0x61, 0x6c, 0x2d, 0x55, 0x69, 0x64, 0x01, 0x00, 0x00, 0x00 }; + uid = [event uid]; + + binPrefix = [prefix convertHexStringToBytes]; + [binPrefix getBytes: &newGlobalId.ByteArrayID]; + [self _setInstanceDate: &newGlobalId + fromDate: [event recurrenceId]]; + uidAsUTF8 = [uid UTF8String]; + + // 0x0c is the size of our dataPrefix + newGlobalId.Size = 0x0c + strlen(uidAsUTF8); + newGlobalId.Data = malloc(newGlobalId.Size * sizeof(uint8_t)); + memcpy(newGlobalId.Data, dataPrefix, 0x0c); + memcpy(newGlobalId.Data + 0x0c, uidAsUTF8, newGlobalId.Size - 0x0c); + + globalObjectId = [[NSData alloc] initWithBytes: &newGlobalId length: 40 + newGlobalId.Size*sizeof(uint8_t)]; + free(newGlobalId.Data); + + return [globalObjectId autorelease]; +} + +// +// For debugging purposes... +// +// - (NSString *) _uidFromGlobalObjectId: (NSData *) objectId +// { +// NSString *uid; + +// struct GlobalObjectId *newGlobalId; +// NSUInteger length; +// uint8_t *bytes; + +// length = [objectId length]; +// uid = nil; + +// bytes = malloc(length*sizeof(uint8_t)); +// [objectId getBytes: bytes length: length]; + +// newGlobalId = bytes; + +// // We must take the offset (dataPrefix) into account +// uid = [[NSString alloc] initWithBytes: newGlobalId->Data+12 length: newGlobalId->Size-12 encoding: NSUTF8StringEncoding]; +// free(bytes); + +// return AUTORELEASE(uid); +// } + + +// +// +// - (NSString *) _emailAddressesFrom: (NSArray *) enveloppeAddresses { NSMutableArray *addresses; @@ -183,15 +286,57 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. return d; } +// +// +// +- (iCalCalendar *) calendarFromIMIPMessage +{ + NSDictionary *part; + NSArray *parts; + int i; + + // We check if we have at least 2 parts and if one of them is a text/calendar + parts = [[self bodyStructure] objectForKey: @"parts"]; + + if ([parts count] > 1) + { + for (i = 0; i < [parts count]; i++) + { + part = [parts objectAtIndex: i]; + + if ([[part objectForKey: @"type"] isEqualToString: @"text"] && + [[part objectForKey: @"subtype"] isEqualToString: @"calendar"]) + { + id bodyPart; + + bodyPart = [self lookupImap4BodyPartKey: [NSString stringWithFormat: @"%d", i+1] + inContext: self->context]; + + if (bodyPart) + { + NSData *calendarData; + + calendarData = [bodyPart fetchBLOB]; + return [iCalCalendar parseSingleFromSource: calendarData]; + } + } + } + } + + return nil; +} + // // // - (NSString *) activeSyncRepresentation { + NSData *d, *globalObjId; NSMutableString *s; - NSData *d; id value; + iCalCalendar *calendar; + int preferredBodyType, nativeBodyType; s = [NSMutableString string]; @@ -234,58 +379,63 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // Read [s appendFormat: @"%d", ([self read] ? 1 : 0)]; - // MesssageClass - [s appendFormat: @"%@", @"IPM.Note"]; - //[s appendFormat: @"%@", @"IPM.Schedule.Meeting.Request"]; - // - // TEST - // -#if 0 - id foo = [self lookupImap4BodyPartKey: @"2" inContext: self->context]; - NSData *calendarData; - iCalCalendar *calendar; - iCalEvent *event; - - calendarData = [foo fetchBLOB]; - calendar = [iCalCalendar parseSingleFromSource: calendarData]; - event = [[calendar events] lastObject]; - - if ([event timeStampAsDate]) - [s appendFormat: @"%@", [[event timeStampAsDate] activeSyncRepresentation]]; - else if ([event created]) - [s appendFormat: @"%@", [[event created] activeSyncRepresentation]]; - - [s appendString: @""]; - - // StartTime -- http://msdn.microsoft.com/en-us/library/ee157132(v=exchg.80).aspx - if ([event startDate]) - [s appendFormat: @"%@", [[event startDate] activeSyncRepresentation]]; - - // EndTime -- http://msdn.microsoft.com/en-us/library/ee157945(v=exchg.80).aspx - if ([event endDate]) - [s appendFormat: @"%@", [[event endDate] activeSyncRepresentation]]; - - // Timezone - iCalTimeZone *tz; - - tz = [(iCalDateTime *)[event firstChildWithTag: @"dtstart"] timeZone]; - - if (!tz) - tz = [iCalTimeZone timeZoneForName: @"Europe/London"]; - - [s appendFormat: @"%@", [[tz activeSyncRepresentation] stringByReplacingString: @"\n" withString: @""]];; - - [s appendFormat: @"%d", 0]; - [s appendFormat: @"%@", @"sogo3@example.com"]; - [s appendFormat: @"%d", 1]; - - [s appendString: @""]; -#endif - // - // TEST - // + // We handle MeetingRequest + calendar = [self calendarFromIMIPMessage]; + if (calendar) + { + iCalTimeZone *tz; + iCalEvent *event; + + event = [[calendar events] lastObject]; + + [s appendString: @""]; + + if ([event timeStampAsDate]) + [s appendFormat: @"%@", [[event timeStampAsDate] activeSyncRepresentationWithoutSeparators]]; + else if ([event created]) + [s appendFormat: @"%@", [[event created] activeSyncRepresentationWithoutSeparators]]; + + // StartTime -- http://msdn.microsoft.com/en-us/library/ee157132(v=exchg.80).aspx + if ([event startDate]) + [s appendFormat: @"%@", [[event startDate] activeSyncRepresentationWithoutSeparators]]; + + // EndTime -- http://msdn.microsoft.com/en-us/library/ee157945(v=exchg.80).aspx + if ([event endDate]) + [s appendFormat: @"%@", [[event endDate] activeSyncRepresentationWithoutSeparators]]; + + // Timezone + tz = [(iCalDateTime *)[event firstChildWithTag: @"dtstart"] timeZone]; + + if (!tz) + tz = [iCalTimeZone timeZoneForName: @"Europe/London"]; + + [s appendFormat: @"%@", [[tz activeSyncRepresentation] stringByReplacingString: @"\n" withString: @""]];; + + [s appendFormat: @"%d", 0]; + [s appendFormat: @"%@", [[event organizer] rfc822Email]]; + [s appendFormat: @"%d", 1]; + + // From http://blogs.msdn.com/b/exchangedev/archive/2011/07/22/working-with-meeting-requests-in-exchange-activesync.aspx: + // + // "Clients that need to determine whether the GlobalObjId element for a meeting request corresponds to an existing Calendar + // object in the Calendar folder have to convert the GlobalObjId element value to a UID element value to make the comparison." + // + globalObjId = [self _computeGlobalObjectIdFromEvent: event]; + [s appendFormat: @"%@", [globalObjId stringByEncodingBase64]]; + [s appendString: @""]; + + // MesssageClass and ContentClass + [s appendFormat: @"%@", @"IPM.Schedule.Meeting.Request"]; + [s appendFormat: @"%@", @"urn:content-classes:calendarmessage"]; + } + else + { + // MesssageClass and ContentClass + [s appendFormat: @"%@", @"IPM.Note"]; + [s appendFormat: @"%@", @"urn:content-classes:message"]; + } // Reply-To - FIXME //NSArray *replyTo = [[message objectForKey: @"envelope"] replyTo]; @@ -349,10 +499,6 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. [s appendString: @""]; } - // ContentClass - [s appendFormat: @"%@", @"urn:content-classes:message"]; - //[s appendFormat: @"%@", @"urn:content-classes:calendarmessage"]; - // Flags [s appendString: @""]; [s appendFormat: @"%d", 0]; diff --git a/ActiveSync/iCalEvent+ActiveSync.m b/ActiveSync/iCalEvent+ActiveSync.m index 66ec0b770..82767e915 100644 --- a/ActiveSync/iCalEvent+ActiveSync.m +++ b/ActiveSync/iCalEvent+ActiveSync.m @@ -204,6 +204,38 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // http://blogs.msdn.com/b/exchangedev/archive/2011/07/22/working-with-meeting-requests-in-exchange-activesync.aspx // http://blogs.msdn.com/b/exchangedev/archive/2011/07/29/working-with-meeting-responses-in-exchange-activesync.aspx // +// +// Here is an example of a Sync call when sogo10 accepts an invitation from sogo3: +// +// +// 2978-52EA9D00-1-A253E70.ics +// +// LAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAsAAAABAAIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAMAAAACAAIAAAAAAAAAxP///w== +// 0 +// 20140207T130000Z +// 20140207T140000Z +// 20140130T185245Z +// test 8 +// 0 +// +// 1 +// +// +// sogo3@example.com +// 2978-52EA9D00-1-A253E70 +// +// +// sogo10 +// sogo10@example.com +// 1 +// +// +// 2 +// 3 +// Wolfgang Fritz +// +// +// - (void) takeActiveSyncValues: (NSDictionary *) theValues { iCalDateTime *start, *end; @@ -331,6 +363,66 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. [rule takeActiveSyncValues: o]; } + + // Organizer + if ((o = [theValues objectForKey: @"Organizer_Email"])) + { + iCalPerson *person; + + person = [iCalPerson elementWithTag: @"organizer"]; + [person setEmail: o]; + [person setCn: [theValues objectForKey: @"Organizer_Name"]]; + [person setPartStat: @"ACCEPTED"]; + [self setOrganizer: person]; + } + + // Attendees + if ((o = [theValues objectForKey: @"Attendees"])) + { + NSMutableArray *attendees; + NSDictionary *attendee; + iCalPerson *person; + int status, i; + + attendees = [NSMutableArray array]; + + for (i = 0; i < [o count]; i++) + { + // Each attendee has is a dictionary similar to this: + // { "Attendee_Email" = "sogo3@example.com"; "Attendee_Name" = "Wolfgang Fritz"; "Attendee_Status" = 5; "Attendee_Type" = 1; } + attendee = [o objectAtIndex: i]; + + person = [iCalPerson elementWithTag: @"attendee"]; + [person setCn: [attendee objectForKey: @"Attendee_Name"]]; + [person setEmail: [attendee objectForKey: @"Attendee_Email"]]; + + status = [[attendee objectForKey: @"Attendee_Status"] intValue]; + + switch (status) + { + case 2: + [person setPartStat: @"TENTATIVE"]; + break; + case 3: + [person setPartStat: @"ACCEPTED"]; + break; + case 4: + [person setPartStat: @"DECLINED"]; + break; + case 0: + case 5: + default: + [person setPartStat: @"NEEDS-ACTION"]; + break; + } + + // FIXME: handle Attendee_Type + + [attendees addObject: person]; + } + + [self setAttendees: attendees]; + } } @end diff --git a/ActiveSync/iCalTimeZone+ActiveSync.m b/ActiveSync/iCalTimeZone+ActiveSync.m index d9c1f2fc6..7b7ceefa0 100644 --- a/ActiveSync/iCalTimeZone+ActiveSync.m +++ b/ActiveSync/iCalTimeZone+ActiveSync.m @@ -42,14 +42,14 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #import struct SYSTEMTIME { - uint16_t wYear; - uint16_t wMonth; - uint16_t wDayOfWeek; - uint16_t wDay; - uint16_t wHour; - uint16_t wMinute; - uint16_t wSecond; - uint16_t wMilliseconds; + uint16_t wYear; + uint16_t wMonth; + uint16_t wDayOfWeek; + uint16_t wDay; + uint16_t wHour; + uint16_t wMinute; + uint16_t wSecond; + uint16_t wMilliseconds; }; @interface iCalTimeZonePeriod (ActiveSync)