First pass at event invitations support + few bug fixes.
parent
d709a1216b
commit
9e9407cf30
|
@ -31,6 +31,9 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
|
||||||
#import <Foundation/NSArray.h>
|
#import <Foundation/NSArray.h>
|
||||||
#import <Foundation/NSDictionary.h>
|
#import <Foundation/NSDictionary.h>
|
||||||
|
#import <Foundation/NSString.h>
|
||||||
|
|
||||||
|
static NSArray *asElementArray = nil;
|
||||||
|
|
||||||
@implementation NGDOMElement (ActiveSync)
|
@implementation NGDOMElement (ActiveSync)
|
||||||
|
|
||||||
|
@ -44,13 +47,40 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
// </Flag>
|
// </Flag>
|
||||||
// </ApplicationData>
|
// </ApplicationData>
|
||||||
//
|
//
|
||||||
|
// and stuff like that:
|
||||||
|
//
|
||||||
|
// <Attendees xmlns="Calendar:">
|
||||||
|
// <Attendee>
|
||||||
|
// <Attendee_Email>sogo1@example.com</Attendee_Email>
|
||||||
|
// <Attendee_Name>John Doe</Attendee_Name>
|
||||||
|
// <Attendee_Status>5</Attendee_Status>
|
||||||
|
// <Attendee_Type>1</Attendee_Type>
|
||||||
|
// </Attendee>
|
||||||
|
// <Attendee>
|
||||||
|
// <Attendee_Email>sogo2@example.com</Attendee_Email>
|
||||||
|
// <Attendee_Name>Balthazar César</Attendee_Name>
|
||||||
|
// <Attendee_Status>5</Attendee_Status>
|
||||||
|
// <Attendee_Type>1</Attendee_Type>
|
||||||
|
// </Attendee>
|
||||||
|
// <Attendee>
|
||||||
|
// <Attendee_Email>sogo3@example.com</Attendee_Email>
|
||||||
|
// <Attendee_Name>Wolfgang Fritz</Attendee_Name>
|
||||||
|
// <Attendee_Status>5</Attendee_Status>
|
||||||
|
// <Attendee_Type>1</Attendee_Type>
|
||||||
|
// </Attendee>
|
||||||
|
// </Attendees>
|
||||||
|
//
|
||||||
|
|
||||||
- (NSDictionary *) applicationData
|
- (NSDictionary *) applicationData
|
||||||
{
|
{
|
||||||
NSMutableDictionary *data;
|
NSMutableDictionary *data;
|
||||||
id <DOMNodeList> children;
|
id <DOMNodeList> children;
|
||||||
id <DOMElement> element;
|
id <DOMElement> element;
|
||||||
int i;
|
int i, count;
|
||||||
|
|
||||||
|
if (!asElementArray)
|
||||||
|
asElementArray = [[NSArray alloc] initWithObjects: @"Attendee", nil];
|
||||||
|
|
||||||
data = [NSMutableDictionary dictionary];
|
data = [NSMutableDictionary dictionary];
|
||||||
|
|
||||||
children = [self childNodes];
|
children = [self childNodes];
|
||||||
|
@ -65,10 +95,55 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
id value;
|
id value;
|
||||||
|
|
||||||
tag = [element tagName];
|
tag = [element tagName];
|
||||||
|
count = [(NSArray *)[element childNodes] count];
|
||||||
|
|
||||||
// Handle inner data
|
// Handle inner data - see above for samples
|
||||||
if ([(NSArray *)[element childNodes] count] > 2)
|
if (count > 2)
|
||||||
value = [(NGDOMElement *)element applicationData];
|
{
|
||||||
|
NSMutableArray *innerElements;
|
||||||
|
id <DOMElement> 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
|
else
|
||||||
value = [[element firstChild] nodeValue];
|
value = [[element firstChild] nodeValue];
|
||||||
|
|
||||||
|
|
|
@ -35,6 +35,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
#include "SOGoActiveSyncConstants.h"
|
#include "SOGoActiveSyncConstants.h"
|
||||||
|
|
||||||
@class NSCalendarDate;
|
@class NSCalendarDate;
|
||||||
|
@class NSData;
|
||||||
|
|
||||||
@interface NSString (ActiveSync)
|
@interface NSString (ActiveSync)
|
||||||
|
|
||||||
|
@ -43,6 +44,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
- (NSCalendarDate *) calendarDate;
|
- (NSCalendarDate *) calendarDate;
|
||||||
- (NSString *) deviceId;
|
- (NSString *) deviceId;
|
||||||
- (NSString *) command;
|
- (NSString *) command;
|
||||||
|
- (NSData *) convertHexStringToBytes;
|
||||||
|
|
||||||
@end
|
@end
|
||||||
|
|
||||||
|
|
|
@ -31,6 +31,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
|
||||||
#include <Foundation/NSArray.h>
|
#include <Foundation/NSArray.h>
|
||||||
#include <Foundation/NSCalendarDate.h>
|
#include <Foundation/NSCalendarDate.h>
|
||||||
|
#include <Foundation/NSData.h>
|
||||||
#include <Foundation/NSDate.h>
|
#include <Foundation/NSDate.h>
|
||||||
|
|
||||||
#include <NGExtensions/NSString+misc.h>
|
#include <NGExtensions/NSString+misc.h>
|
||||||
|
@ -53,30 +54,34 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
|
||||||
- (NSString *) realCollectionIdWithFolderType: (SOGoMicrosoftActiveSyncFolderType *) folderType;
|
- (NSString *) realCollectionIdWithFolderType: (SOGoMicrosoftActiveSyncFolderType *) folderType;
|
||||||
{
|
{
|
||||||
NSString *realCollectionId;
|
NSString *realCollectionId, *v;
|
||||||
|
|
||||||
*folderType = ActiveSyncGenericFolder;
|
*folderType = ActiveSyncGenericFolder;
|
||||||
|
v = [self stringByUnescapingURL];
|
||||||
|
|
||||||
if ([self hasPrefix: @"vevent/"])
|
if ([v hasPrefix: @"vevent/"])
|
||||||
{
|
{
|
||||||
realCollectionId = [self substringFromIndex: 7];
|
realCollectionId = [v substringFromIndex: 7];
|
||||||
*folderType = ActiveSyncEventFolder;
|
*folderType = ActiveSyncEventFolder;
|
||||||
}
|
}
|
||||||
else if ([self hasPrefix: @"vtodo/"])
|
else if ([v hasPrefix: @"vtodo/"])
|
||||||
{
|
{
|
||||||
realCollectionId = [self substringFromIndex: 6];
|
realCollectionId = [v substringFromIndex: 6];
|
||||||
*folderType = ActiveSyncTaskFolder;
|
*folderType = ActiveSyncTaskFolder;
|
||||||
}
|
}
|
||||||
else if ([self hasPrefix: @"vcard/"])
|
else if ([v hasPrefix: @"vcard/"])
|
||||||
{
|
{
|
||||||
realCollectionId = [self substringFromIndex: 6];
|
realCollectionId = [v substringFromIndex: 6];
|
||||||
*folderType = ActiveSyncContactFolder;
|
*folderType = ActiveSyncContactFolder;
|
||||||
}
|
}
|
||||||
|
else if ([v hasPrefix: @"mail/"])
|
||||||
|
{
|
||||||
|
realCollectionId = [[v stringByUnescapingURL] substringFromIndex: 5];
|
||||||
|
*folderType = ActiveSyncMailFolder;
|
||||||
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
// mail/
|
realCollectionId = nil;
|
||||||
realCollectionId = [[self stringByUnescapingURL] substringFromIndex: 5];
|
|
||||||
*folderType = ActiveSyncMailFolder;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return realCollectionId;
|
return realCollectionId;
|
||||||
|
@ -150,4 +155,100 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
return s;
|
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
|
@end
|
||||||
|
|
|
@ -46,13 +46,12 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
#import <NGObjWeb/WORequest.h>
|
#import <NGObjWeb/WORequest.h>
|
||||||
#import <NGObjWeb/WOResponse.h>
|
#import <NGObjWeb/WOResponse.h>
|
||||||
|
|
||||||
|
#import <NGCards/iCalCalendar.h>
|
||||||
#import <NGCards/iCalEntityObject.h>
|
#import <NGCards/iCalEntityObject.h>
|
||||||
#import <NGCards/iCalEvent.h>
|
#import <NGCards/iCalEvent.h>
|
||||||
#import <NGCards/iCalToDo.h>
|
#import <NGCards/iCalToDo.h>
|
||||||
#import <NGCards/NGVCard.h>
|
#import <NGCards/NGVCard.h>
|
||||||
|
|
||||||
|
|
||||||
#import <NGExtensions/NSCalendarDate+misc.h>
|
#import <NGExtensions/NSCalendarDate+misc.h>
|
||||||
#import <NGExtensions/NSString+misc.h>
|
#import <NGExtensions/NSString+misc.h>
|
||||||
|
|
||||||
|
@ -107,46 +106,6 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
|
||||||
@implementation SOGoActiveSyncDispatcher (Sync)
|
@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;
|
|
||||||
}
|
|
||||||
|
|
||||||
//
|
//
|
||||||
// <?xml version="1.0"?>
|
// <?xml version="1.0"?>
|
||||||
// <!DOCTYPE ActiveSync PUBLIC "-//MICROSOFT//DTD ActiveSync//EN" "http://www.microsoft.com/">
|
// <!DOCTYPE ActiveSync PUBLIC "-//MICROSOFT//DTD ActiveSync//EN" "http://www.microsoft.com/">
|
||||||
|
|
|
@ -29,6 +29,8 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
*/
|
*/
|
||||||
#import <Foundation/NSObject.h>
|
#import <Foundation/NSObject.h>
|
||||||
|
|
||||||
|
#include "SOGoActiveSyncConstants.h"
|
||||||
|
|
||||||
@class NSException;
|
@class NSException;
|
||||||
|
|
||||||
@interface SOGoActiveSyncDispatcher : NSObject
|
@interface SOGoActiveSyncDispatcher : NSObject
|
||||||
|
@ -36,6 +38,9 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
id context;
|
id context;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
- (id) collectionFromId: (NSString *) theCollectionId
|
||||||
|
type: (SOGoMicrosoftActiveSyncFolderType) theFolderType;
|
||||||
|
|
||||||
- (NSException *) dispatchRequest: (id) theRequest
|
- (NSException *) dispatchRequest: (id) theRequest
|
||||||
inResponse: (id) theResponse
|
inResponse: (id) theResponse
|
||||||
context: (id) theContext;
|
context: (id) theContext;
|
||||||
|
|
|
@ -45,7 +45,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
#import <NGObjWeb/WORequest.h>
|
#import <NGObjWeb/WORequest.h>
|
||||||
#import <NGObjWeb/WOResponse.h>
|
#import <NGObjWeb/WOResponse.h>
|
||||||
|
|
||||||
|
#import <NGCards/iCalCalendar.h>
|
||||||
#import <NGCards/iCalEntityObject.h>
|
#import <NGCards/iCalEntityObject.h>
|
||||||
#import <NGCards/iCalEvent.h>
|
#import <NGCards/iCalEvent.h>
|
||||||
#import <NGCards/iCalToDo.h>
|
#import <NGCards/iCalToDo.h>
|
||||||
|
@ -85,6 +85,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
|
||||||
#import <Appointments/SOGoAppointmentFolder.h>
|
#import <Appointments/SOGoAppointmentFolder.h>
|
||||||
#import <Appointments/SOGoAppointmentFolders.h>
|
#import <Appointments/SOGoAppointmentFolders.h>
|
||||||
|
#import <Appointments/SOGoAppointmentObject.h>
|
||||||
|
|
||||||
#import <Contacts/SOGoContactGCSFolder.h>
|
#import <Contacts/SOGoContactGCSFolder.h>
|
||||||
#import <Contacts/SOGoContactFolders.h>
|
#import <Contacts/SOGoContactFolders.h>
|
||||||
|
@ -127,6 +128,49 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
[[[context activeUser] userSettings] synchronize];
|
[[[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;
|
EOQualifier *notDeletedQualifier, *sinceDateQualifier;
|
||||||
NSString *collectionId, *realCollectionId;
|
NSString *collectionId, *realCollectionId;
|
||||||
id currentFolder, currentCollection;
|
|
||||||
SOGoMailAccounts *accountsFolder;
|
|
||||||
SOGoUserFolder *userFolder;
|
|
||||||
EOAndQualifier *qualifier;
|
EOAndQualifier *qualifier;
|
||||||
NSCalendarDate *filter;
|
NSCalendarDate *filter;
|
||||||
|
id currentCollection;
|
||||||
NSMutableString *s;
|
NSMutableString *s;
|
||||||
NSArray *uids;
|
NSArray *uids;
|
||||||
NSData *d;
|
NSData *d;
|
||||||
|
@ -541,15 +583,8 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
|
||||||
collectionId = [[(id)[theDocumentElement getElementsByTagName: @"CollectionId"] lastObject] textValue];
|
collectionId = [[(id)[theDocumentElement getElementsByTagName: @"CollectionId"] lastObject] textValue];
|
||||||
realCollectionId = [collectionId realCollectionIdWithFolderType: &folderType];
|
realCollectionId = [collectionId realCollectionIdWithFolderType: &folderType];
|
||||||
|
currentCollection = [self collectionFromId: realCollectionId type: folderType];
|
||||||
|
|
||||||
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%@", realCollectionId]
|
|
||||||
inContext: context
|
|
||||||
acquire: NO];
|
|
||||||
//
|
//
|
||||||
// For IMAP, we simply build a request like this:
|
// For IMAP, we simply build a request like this:
|
||||||
//
|
//
|
||||||
|
@ -677,12 +712,91 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
|
||||||
|
|
||||||
//
|
//
|
||||||
//
|
// <?xml version="1.0"?>
|
||||||
|
// <!DOCTYPE ActiveSync PUBLIC "-//MICROSOFT//DTD ActiveSync//EN" "http://www.microsoft.com/">
|
||||||
|
// <MeetingResponse xmlns="MeetingResponse:">
|
||||||
|
// <Request>
|
||||||
|
// <UserResponse>1</UserResponse>
|
||||||
|
// <CollectionId>mail%2FINBOX</CollectionId>
|
||||||
|
// <RequestId>283</RequestId>
|
||||||
|
// </Request>
|
||||||
|
// </MeetingResponse>
|
||||||
//
|
//
|
||||||
- (void) processMeetingResponse: (id <DOMElement>) theDocumentElement
|
- (void) processMeetingResponse: (id <DOMElement>) theDocumentElement
|
||||||
inResponse: (WOResponse *) theResponse
|
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: @"<?xml version=\"1.0\" encoding=\"utf-8\"?>"];
|
||||||
|
[s appendString: @"<!DOCTYPE ActiveSync PUBLIC \"-//MICROSOFT//DTD ActiveSync//EN\" \"http://www.microsoft.com/\">"];
|
||||||
|
[s appendString: @"<MeetingResponse xmlns=\"MeetingResponse:\">"];
|
||||||
|
[s appendString: @"<Result>"];
|
||||||
|
[s appendFormat: @"<RequestId>%@</RequestId>", requestId];
|
||||||
|
[s appendFormat: @"<CalendarId>%@</CalendarId>", [event uid]];
|
||||||
|
[s appendFormat: @"<Status>%d</Status>", status];
|
||||||
|
[s appendString: @"</Result>"];
|
||||||
|
[s appendString: @"</MeetingResponse>"];
|
||||||
|
|
||||||
|
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
|
// FIXME
|
||||||
if (srcFolderType == ActiveSyncMailFolder && dstFolderType == ActiveSyncMailFolder)
|
if (srcFolderType == ActiveSyncMailFolder && dstFolderType == ActiveSyncMailFolder)
|
||||||
{
|
{
|
||||||
SOGoMailAccounts *accountsFolder;
|
|
||||||
SOGoMailFolder *currentFolder;
|
|
||||||
SOGoUserFolder *userFolder;
|
|
||||||
NGImap4Client *client;
|
NGImap4Client *client;
|
||||||
id currentCollection;
|
id currentCollection;
|
||||||
|
|
||||||
NSDictionary *response;
|
NSDictionary *response;
|
||||||
NSString *v;
|
NSString *v;
|
||||||
|
|
||||||
userFolder = [[context activeUser] homeFolderInContext: context];
|
// userFolder = [[context activeUser] homeFolderInContext: context];
|
||||||
accountsFolder = [userFolder lookupName: @"Mail" inContext: context acquire: NO];
|
// accountsFolder = [userFolder lookupName: @"Mail" inContext: context acquire: NO];
|
||||||
currentFolder = [accountsFolder lookupName: @"0" inContext: context acquire: NO];
|
// currentFolder = [accountsFolder lookupName: @"0" inContext: context acquire: NO];
|
||||||
|
|
||||||
currentCollection = [currentFolder lookupName: [NSString stringWithFormat: @"folder%@", srcFolderId]
|
currentCollection = [self collectionFromId: srcFolderId type: srcFolderType];
|
||||||
inContext: context
|
|
||||||
acquire: NO];
|
// [currentFolder lookupName: [NSString stringWithFormat: @"folder%@", srcFolderId]
|
||||||
|
// inContext: context
|
||||||
|
// acquire: NO];
|
||||||
|
|
||||||
client = [[currentCollection imap4Connection] client];
|
client = [[currentCollection imap4Connection] client];
|
||||||
[client select: srcFolderId];
|
[client select: srcFolderId];
|
||||||
|
@ -747,7 +860,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
addOrRemove: YES];
|
addOrRemove: YES];
|
||||||
|
|
||||||
if ([[response valueForKey: @"result"] boolValue])
|
if ([[response valueForKey: @"result"] boolValue])
|
||||||
[currentCollection expunge];
|
[(SOGoMailFolder *)currentCollection expunge];
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -32,10 +32,12 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
|
||||||
#import <Mailer/SOGoMailObject.h>
|
#import <Mailer/SOGoMailObject.h>
|
||||||
|
|
||||||
|
@class iCalCalendar;
|
||||||
@class NSDictionary;
|
@class NSDictionary;
|
||||||
|
|
||||||
@interface SOGoMailObject (ActiveSync)
|
@interface SOGoMailObject (ActiveSync)
|
||||||
|
|
||||||
|
- (iCalCalendar *) calendarFromIMIPMessage;
|
||||||
- (NSString *) activeSyncRepresentation;
|
- (NSString *) activeSyncRepresentation;
|
||||||
- (void) takeActiveSyncValues: (NSDictionary *) theValues;
|
- (void) takeActiveSyncValues: (NSDictionary *) theValues;
|
||||||
|
|
||||||
|
|
|
@ -38,7 +38,6 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
#import <NGCards/iCalEvent.h>
|
#import <NGCards/iCalEvent.h>
|
||||||
#import <NGCards/iCalTimeZone.h>
|
#import <NGCards/iCalTimeZone.h>
|
||||||
|
|
||||||
|
|
||||||
#import <NGExtensions/NGBase64Coding.h>
|
#import <NGExtensions/NGBase64Coding.h>
|
||||||
#import <NGExtensions/NSString+misc.h>
|
#import <NGExtensions/NSString+misc.h>
|
||||||
#import <NGExtensions/NSString+Encoding.h>
|
#import <NGExtensions/NSString+Encoding.h>
|
||||||
|
@ -46,14 +45,118 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
#import <NGImap4/NGImap4EnvelopeAddress.h>
|
#import <NGImap4/NGImap4EnvelopeAddress.h>
|
||||||
#import <NGObjWeb/WOContext+SoObjects.h>
|
#import <NGObjWeb/WOContext+SoObjects.h>
|
||||||
|
|
||||||
|
#include "iCalTimeZone+ActiveSync.h"
|
||||||
#include "NSDate+ActiveSync.h"
|
#include "NSDate+ActiveSync.h"
|
||||||
|
#include "NSString+ActiveSync.h"
|
||||||
|
|
||||||
#include "../SoObjects/Mailer/NSString+Mail.h"
|
#include "../SoObjects/Mailer/NSString+Mail.h"
|
||||||
|
#include "../SoObjects/Mailer/SOGoMailBodyPart.h"
|
||||||
|
|
||||||
#include <SOGo/SOGoUser.h>
|
#include <SOGo/SOGoUser.h>
|
||||||
|
|
||||||
|
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)
|
@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
|
- (NSString *) _emailAddressesFrom: (NSArray *) enveloppeAddresses
|
||||||
{
|
{
|
||||||
NSMutableArray *addresses;
|
NSMutableArray *addresses;
|
||||||
|
@ -183,15 +286,57 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
return d;
|
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
|
- (NSString *) activeSyncRepresentation
|
||||||
{
|
{
|
||||||
|
NSData *d, *globalObjId;
|
||||||
NSMutableString *s;
|
NSMutableString *s;
|
||||||
NSData *d;
|
|
||||||
id value;
|
id value;
|
||||||
|
|
||||||
|
iCalCalendar *calendar;
|
||||||
|
|
||||||
int preferredBodyType, nativeBodyType;
|
int preferredBodyType, nativeBodyType;
|
||||||
|
|
||||||
s = [NSMutableString string];
|
s = [NSMutableString string];
|
||||||
|
@ -234,58 +379,63 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
// Read
|
// Read
|
||||||
[s appendFormat: @"<Read xmlns=\"Email:\">%d</Read>", ([self read] ? 1 : 0)];
|
[s appendFormat: @"<Read xmlns=\"Email:\">%d</Read>", ([self read] ? 1 : 0)];
|
||||||
|
|
||||||
// MesssageClass
|
|
||||||
[s appendFormat: @"<MessageClass xmlns=\"Email:\">%@</MessageClass>", @"IPM.Note"];
|
|
||||||
//[s appendFormat: @"<MessageClass xmlns=\"Email:\">%@</MessageClass>", @"IPM.Schedule.Meeting.Request"];
|
|
||||||
|
|
||||||
//
|
// We handle MeetingRequest
|
||||||
// TEST
|
calendar = [self calendarFromIMIPMessage];
|
||||||
//
|
|
||||||
#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: @"<DTStamp xmlns=\"Calendar:\">%@</DTStamp>", [[event timeStampAsDate] activeSyncRepresentation]];
|
|
||||||
else if ([event created])
|
|
||||||
[s appendFormat: @"<DTStamp xmlns=\"Calendar:\">%@</DTStamp>", [[event created] activeSyncRepresentation]];
|
|
||||||
|
|
||||||
[s appendString: @"<MeetingRequest xmlns=\"Email:\">"];
|
|
||||||
|
|
||||||
// StartTime -- http://msdn.microsoft.com/en-us/library/ee157132(v=exchg.80).aspx
|
|
||||||
if ([event startDate])
|
|
||||||
[s appendFormat: @"<StartTime xmlns=\"Email:\">%@</StartTime>", [[event startDate] activeSyncRepresentation]];
|
|
||||||
|
|
||||||
// EndTime -- http://msdn.microsoft.com/en-us/library/ee157945(v=exchg.80).aspx
|
|
||||||
if ([event endDate])
|
|
||||||
[s appendFormat: @"<EndTime xmlns=\"Email:\">%@</EndTime>", [[event endDate] activeSyncRepresentation]];
|
|
||||||
|
|
||||||
// Timezone
|
|
||||||
iCalTimeZone *tz;
|
|
||||||
|
|
||||||
tz = [(iCalDateTime *)[event firstChildWithTag: @"dtstart"] timeZone];
|
|
||||||
|
|
||||||
if (!tz)
|
|
||||||
tz = [iCalTimeZone timeZoneForName: @"Europe/London"];
|
|
||||||
|
|
||||||
[s appendFormat: @"<TimeZone xmlns=\"Email:\">%@</TimeZone>", [[tz activeSyncRepresentation] stringByReplacingString: @"\n" withString: @""]];;
|
|
||||||
|
|
||||||
[s appendFormat: @"<InstanceType xmlns=\"Email:\">%d</InstanceType>", 0];
|
|
||||||
[s appendFormat: @"<Organizer xmlns=\"Email:\">%@</Organizer>", @"sogo3@example.com"];
|
|
||||||
[s appendFormat: @"<ResponseRequested xmlns=\"Email:\">%d</ResponseRequested>", 1];
|
|
||||||
|
|
||||||
[s appendString: @"</MeetingRequest>"];
|
|
||||||
#endif
|
|
||||||
//
|
|
||||||
// TEST
|
|
||||||
//
|
|
||||||
|
|
||||||
|
if (calendar)
|
||||||
|
{
|
||||||
|
iCalTimeZone *tz;
|
||||||
|
iCalEvent *event;
|
||||||
|
|
||||||
|
event = [[calendar events] lastObject];
|
||||||
|
|
||||||
|
[s appendString: @"<MeetingRequest xmlns=\"Email:\">"];
|
||||||
|
|
||||||
|
if ([event timeStampAsDate])
|
||||||
|
[s appendFormat: @"<DTStamp xmlns=\"Email:\">%@</DTStamp>", [[event timeStampAsDate] activeSyncRepresentationWithoutSeparators]];
|
||||||
|
else if ([event created])
|
||||||
|
[s appendFormat: @"<DTStamp xmlns=\"Email:\">%@</DTStamp>", [[event created] activeSyncRepresentationWithoutSeparators]];
|
||||||
|
|
||||||
|
// StartTime -- http://msdn.microsoft.com/en-us/library/ee157132(v=exchg.80).aspx
|
||||||
|
if ([event startDate])
|
||||||
|
[s appendFormat: @"<StartTime xmlns=\"Email:\">%@</StartTime>", [[event startDate] activeSyncRepresentationWithoutSeparators]];
|
||||||
|
|
||||||
|
// EndTime -- http://msdn.microsoft.com/en-us/library/ee157945(v=exchg.80).aspx
|
||||||
|
if ([event endDate])
|
||||||
|
[s appendFormat: @"<EndTime xmlns=\"Email:\">%@</EndTime>", [[event endDate] activeSyncRepresentationWithoutSeparators]];
|
||||||
|
|
||||||
|
// Timezone
|
||||||
|
tz = [(iCalDateTime *)[event firstChildWithTag: @"dtstart"] timeZone];
|
||||||
|
|
||||||
|
if (!tz)
|
||||||
|
tz = [iCalTimeZone timeZoneForName: @"Europe/London"];
|
||||||
|
|
||||||
|
[s appendFormat: @"<TimeZone xmlns=\"Email:\">%@</TimeZone>", [[tz activeSyncRepresentation] stringByReplacingString: @"\n" withString: @""]];;
|
||||||
|
|
||||||
|
[s appendFormat: @"<InstanceType xmlns=\"Email:\">%d</InstanceType>", 0];
|
||||||
|
[s appendFormat: @"<Organizer xmlns=\"Email:\">%@</Organizer>", [[event organizer] rfc822Email]];
|
||||||
|
[s appendFormat: @"<ResponseRequested xmlns=\"Email:\">%d</ResponseRequested>", 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 xmlns=\"Email:\">%@</GlobalObjId>", [globalObjId stringByEncodingBase64]];
|
||||||
|
[s appendString: @"</MeetingRequest>"];
|
||||||
|
|
||||||
|
// MesssageClass and ContentClass
|
||||||
|
[s appendFormat: @"<MessageClass xmlns=\"Email:\">%@</MessageClass>", @"IPM.Schedule.Meeting.Request"];
|
||||||
|
[s appendFormat: @"<ContentClass xmlns=\"Email:\">%@</ContentClass>", @"urn:content-classes:calendarmessage"];
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// MesssageClass and ContentClass
|
||||||
|
[s appendFormat: @"<MessageClass xmlns=\"Email:\">%@</MessageClass>", @"IPM.Note"];
|
||||||
|
[s appendFormat: @"<ContentClass xmlns=\"Email:\">%@</ContentClass>", @"urn:content-classes:message"];
|
||||||
|
}
|
||||||
|
|
||||||
// Reply-To - FIXME
|
// Reply-To - FIXME
|
||||||
//NSArray *replyTo = [[message objectForKey: @"envelope"] replyTo];
|
//NSArray *replyTo = [[message objectForKey: @"envelope"] replyTo];
|
||||||
|
@ -349,10 +499,6 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
[s appendString: @"</Attachments>"];
|
[s appendString: @"</Attachments>"];
|
||||||
}
|
}
|
||||||
|
|
||||||
// ContentClass
|
|
||||||
[s appendFormat: @"<ContentClass xmlns=\"Email:\">%@</ContentClass>", @"urn:content-classes:message"];
|
|
||||||
//[s appendFormat: @"<ContentClass xmlns=\"Email:\">%@</ContentClass>", @"urn:content-classes:calendarmessage"];
|
|
||||||
|
|
||||||
// Flags
|
// Flags
|
||||||
[s appendString: @"<Flag xmlns=\"Email:\">"];
|
[s appendString: @"<Flag xmlns=\"Email:\">"];
|
||||||
[s appendFormat: @"<FlagStatus>%d</FlagStatus>", 0];
|
[s appendFormat: @"<FlagStatus>%d</FlagStatus>", 0];
|
||||||
|
|
|
@ -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/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
|
// 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:
|
||||||
|
//
|
||||||
|
// <Change>
|
||||||
|
// <ServerId>2978-52EA9D00-1-A253E70.ics</ServerId>
|
||||||
|
// <ApplicationData>
|
||||||
|
// <TimeZone xmlns="Calendar:">LAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAsAAAABAAIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAMAAAACAAIAAAAAAAAAxP///w==</TimeZone>
|
||||||
|
// <AllDayEvent xmlns="Calendar:">0</AllDayEvent>
|
||||||
|
// <StartTime xmlns="Calendar:">20140207T130000Z</StartTime>
|
||||||
|
// <EndTime xmlns="Calendar:">20140207T140000Z</EndTime>
|
||||||
|
// <DTStamp xmlns="Calendar:">20140130T185245Z</DTStamp>
|
||||||
|
// <Subject xmlns="Calendar:">test 8</Subject>
|
||||||
|
// <Sensitivity xmlns="Calendar:">0</Sensitivity>
|
||||||
|
// <Body xmlns="AirSyncBase:">
|
||||||
|
// <Type>1</Type>
|
||||||
|
// <Data/>
|
||||||
|
// </Body>
|
||||||
|
// <Organizer_Email xmlns="Calendar:">sogo3@example.com</Organizer_Email>
|
||||||
|
// <UID xmlns="Calendar:">2978-52EA9D00-1-A253E70</UID>
|
||||||
|
// <Attendees xmlns="Calendar:">
|
||||||
|
// <Attendee>
|
||||||
|
// <Attendee_Name>sogo10</Attendee_Name>
|
||||||
|
// <Attendee_Email>sogo10@example.com</Attendee_Email>
|
||||||
|
// <Attendee_Type>1</Attendee_Type>
|
||||||
|
// </Attendee>
|
||||||
|
// </Attendees>
|
||||||
|
// <BusyStatus xmlns="Calendar:">2</BusyStatus>
|
||||||
|
// <MeetingStatus xmlns="Calendar:">3</MeetingStatus>
|
||||||
|
// <Organizer_Name xmlns="Calendar:">Wolfgang Fritz</Organizer_Name>
|
||||||
|
// </ApplicationData>
|
||||||
|
// </Change>
|
||||||
|
//
|
||||||
- (void) takeActiveSyncValues: (NSDictionary *) theValues
|
- (void) takeActiveSyncValues: (NSDictionary *) theValues
|
||||||
{
|
{
|
||||||
iCalDateTime *start, *end;
|
iCalDateTime *start, *end;
|
||||||
|
@ -331,6 +363,66 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
|
||||||
[rule takeActiveSyncValues: o];
|
[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
|
@end
|
||||||
|
|
|
@ -42,14 +42,14 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
#import <NGExtensions/NGBase64Coding.h>
|
#import <NGExtensions/NGBase64Coding.h>
|
||||||
|
|
||||||
struct SYSTEMTIME {
|
struct SYSTEMTIME {
|
||||||
uint16_t wYear;
|
uint16_t wYear;
|
||||||
uint16_t wMonth;
|
uint16_t wMonth;
|
||||||
uint16_t wDayOfWeek;
|
uint16_t wDayOfWeek;
|
||||||
uint16_t wDay;
|
uint16_t wDay;
|
||||||
uint16_t wHour;
|
uint16_t wHour;
|
||||||
uint16_t wMinute;
|
uint16_t wMinute;
|
||||||
uint16_t wSecond;
|
uint16_t wSecond;
|
||||||
uint16_t wMilliseconds;
|
uint16_t wMilliseconds;
|
||||||
};
|
};
|
||||||
|
|
||||||
@interface iCalTimeZonePeriod (ActiveSync)
|
@interface iCalTimeZonePeriod (ActiveSync)
|
||||||
|
|
Loading…
Reference in New Issue