(fix) EAS with drafts with attachments
parent
518e6a56fd
commit
d02a97468e
|
@ -34,6 +34,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|||
#import <Foundation/NSString.h>
|
||||
|
||||
static NSArray *asElementArray = nil;
|
||||
static NSArray *considerAsSameArray = nil;
|
||||
|
||||
@implementation NGDOMElement (ActiveSync)
|
||||
|
||||
|
@ -101,7 +102,44 @@ static NSArray *asElementArray = nil;
|
|||
int i, count;
|
||||
|
||||
if (!asElementArray)
|
||||
asElementArray = [[NSArray alloc] initWithObjects: @"Attendee", @"Category", @"Exception", nil];
|
||||
asElementArray = [[NSArray alloc] initWithObjects: @"Attendee", @"Category", @"Exception", @"Add", @"Delete", nil];
|
||||
|
||||
// FIXME
|
||||
/* if the client sends
|
||||
<attachments>
|
||||
<add>
|
||||
...
|
||||
</add>
|
||||
<add>
|
||||
...
|
||||
</add>
|
||||
</attachments>
|
||||
|
||||
the result is a NSarray i.e. {add1; add2; ...}
|
||||
|
||||
if the client sends
|
||||
|
||||
<attachments>
|
||||
<add>
|
||||
...
|
||||
</add>
|
||||
<add>
|
||||
...
|
||||
</add>
|
||||
<delete>
|
||||
...
|
||||
</delete>
|
||||
<delete>
|
||||
...
|
||||
</delete>
|
||||
</attachments>
|
||||
|
||||
|
||||
the result is a NSDictionary ie. Add = { }; Delete = { }; the dictionary would contain only one entry for add
|
||||
*/
|
||||
if (!considerAsSameArray)
|
||||
considerAsSameArray = [[NSArray alloc] initWithObjects: @"Add", @"Delete", nil];
|
||||
|
||||
|
||||
data = [NSMutableDictionary dictionary];
|
||||
|
||||
|
@ -151,7 +189,7 @@ static NSArray *asElementArray = nil;
|
|||
if (!innerTag)
|
||||
innerTag = [innerElement tagName];
|
||||
|
||||
if ([innerTag isEqualToString: [innerElement tagName]])
|
||||
if ([innerTag isEqualToString: [innerElement tagName]] || [considerAsSameArray containsObject: innerTag])
|
||||
{
|
||||
if ([(id)innerElement isTextNode])
|
||||
[innerElements addObject: [(NGDOMElement *)innerElement textValue]];
|
||||
|
|
|
@ -311,8 +311,34 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|||
anAddition = [additions objectAtIndex: i];
|
||||
is_new = YES;
|
||||
|
||||
clientId = [[(id)[anAddition getElementsByTagName: @"ClientId"] lastObject] textValue];
|
||||
|
||||
/*
|
||||
FIXME
|
||||
<Add>
|
||||
<ClientId>338</ClientId>
|
||||
<ApplicationData>
|
||||
<To xmlns="Email:"><foot@bar.com></To>
|
||||
<Cc xmlns="Email:"/>
|
||||
<Subject xmlns="Email:">test</Subject>
|
||||
<Reply-To xmlns="Email:">foo@bar.com</Reply-To>
|
||||
<Importance xmlns="Email:">1</Importance>
|
||||
<Read xmlns="Email:">1</Read>
|
||||
<Attachments xmlns="AirSyncBase:">
|
||||
<Add>
|
||||
<ClientId>152-ab557915-8451-49a7-a9c6-a9ac153021ad</ClientId>
|
||||
|
||||
|
||||
-> lastObject returns the ClientId in Attachments element -> try with objectAtIndex: 0 -> is this correct?
|
||||
*/
|
||||
//clientId = [[(id)[anAddition getElementsByTagName: @"ClientId"] lastObject] textValue];
|
||||
clientId = [[(id)[anAddition getElementsByTagName: @"ClientId"] objectAtIndex: 0] textValue];
|
||||
|
||||
allValues = [NSMutableDictionary dictionaryWithDictionary: [[(id)[anAddition getElementsByTagName: @"ApplicationData"] lastObject] applicationData]];
|
||||
|
||||
// FIXME: ignore the <Add> elements of Attachemnts - above (id)[theDocumentElement getElementsByTagName: @"Add"]; return any <Add> elements instead of only the direct childs of the <commands> element ..
|
||||
if (![allValues count])
|
||||
continue;
|
||||
|
||||
|
||||
switch (theFolderType)
|
||||
{
|
||||
|
@ -367,23 +393,39 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|||
case ActiveSyncMailFolder:
|
||||
default:
|
||||
{
|
||||
// Support SMS to Exchange eMail sync.
|
||||
// Support Draft Mail/SMS to Exchange eMail sync.
|
||||
NSString *serverId;
|
||||
NSMutableString *s;
|
||||
NSDictionary *result;
|
||||
NSNumber *modseq;
|
||||
|
||||
serverId = nil;
|
||||
s = [NSMutableString string];
|
||||
|
||||
sogoObject = [SOGoMailObject objectWithName: @"Mail" inContainer: theCollection];
|
||||
serverId = [sogoObject storeMail: allValues inContext: context];
|
||||
serverId = [sogoObject storeMail: allValues inBuffer: s inContext: context];
|
||||
if (serverId)
|
||||
{
|
||||
sogoObject = [theCollection lookupName: serverId inContext: context acquire: 0];
|
||||
[sogoObject takeActiveSyncValues: allValues inContext: context];
|
||||
|
||||
// Everything is fine, lets generate our response
|
||||
// serverId = clientId - There is no furhter processing after adding the SMS to the inbox.
|
||||
[theBuffer appendString: @"<Add>"];
|
||||
[theBuffer appendFormat: @"<ClientId>%@</ClientId>", clientId];
|
||||
[theBuffer appendFormat: @"<ServerId>%@</ServerId>", serverId];
|
||||
[theBuffer appendFormat: @"<Status>%d</Status>", 1];
|
||||
[theBuffer appendString: s];
|
||||
[theBuffer appendString: @"</Add>"];
|
||||
|
||||
|
||||
folderMetadata = [self _folderMetadataForKey: [self _getNameInCache: theCollection withType: theFolderType]];
|
||||
syncCache = [folderMetadata objectForKey: @"SyncCache"];
|
||||
[syncCache setObject: @"0" forKey: serverId];
|
||||
|
||||
result = [sogoObject fetchParts: [NSArray arrayWithObject: @"MODSEQ"]];
|
||||
modseq = [[[result objectForKey: @"RawResponse"] objectForKey: @"fetch"] objectForKey: @"modseq"];
|
||||
|
||||
[syncCache setObject: [modseq stringValue] forKey: serverId];
|
||||
|
||||
[self _setFolderMetadata: folderMetadata forKey: [self _getNameInCache: theCollection withType: theFolderType]];
|
||||
|
||||
continue;
|
||||
|
@ -670,6 +712,24 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|||
NSDictionary *result;
|
||||
NSNumber *modseq;
|
||||
|
||||
// Process an update to a Draft Mail.
|
||||
if ([allChanges objectForKey: @"Body"])
|
||||
{
|
||||
NSString *serverId;
|
||||
NSMutableString *s;
|
||||
|
||||
serverId = nil;
|
||||
s = [NSMutableString string];
|
||||
|
||||
serverId = [sogoObject storeMail: allChanges inBuffer: s inContext: context];
|
||||
if (serverId)
|
||||
{
|
||||
// we delete the original email - next sync will update the client with the new mail
|
||||
[sogoObject delete];
|
||||
sogoObject = [theCollection lookupName: serverId inContext: context acquire: 0];
|
||||
}
|
||||
}
|
||||
|
||||
[sogoObject takeActiveSyncValues: allChanges inContext: context];
|
||||
|
||||
result = [sogoObject fetchParts: [NSArray arrayWithObject: @"MODSEQ"]];
|
||||
|
@ -684,7 +744,13 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|||
|
||||
[theBuffer appendString: @"<Change>"];
|
||||
[theBuffer appendFormat: @"<ServerId>%@</ServerId>", origServerId];
|
||||
[theBuffer appendFormat: @"<Status>%d</Status>", 1];
|
||||
|
||||
// A body element is sent only for draft mails - status 8 will delete the mail on the client - the next sync update fetch the new mail
|
||||
if ([allChanges objectForKey: @"Body"] && theFolderType == ActiveSyncMailFolder)
|
||||
[theBuffer appendFormat: @"<Status>%d</Status>", 8];
|
||||
else
|
||||
[theBuffer appendFormat: @"<Status>%d</Status>", 1];
|
||||
|
||||
[theBuffer appendString: @"</Change>"];
|
||||
}
|
||||
}
|
||||
|
|
|
@ -43,6 +43,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|||
- (void) takeActiveSyncValues: (NSDictionary *) theValues
|
||||
inContext: (WOContext *) context;
|
||||
- (NSString *) storeMail: (NSDictionary *) theValues
|
||||
inBuffer: (NSMutableString *) theBuffer
|
||||
inContext: (WOContext *) _context;
|
||||
|
||||
@end
|
||||
|
|
|
@ -65,6 +65,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|||
|
||||
#import <SOGo/SOGoUserDefaults.h>
|
||||
#import <SOGo/SOGoUserFolder.h>
|
||||
#import <SOGo/NSArray+Utilities.h>
|
||||
|
||||
#include "iCalTimeZone+ActiveSync.h"
|
||||
#include "NSData+ActiveSync.h"
|
||||
|
@ -1449,18 +1450,22 @@ struct GlobalObjectId {
|
|||
}
|
||||
|
||||
- (NSString *) storeMail: (NSDictionary *) theValues
|
||||
inBuffer: (NSMutableString *) theBuffer
|
||||
inContext: (WOContext *) _context
|
||||
{
|
||||
NSString *dateReceived, *folder, *s;
|
||||
NSString *dateReceived, *folder, *s, *serverId, *messageId;
|
||||
NGMimeMessageGenerator *generator;
|
||||
NGMimeMessage *bounceMessage;
|
||||
NGMimeMessage *draftMessage;
|
||||
NGMimeBodyPart *bodyPart;
|
||||
NGMimeMultipartBody *body;
|
||||
NSDictionary *identity;
|
||||
NGMutableHashMap *map;
|
||||
NGImap4Client *client;
|
||||
NSData *message_data;
|
||||
NSArray *attachmentKeys;
|
||||
|
||||
id o, result;
|
||||
int bodyType;
|
||||
id o, a, result, attachments;
|
||||
int bodyType, i;
|
||||
|
||||
identity = [[context activeUser] primaryIdentity];
|
||||
message_data = nil;
|
||||
|
@ -1480,11 +1485,21 @@ struct GlobalObjectId {
|
|||
map = [[[NGMutableHashMap alloc] initWithCapacity: 1] autorelease];
|
||||
|
||||
[map setObject: [NSString stringWithFormat: @"%@ <%@>", [identity objectForKey: @"fullName"], [identity objectForKey: @"email"]] forKey: @"from"];
|
||||
|
||||
if ((o = [theValues objectForKey: @"To"]))
|
||||
[map setObject: o forKey: @"to"];
|
||||
|
||||
if ((o = [theValues objectForKey: @"Cc"]))
|
||||
[map setObject: o forKey: @"cc"];
|
||||
|
||||
if ((o = [theValues objectForKey: @"Bcc"]))
|
||||
[map setObject: o forKey: @"bcc"];
|
||||
|
||||
if ((o = [theValues objectForKey: @"Reply-To"]))
|
||||
[map setObject: o forKey: @"Reply-To"];
|
||||
|
||||
if ((o = [theValues objectForKey: @"Subject"]))
|
||||
[map setObject: o forKey: @"subject"];
|
||||
[map setObject: o forKey: @"subject"];
|
||||
|
||||
o = [[theValues objectForKey: @"DateReceived"] calendarDate];
|
||||
|
||||
|
@ -1510,17 +1525,114 @@ struct GlobalObjectId {
|
|||
#endif
|
||||
|
||||
[map setObject: dateReceived forKey: @"date"];
|
||||
[map setObject: [NSString generateMessageID] forKey: @"message-id"];
|
||||
[map setObject: (bodyType == 1 ? @"text/plain; charset=utf-8" : @"text/html; charset=utf-8")
|
||||
forKey: @"content-type"];
|
||||
[map setObject: @"quoted-printable" forKey: @"content-transfer-encoding"];
|
||||
|
||||
bounceMessage = [[[NGMimeMessage alloc] initWithHeader: map] autorelease];
|
||||
messageId = [NSString generateMessageID];
|
||||
[map setObject: messageId forKey: @"message-id"];
|
||||
|
||||
[bounceMessage setBody: [[NSString stringWithFormat: @"%@", [[theValues objectForKey: @"Body"] objectForKey: @"Data"] ] dataUsingEncoding: NSUTF8StringEncoding]];
|
||||
attachmentKeys = [self fetchFileAttachmentKeys];
|
||||
|
||||
if ((attachments = [theValues objectForKey: @"Attachments"]) || [attachmentKeys count])
|
||||
{
|
||||
[map setObject: @"multipart/mixed" forKey: @"content-type"];
|
||||
draftMessage = [[[NGMimeMessage alloc] initWithHeader: map] autorelease];
|
||||
body = [[[NGMimeMultipartBody alloc] initWithPart: draftMessage] autorelease];
|
||||
|
||||
map = [[[NGMutableHashMap alloc] initWithCapacity: 1] autorelease];
|
||||
|
||||
[map setObject: (bodyType == 1 ? @"text/plain; charset=utf-8" : @"text/html; charset=utf-8")
|
||||
forKey: @"content-type"];
|
||||
[map setObject: @"quoted-printable" forKey: @"content-transfer-encoding"];
|
||||
|
||||
bodyPart = [[[NGMimeBodyPart alloc] initWithHeader:map] autorelease];
|
||||
|
||||
[bodyPart setBody: [[NSString stringWithFormat: @"%@", [[theValues objectForKey: @"Body"] objectForKey: @"Data"] ] dataUsingEncoding: NSUTF8StringEncoding]];
|
||||
|
||||
[body addBodyPart: bodyPart];
|
||||
|
||||
// Add attachments from the original mail - skip deletions
|
||||
if ([attachmentKeys count])
|
||||
{
|
||||
id currentAttachment;
|
||||
NGHashMap *response;
|
||||
NSData *bodydata;
|
||||
NSArray *paths;
|
||||
NGMimeFileData *fdata;
|
||||
int i, ii;
|
||||
BOOL found;
|
||||
|
||||
paths = [attachmentKeys keysWithFormat: @"BODY[%{path}]"];
|
||||
response = [[self fetchParts: paths] objectForKey: @"RawResponse"];
|
||||
|
||||
for (i = 0; i < [attachmentKeys count]; i++)
|
||||
{
|
||||
currentAttachment = [attachmentKeys objectAtIndex: i];
|
||||
found = NO;
|
||||
for (ii = 0; ii < [attachments count]; ii++)
|
||||
{
|
||||
// no ClientId means its a deletio
|
||||
if (![[attachments objectAtIndex: ii] objectForKey: @"ClientId"] &&
|
||||
[[[[attachments objectAtIndex: ii] objectForKey: @"FileReference"] lastPathComponent] isEqualToString: [currentAttachment objectForKey: @"path"]])
|
||||
{
|
||||
found = YES;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// skip deletions
|
||||
if (found)
|
||||
continue;
|
||||
|
||||
bodydata = [[[response objectForKey: @"fetch"] objectForKey: [NSString stringWithFormat: @"body[%@]", [currentAttachment objectForKey: @"path"]]] valueForKey: @"data"];
|
||||
|
||||
map = [[[NGMutableHashMap alloc] initWithCapacity: 1] autorelease];
|
||||
[map setObject: [currentAttachment objectForKey: @"mimetype"] forKey: @"content-type"];
|
||||
[map setObject: [currentAttachment objectForKey: @"encoding"] forKey: @"content-transfer-encoding"];
|
||||
[map addObject: [NSString stringWithFormat: @"attachment; filename=\"%@\"", [currentAttachment objectForKey: @"filename"]] forKey: @"content-disposition"];
|
||||
bodyPart = [[[NGMimeBodyPart alloc] initWithHeader: map] autorelease];
|
||||
|
||||
fdata = [[NGMimeFileData alloc] initWithBytes:[bodydata bytes] length:[bodydata length]];
|
||||
[bodyPart setBody: fdata];
|
||||
RELEASE(fdata);
|
||||
[body addBodyPart: bodyPart];
|
||||
}
|
||||
}
|
||||
|
||||
// add new attachments
|
||||
for (i = 0; i < [attachments count]; i++)
|
||||
{
|
||||
|
||||
map = [[[NGMutableHashMap alloc] initWithCapacity: 1] autorelease];
|
||||
a = [attachments objectAtIndex: i];
|
||||
|
||||
// no ClientId means its a deletion
|
||||
if (![a objectForKey: @"ClientId"])
|
||||
continue;
|
||||
|
||||
[map setObject: @"application/octet-stream" forKey: @"content-type"]; // FIXME ?? can we guess the right content-type
|
||||
[map setObject: @"base64" forKey: @"content-transfer-encoding"];
|
||||
[map setObject: [NSString stringWithFormat: @"attachment; filename=\"%@\"", [a objectForKey: @"DisplayName"]] forKey: @"content-disposition"];
|
||||
|
||||
bodyPart = [[[NGMimeBodyPart alloc] initWithHeader:map] autorelease];
|
||||
[bodyPart setBody: [[a objectForKey: @"Content"] stringByDecodingBase64]];
|
||||
[body addBodyPart: bodyPart];
|
||||
}
|
||||
|
||||
|
||||
[draftMessage setBody: body];
|
||||
}
|
||||
else
|
||||
{
|
||||
[map setObject: (bodyType == 1 ? @"text/plain; charset=utf-8" : @"text/html; charset=utf-8")
|
||||
forKey: @"content-type"];
|
||||
[map setObject: @"quoted-printable" forKey: @"content-transfer-encoding"];
|
||||
|
||||
draftMessage = [[[NGMimeMessage alloc] initWithHeader: map] autorelease];
|
||||
|
||||
[draftMessage setBody: [[NSString stringWithFormat: @"%@", [[theValues objectForKey: @"Body"] objectForKey: @"Data"] ] dataUsingEncoding: NSUTF8StringEncoding]];
|
||||
}
|
||||
|
||||
generator = [[[NGMimeMessageGenerator alloc] init] autorelease];
|
||||
message_data = [generator generateMimeFromPart: bounceMessage];
|
||||
message_data = [generator generateMimeFromPart: draftMessage];
|
||||
}
|
||||
|
||||
if (message_data)
|
||||
|
@ -1540,8 +1652,32 @@ struct GlobalObjectId {
|
|||
toFolder: folder
|
||||
withFlags: [NSArray arrayWithObjects: @"draft", nil]];
|
||||
|
||||
serverId = [NSString stringWithFormat: @"%d", [self IMAP4IDFromAppendResult: result]];
|
||||
|
||||
[theBuffer appendString: @"<ApplicationData>"];
|
||||
[theBuffer appendFormat: @"<ConversationId xmlns=\"Email2:\">%@</ConversationId>", [[messageId dataUsingEncoding: NSUTF8StringEncoding] activeSyncRepresentationInContext: context]];
|
||||
|
||||
if ([attachments count])
|
||||
{
|
||||
[theBuffer appendString: @"<Attachments xmlns=\"AirSyncBase:\">"];
|
||||
|
||||
for (i = 0; i < [attachments count]; i++)
|
||||
{
|
||||
a = [attachments objectAtIndex: i];
|
||||
|
||||
[theBuffer appendString: @"<Attachment>"];
|
||||
[theBuffer appendFormat: @"<ClientId xmlns=\"AirSyncBase:\">%@</ClientId>", [a objectForKey: @"ClientId"]];
|
||||
[theBuffer appendFormat: @"<FileReference xmlns=\"AirSyncBase:\">mail/%@/%@/%d</FileReference>", [[[self container] relativeImap4Name] stringByEscapingURL], serverId, i+2];
|
||||
[theBuffer appendString: @"</Attachment>"];
|
||||
}
|
||||
|
||||
[theBuffer appendString: @"</Attachments>"];
|
||||
}
|
||||
|
||||
[theBuffer appendString: @"</ApplicationData>"];
|
||||
|
||||
if ([[result objectForKey: @"result"] boolValue])
|
||||
return [NSString stringWithFormat: @"%d", [self IMAP4IDFromAppendResult: result]];
|
||||
return serverId;
|
||||
}
|
||||
|
||||
return nil;
|
||||
|
|
1
NEWS
1
NEWS
|
@ -57,6 +57,7 @@ Bug fixes
|
|||
- [eas] avoid sync requests for shared folders every second (#4275)
|
||||
- [eas] we skip the organizer from the attendees list (#4402)
|
||||
- [eas] correctly handle all-day events with EAS v16 (#4397)
|
||||
- [eas] fixed EAS save in drafts with attachments
|
||||
|
||||
3.2.10 (2017-07-05)
|
||||
-------------------
|
||||
|
|
Loading…
Reference in New Issue