From 30bcae5acbc50826e52ac74c184fa4a32240807b Mon Sep 17 00:00:00 2001 From: Ludovic Marcotte Date: Wed, 21 Sep 2016 09:20:37 -0400 Subject: [PATCH] (feat) initial EAS support for server-side mailbox search operations --- ActiveSync/SOGoActiveSyncDispatcher.m | 195 ++++++++++++++++++++++++-- 1 file changed, 182 insertions(+), 13 deletions(-) diff --git a/ActiveSync/SOGoActiveSyncDispatcher.m b/ActiveSync/SOGoActiveSyncDispatcher.m index d7cfd2094..41e5cdc10 100644 --- a/ActiveSync/SOGoActiveSyncDispatcher.m +++ b/ActiveSync/SOGoActiveSyncDispatcher.m @@ -2689,31 +2689,23 @@ void handle_eas_terminate(int signum) // // // -- (void) processSearch: (id ) theDocumentElement - inResponse: (WOResponse *) theResponse +- (void) processSearchGAL: (id ) theDocumentElement + inResponse: (WOResponse *) theResponse { SOGoContactSourceFolder *currentFolder; NSArray *allKeys, *allContacts, *mails; NSDictionary *systemSources, *contact; - NSString *name, *query, *current_mail; SOGoContactFolders *contactFolders; + NSString *current_mail, *query; SOGoUserFolder *userFolder; + NSMutableString *s; NSData *d; id o; int i, j, total; - - name = [[(id)[theDocumentElement getElementsByTagName: @"Name"] lastObject] textValue]; + query = [[(id)[theDocumentElement getElementsByTagName: @"Query"] lastObject] textValue]; - - // FIXME: for now, we only search in the GAL - if (![name isEqualToString: @"GAL"]) - { - [theResponse setStatus: 500]; - return; - } - userFolder = [[context activeUser] homeFolderInContext: context]; contactFolders = [userFolder privateContacts: @"Contacts" inContext: context]; @@ -2810,6 +2802,183 @@ void handle_eas_terminate(int signum) [theResponse setContent: d]; } +- (EOQualifier *) _qualifierFromMailboxSearchQuery: (id ) theDocumentElement +{ + id *andElement, *freeTextElement, *greaterThanElement; + + andElement = [[theDocumentElement getElementsByTagName: @"And"] lastObject]; + if (andElement) + { + EOQualifier *qualifier, *fetchQualifier, *notDeleted, *greaterThanQualifier; + NSString *query; + id o; + + freeTextElement = [[andElement getElementsByTagName: @"FreeText"] lastObject]; + query = [freeTextElement textValue]; + greaterThanQualifier = nil; + + if (!query) + return nil; + + // We check for the date ranges - we only support the GreaterThan since + // the IMAP protocol is limited in this regard + greaterThanElement = [[andElement getElementsByTagName: @"GreaterThan"] lastObject]; + if (greaterThanElement && [[greaterThanElement getElementsByTagName: @"DateReceived"] lastObject]) + { + o = [[[greaterThanElement getElementsByTagName: @"Value"] lastObject] textValue]; + greaterThanQualifier = [EOQualifier qualifierWithQualifierFormat: + @"(DATE >= %@)", [o calendarDate]]; + } + + notDeleted = [EOQualifier qualifierWithQualifierFormat: @"(not (flags = %@))", @"deleted"]; + qualifier = [EOQualifier qualifierWithQualifierFormat: [NSString stringWithFormat: @"(%@ doesContain: '%@')", @"subject", query]]; + fetchQualifier = [[EOAndQualifier alloc] initWithQualifiers: notDeleted, qualifier, greaterThanQualifier, nil]; + + return [fetchQualifier autorelease]; + } + + return nil; +} + +// +// +// +// +// Mailbox +// +// +// mail%2Fsogo_7f53_1c63c93c_1 +// aaa;bbb;09/12/2016-09/19/2016;ccc;ddd; +// +// +// 2015-09-19T04:00:00.000Z +// +// +// +// 2016-09-19T14:26:00.000Z +// +// +// +// +// +// 0-99 +// +// 1 +// 51200 +// +// 2 +// 1 +// +// +// +// +- (void) processSearchMailbox: (id ) theDocumentElement + inResponse: (WOResponse *) theResponse +{ + NSString *folderId, *realCollectionId, *itemId; + SOGoMailAccounts *accountsFolder; + SOGoMailAccount *accountFolder; + SOGoMailFolder *currentFolder; + SOGoMailObject *mailObject; + SOGoUserFolder *userFolder; + EOQualifier *qualifier; + NSArray *sortedUIDs; + NSMutableString *s; + NSData *d; + + SOGoMicrosoftActiveSyncFolderType folderType; + int i, total; + + // FIXME: support more than one CollectionId tag + DeepTraversal + folderId = [[[[(id)[theDocumentElement getElementsByTagName: @"Query"] lastObject] getElementsByTagName: @"CollectionId"] lastObject] textValue]; + realCollectionId = [folderId realCollectionIdWithFolderType: &folderType]; + realCollectionId = [self globallyUniqueIDToIMAPFolderName: realCollectionId type: folderType]; + + userFolder = [[context activeUser] homeFolderInContext: context]; + accountsFolder = [userFolder lookupName: @"Mail" inContext: context acquire: NO]; + accountFolder = [accountsFolder lookupName: @"0" inContext: context acquire: NO]; + currentFolder = [accountFolder lookupName: [NSString stringWithFormat: @"folder%@", realCollectionId] + inContext: context + acquire: NO]; + + // We build the qualifier and we launch our search operation + qualifier = [self _qualifierFromMailboxSearchQuery: [(id)[theDocumentElement getElementsByTagName: @"Query"] lastObject]]; + + if (!qualifier) + { + [theResponse setStatus: 500]; + return; + } + + sortedUIDs = [currentFolder fetchUIDsMatchingQualifier: qualifier + sortOrdering: @"REVERSE ARRIVAL" + threaded: NO]; + + // Prepare the response + s = [NSMutableString string]; + + [s appendString: @""]; + [s appendString: @""]; + [s appendString: @""]; + [s appendFormat: @"1"]; + [s appendFormat: @""]; + [s appendFormat: @""]; + [s appendFormat: @"1"]; + + total = [sortedUIDs count]; + for (i = 0; i < total; i++) + { + [s appendString: @""]; + [s appendString: @""]; + itemId = [[sortedUIDs objectAtIndex: i] stringValue]; + mailObject = [currentFolder lookupName: itemId inContext: context acquire: NO]; + + if ([mailObject isKindOfClass: [NSException class]]) + continue; + + [s appendFormat: [mailObject activeSyncRepresentationInContext: context]]; + [s appendString: @""]; + [s appendFormat: @""]; + } + + [s appendFormat: @"0-%d", total-1]; + [s appendFormat: @"%d", total]; + [s appendString: @""]; + [s appendString: @""]; + [s appendString: @""]; + + d = [[s dataUsingEncoding: NSUTF8StringEncoding] xml2wbxml]; + + [theResponse setContent: d]; +} + +// +// We support EAS Search on the GAL and Mailbox. +// +// We do NOT support it on the DocumentLibrary. +// +- (void) processSearch: (id ) theDocumentElement + inResponse: (WOResponse *) theResponse +{ + NSString *name; + + name = [[(id)[theDocumentElement getElementsByTagName: @"Name"] lastObject] textValue]; + + if ([name isEqualToString: @"GAL"]) + { + return [self processSearchGAL: theDocumentElement + inResponse: theResponse]; + } + else if ([name isEqualToString: @"Mailbox"]) + { + return [self processSearchMailbox: theDocumentElement + inResponse: theResponse]; + } + + [theResponse setStatus: 500]; + return; +} + // // //