View Issue Details
| ID | Project | Category | View Status | Date Submitted | Last Update |
|---|---|---|---|---|---|
| 0005620 | SOGo | ActiveSync | public | 2022-10-07 09:15 | 2023-02-02 19:41 |
| Reporter | bgaussen | Assigned To | |||
| Priority | normal | Severity | major | Reproducibility | always |
| Status | new | Resolution | open | ||
| Platform | Apple | OS | iOS | OS Version | 16 |
| Product Version | 5.7.1 | ||||
| Summary | 0005620: EAS server search fails | ||||
| Description | Running SOGo on a mallow installation, I experience issues with EAS servers searches that don't return any results. mailcowdockerized-sogo-mailcow-1 | Oct 6 17:17:17 90a0cb111729 sogod [55]: <0x0x55aacaef50d0[SOGoActiveSyncDispatcher]> EAS - request for device TK8BPB39TT36L9HIRFF63UHEOG: <?xml version="1.0"?> Please ask if more or more precise logs needed. | ||||
| Steps To Reproduce | Search a SOGo mailbox from iOS 16 mail client. Only local results shown. | ||||
| Tags | No tags attached. | ||||
|
Can confirm. I test on ios 16.x and only local mails (within defined sync range) are returned. Testing this on older ios (version 12) works. There must be a change in ios, as when i test this against another setup with z-push, the behavior is exactly the same. |
|
|
This is not yet implemented. Depends on https://github.com/libwbxml/libwbxml/pull/86. |
|
|
Hi @tfu, Thanks for the feedback, It seems that PR has been merged. Installed the lib but it seems somes changes are needed in the code also :
For those steps it's ok. However the response provided by SOGo seems to be ok but iOS still displays "no results". Any idea of where to get documentation ? Here is the search EAS request :
and extract of the response : |
|
|
I drafted a fix for this some time ago but don't have to time finish it. Maybe it helps. 5620.diff (12,993 bytes)
diff --git a/ActiveSync/SOGoActiveSyncDispatcher.m b/ActiveSync/SOGoActiveSyncDispatcher.m
index 03b378690..7f9ce6845 100644
--- a/ActiveSync/SOGoActiveSyncDispatcher.m
+++ b/ActiveSync/SOGoActiveSyncDispatcher.m
@@ -341,18 +341,30 @@ void handle_eas_terminate(int signum)
default:
{
SOGoMailAccounts *accountsFolder;
- SOGoMailFolder *currentFolder;
+ SOGoMailAccount *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];
- if (![(SOGoMailFolder *)collection exists])
- collection = nil;
+
+ if (![(SOGoMailFolder *)collection exists]) {
+ // If templates folder doesn't exists yet, then create it.
+ if ([theCollectionId isEqualToString: [currentFolder templatesFolderNameInContext: context]]) {
+ if ([(SOGoMailFolder *)[currentFolder templatesFolderInContext: context] create]) {
+ collection = [currentFolder lookupName: [NSString stringWithFormat: @"folder%@", theCollectionId]
+ inContext: context
+ acquire: NO];
+
+ }
+ }
+
+ if (![(SOGoMailFolder *)collection exists])
+ collection = nil;
+ }
}
}
@@ -3152,6 +3164,106 @@ void handle_eas_terminate(int signum)
return nil;
}
+- (EOQualifier *) _qualifierFromMailboxFindQuery: (id <DOMElement>) theDocumentElement
+{
+ id <DOMElement> freeTextElement;
+ NSMutableArray *qualifiers;
+ EOQualifier *fetchQualifier;
+ NSRange r, r1;
+ NSString *a, *s, *query;
+
+ freeTextElement = [(id)[theDocumentElement getElementsByTagName: @"FreeText"] lastObject];
+ query = [(id)freeTextElement textValue];
+
+ if (!query)
+ return nil;
+
+ //query = @"mmm OR to:xxx";
+ qualifiers = [NSMutableArray array];
+
+ while ([query length] > 0)
+ {
+ r = [query rangeOfString:@":"];
+ r1 = [query rangeOfString:@" "];
+ if (r.location != NSNotFound && (r.location < r1.location || r1.location == NSNotFound))
+ {
+ a = [query substringToIndex:r.location];
+ query =[query substringFromIndex:r.location+1];
+
+ if ([query hasPrefix:@"\""])
+ {
+ query = [query substringFromIndex:1];
+ r = [query rangeOfString:@"\""];
+ s = [query substringToIndex:r.location];
+ query =[query substringFromIndex:r.location+1]; // strip quote
+ }
+ else
+ {
+ r = [query rangeOfString:@" "];
+ if (r.location == NSNotFound)
+ {
+ s = query;
+ query = @"";
+ }
+ else
+ {
+ s = [query substringToIndex:r.location];
+ query =[query substringFromIndex:r.location];
+ }
+ }
+ }
+ else if ([query hasPrefix: @"OR"])
+ {
+ s = [query substringToIndex:2];
+ query =[query substringFromIndex:2];
+ a = @"";
+ }
+ else if ([query hasPrefix:@"\""])
+ {
+ query = [query substringFromIndex:1];
+ r = [query rangeOfString:@"\""];
+ s = [query substringToIndex:r.location];
+ query =[query substringFromIndex:r.location+1]; // strip quote
+ a = @"text";
+ }
+ else
+ {
+ r = [query rangeOfString:@" "];
+ if (r.location == NSNotFound)
+ {
+ s = query;
+ query = @"";
+ }
+ else
+ {
+ s = [query substringToIndex:r.location];
+ query =[query substringFromIndex:r.location];
+ }
+ a = @"text";
+ }
+
+ if ([query hasPrefix:@" "])
+ {
+ query = [query substringFromIndex: 1];
+ }
+
+ NSLog(@"tfu a: %@", a);
+ NSLog(@"tfu s: %@", s);
+ NSLog(@"tfu query: %@", query);
+ NSLog(@"tfu length: %d", [query length]);
+
+ if ([a length] > 0)
+ {
+ [qualifiers addObject: [EOQualifier qualifierWithQualifierFormat: [NSString stringWithFormat: @"(%@ doesContain: '%@')", a, s]]];
+ }
+ }
+
+ fetchQualifier = [[EOOrQualifier alloc] initWithQualifierArray: qualifiers];
+
+ NSLog(@"tfu qual %@",fetchQualifier);
+ return [fetchQualifier autorelease];
+
+}
//
// <!DOCTYPE ActiveSync PUBLIC "-//MICROSOFT//DTD ActiveSync//EN" "http://www.microsoft.com/">
// <Search xmlns="Search:">
@@ -3213,9 +3325,10 @@ void handle_eas_terminate(int signum)
return;
}
- bodyPreferenceType = [[(id)[[(id)[theDocumentElement getElementsByTagName: @"BodyPreference"] lastObject] getElementsByTagName: @"Type"] lastObject] textValue];
+ bodyPreferenceType = [[(id)[[(id)[theDocumentElement getElementsByTagName: @"BodyPreference"] firstObject] getElementsByTagName: @"Type"] lastObject] textValue];
[context setObject: bodyPreferenceType forKey: @"BodyPreferenceType"];
mimeSupport = [[(id)[theDocumentElement getElementsByTagName: @"MIMESupport"] lastObject] textValue];
+ NSLog(@"tfu mimeSupport %@",mimeSupport);
[context setObject: mimeSupport forKey: @"MIMESupport"];
[context setObject: @"8" forKey: @"MIMETruncation"];
@@ -3334,6 +3447,149 @@ void handle_eas_terminate(int signum)
[theResponse setContent: d];
}
+- (void) processFindMailbox: (id <DOMElement>) theDocumentElement
+ inResponse: (WOResponse *) theResponse
+{
+ NSString *folderId, *realCollectionId, *itemId;
+ NSMutableArray *folderIdentifiers;
+ SOGoMailAccounts *accountsFolder;
+ SOGoMailAccount *accountFolder;
+ SOGoMailFolder *currentFolder;
+ SOGoMailObject *mailObject;
+ SOGoUserFolder *userFolder;
+ EOQualifier *qualifier;
+ NSArray *sortedUIDs, *a;
+ NSMutableString *s;
+ NSData *d;
+
+ SOGoMicrosoftActiveSyncFolderType folderType;
+ int i, j, total, begin, startRange, endRange, maxResults, overallTotal;
+
+ overallTotal = 0;
+
+ // We build the qualifier and we launch our search operation
+ qualifier = [self _qualifierFromMailboxFindQuery: [(id)[theDocumentElement getElementsByTagName: @"Query"] lastObject]];
+
+ if (!qualifier)
+ {
+ [theResponse setStatus: 500];
+ return;
+ }
+
+ // We check for the maximum number of results to return.
+ a = [[[(id)[theDocumentElement getElementsByTagName: @"Range"] lastObject] textValue] componentsSeparatedByString: @"-"];
+ startRange = [[a objectAtIndex: 0] intValue];
+ begin = startRange;
+ endRange = [[a objectAtIndex: 1] intValue];
+ maxResults = endRange - startRange;
+
+ if (maxResults == 0)
+ maxResults = endRange = 99;
+
+ // FIXME: support more than one CollectionId tag + DeepTraversal
+ folderId = [[(id)[[(id)[theDocumentElement getElementsByTagName: @"Query"] lastObject] getElementsByTagName: @"CollectionId"] lastObject] textValue];
+ folderIdentifiers = [NSMutableArray array];
+
+ // Android 6 will send search requests with no collection ID - so we search in all folders.
+ // Outlook Mobile App sends search requests with CollectionId=0 - We treat this as an all-folder-search.
+ if (!folderId || [folderId isEqualToString: @"0"])
+ {
+ NSArray *foldersInCache;
+ SOGoCacheGCSObject *o;
+ NSString *prefix;
+
+ o = [SOGoCacheGCSObject objectWithName: @"0" inContainer: nil];
+ [o setObjectType: ActiveSyncFolderCacheObject];
+ [o setTableUrl: folderTableURL];
+
+ foldersInCache = [o cacheEntriesForDeviceId: [context objectForKey: @"DeviceId"] newerThanVersion: -1];
+ prefix = [NSString stringWithFormat: @"/%@+folder", [context objectForKey: @"DeviceId"]];
+
+ for (i = 0; i < [foldersInCache count]; i++)
+ {
+ folderId = [foldersInCache objectAtIndex: i];
+ if ([folderId hasPrefix: prefix])
+ {
+ folderId = [NSString stringWithFormat: @"mail/%@", [folderId substringFromIndex: [prefix length]]];
+ [folderIdentifiers addObject: folderId];
+ }
+ }
+ }
+ else
+ {
+ [folderIdentifiers addObject: folderId];
+ }
+
+ userFolder = [[context activeUser] homeFolderInContext: context];
+ accountsFolder = [userFolder lookupName: @"Mail" inContext: context acquire: NO];
+ accountFolder = [accountsFolder lookupName: @"0" inContext: context acquire: NO];
+
+ // Prepare the response
+ s = [NSMutableString string];
+ [s appendString: @"<?xml version=\"1.0\" encoding=\"utf-8\"?>"];
+ [s appendString: @"<!DOCTYPE ActiveSync PUBLIC \"-//MICROSOFT//DTD ActiveSync//EN\" \"http://www.microsoft.com/\">"];
+ [s appendString: @"<Find xmlns=\"Find:\">"];
+ [s appendFormat: @"<Status>1</Status>"];
+ [s appendFormat: @"<Response xmlns=\"Find:\">"];
+ [s appendFormat: @"<Store xmlns=\"ItemOperations:\">Mailbox</Store>"];
+ [s appendFormat: @"<Status>1</Status>"];
+
+ for (i = 0; i < [folderIdentifiers count]; i++)
+ {
+ folderId = [folderIdentifiers objectAtIndex: i];
+ realCollectionId = [folderId realCollectionIdWithFolderType: &folderType];
+ realCollectionId = [self globallyUniqueIDToIMAPFolderName: realCollectionId type: folderType];
+
+ currentFolder = [accountFolder lookupName: [NSString stringWithFormat: @"folder%@", realCollectionId]
+ inContext: context
+ acquire: NO];
+
+ sortedUIDs = [currentFolder fetchUIDsMatchingQualifier: qualifier
+ sortOrdering: @"REVERSE ARRIVAL"
+ threaded: NO];
+ total = [sortedUIDs count];
+ overallTotal+=total;
+
+ if (total < startRange)
+ {
+ begin -= total;
+ continue;;
+ }
+
+ for (j = begin; j < total && maxResults >= 0; j++)
+ {
+ itemId = [[sortedUIDs objectAtIndex: j] stringValue];
+ mailObject = [currentFolder lookupName: itemId inContext: context acquire: NO];
+
+ if ([mailObject isKindOfClass: [NSException class]])
+ continue;
+
+ maxResults--;
+
+ [s appendString: @"<Result xmlns=\"Find:\">"];
+ [s appendString: @"<Class>Email</Class>"];
+ [s appendFormat: @"<ServerId>%@</ServerId>",itemId];
+ [s appendFormat: @"<CollectionId xmlns=\"AirSyncBase:\">%@</CollectionId>", folderId];
+ [s appendString: @"<Properties xmlns=\"Find:\">"];
+ [s appendString: [mailObject activeSyncRepresentationInContext: context]];
+ [s appendString: @"</Properties>"];
+ [s appendFormat: @"</Result>"];
+ }
+ }
+
+ if (overallTotal < startRange)
+ overallTotal = 0;
+
+ [s appendFormat: @"<Range xmlns=\"Find:\">%d-%d</Range>",(overallTotal ? startRange : 0), (overallTotal ? endRange - maxResults - 1 : 0)];
+ [s appendFormat: @"<Total xmlns=\"Find:\">%d</Total>", overallTotal];
+ [s appendString: @"</Response>"];
+ [s appendString: @"</Find>"];
+
+ d = [[s dataUsingEncoding: NSUTF8StringEncoding] xml2wbxml];
+
+ [theResponse setContent: d];
+}
+
//
// We support EAS Search on the GAL and Mailbox.
//
@@ -3361,6 +3617,31 @@ void handle_eas_terminate(int signum)
return;
}
+- (void) processFind: (id <DOMElement>) theDocumentElement
+ inResponse: (WOResponse *) theResponse
+{
+ id <DOMElement> documentElement;
+
+ if ((documentElement = [(id)[theDocumentElement getElementsByTagName: @"MailBoxSearchCriterion"] lastObject]))
+ {
+ return [self processFindMailbox: theDocumentElement
+ inResponse: theResponse];
+ //qualifier = [self _qualifierFromMailboxFindQuery: [(id)[theDocumentElement getElementsByTagName: @"Query"] lastObject]];
+
+
+// return [self processSearchGAL: theDocumentElement
+// inResponse: theResponse];
+ }
+ else if ((documentElement = [(id)[theDocumentElement getElementsByTagName: @"GALSearchCriterion"] lastObject]))
+ {
+ // return [self processSearchMailbox: theDocumentElement
+ // inResponse: theResponse];
+ }
+
+ [theResponse setStatus: 500];
+ return;
+}
+
//
//
// |
|