mirror of
https://github.com/inverse-inc/sogo.git
synced 2026-03-01 21:26:24 +00:00
Monotone-Parent: c2bdfbc593f7ac0758ee78b63baee4d5b409e6bd
Monotone-Revision: a297f80b93f195cc2534f1493b2f4e4a124cf3ca Monotone-Author: wsourdeau@inverse.ca Monotone-Date: 2011-10-26T21:33:51 Monotone-Branch: ca.inverse.sogo
This commit is contained in:
@@ -8,6 +8,14 @@
|
||||
|
||||
2011-10-26 Wolfgang Sourdeau <wsourdeau@inverse.ca>
|
||||
|
||||
* OpenChange/MAPIStoreFSMessage.m,
|
||||
OpenChange/MAPIStoreMailVolatileMessage.m: new descendants of
|
||||
MAPIStoreMemMessage.
|
||||
|
||||
* OpenChange/MAPIStoreDraftsMessage: replaced class with
|
||||
MAPIStoreMemMessage, enabling the creation of mails from any mail
|
||||
folder.
|
||||
|
||||
* OpenChange/MAPIStoreMessage.m
|
||||
(-modifyRecipientsWithRows:andCount:andColumns:): take BCC
|
||||
recipients into account.
|
||||
|
||||
@@ -51,6 +51,8 @@ $(SOGOBACKEND)_OBJC_FILES += \
|
||||
MAPIStoreFolderTable.m \
|
||||
MAPIStorePermissionsTable.m \
|
||||
\
|
||||
MAPIStoreMemMessage.m \
|
||||
\
|
||||
MAPIStoreFSBaseContext.m \
|
||||
MAPIStoreFSFolder.m \
|
||||
MAPIStoreFSFolderTable.m \
|
||||
@@ -86,8 +88,8 @@ $(SOGOBACKEND)_OBJC_FILES += \
|
||||
MAPIStoreMailAttachment.m \
|
||||
MAPIStoreMailContext.m \
|
||||
MAPIStoreMailFolder.m \
|
||||
MAPIStoreDraftsMessage.m \
|
||||
MAPIStoreMailMessage.m \
|
||||
MAPIStoreMailVolatileMessage.m \
|
||||
MAPIStoreMailMessageTable.m \
|
||||
\
|
||||
MAPIStoreNotesContext.m \
|
||||
@@ -115,8 +117,6 @@ $(SOGOBACKEND)_OBJC_FILES += \
|
||||
\
|
||||
EOBitmaskQualifier.m \
|
||||
EOQualifier+MAPIMem.m \
|
||||
\
|
||||
MAPIStoreMemMailMessage.m \
|
||||
|
||||
|
||||
$(SOGOBACKEND)_RESOURCE_FILES += \
|
||||
|
||||
@@ -30,7 +30,6 @@
|
||||
#import <NGCards/NSArray+NGCards.h>
|
||||
#import <NGCards/NGVCard.h>
|
||||
#import <NGCards/NGVCardPhoto.h>
|
||||
#import <SOGo/SOGoUserDefaults.h>
|
||||
#import <Contacts/SOGoContactGCSEntry.h>
|
||||
|
||||
#import "MAPIStoreContactsAttachment.h"
|
||||
|
||||
@@ -1,36 +0,0 @@
|
||||
/* MAPIStoreDraftsMessage.h - this file is part of SOGo
|
||||
*
|
||||
* Copyright (C) 2011 Inverse inc
|
||||
*
|
||||
* Author: Wolfgang Sourdeau <wsourdeau@inverse.ca>
|
||||
*
|
||||
* This file is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2, or (at your option)
|
||||
* any later version.
|
||||
*
|
||||
* This file is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; see the file COPYING. If not, write to
|
||||
* the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
|
||||
* Boston, MA 02111-1307, USA.
|
||||
*/
|
||||
|
||||
#ifndef MAPISTOREDRAFTSMESSAGE_H
|
||||
#define MAPISTOREDRAFTSMESSAGE_H
|
||||
|
||||
#import "MAPIStoreMailMessage.h"
|
||||
|
||||
@class NSMutableArray;
|
||||
|
||||
@interface MAPIStoreDraftsMessage : MAPIStoreMailMessage
|
||||
|
||||
- (int) submitWithFlags: (enum SubmitFlags) flags;
|
||||
|
||||
@end
|
||||
|
||||
#endif /* MAPISTOREDRAFTSMESSAGE_H */
|
||||
@@ -1,711 +0,0 @@
|
||||
/* MAPIStoreDraftsMessage.m - this file is part of SOGo
|
||||
*
|
||||
* Copyright (C) 2011 Inverse inc
|
||||
*
|
||||
* Author: Wolfgang Sourdeau <wsourdeau@inverse.ca>
|
||||
*
|
||||
* This file is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 3, or (at your option)
|
||||
* any later version.
|
||||
*
|
||||
* This file is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; see the file COPYING. If not, write to
|
||||
* the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
|
||||
* Boston, MA 02111-1307, USA.
|
||||
*/
|
||||
|
||||
#import <Foundation/NSCalendarDate.h>
|
||||
#import <Foundation/NSDictionary.h>
|
||||
#import <Foundation/NSString.h>
|
||||
#import <NGExtensions/NSObject+Logs.h>
|
||||
#import <NGMail/NGMailAddress.h>
|
||||
#import <NGMail/NGMailAddressParser.h>
|
||||
#import <SOGo/NSArray+Utilities.h>
|
||||
#import <SOGo/NSDictionary+Utilities.h>
|
||||
#import <SOGo/SOGoUser.h>
|
||||
#import <SOGo/SOGoUserManager.h>
|
||||
#import <Mailer/SOGoDraftObject.h>
|
||||
#import <NGObjWeb/WOContext+SoObjects.h>
|
||||
|
||||
#import "MAPIStoreContext.h"
|
||||
#import "MAPIStoreMapping.h"
|
||||
#import "MAPIStoreMIME.h"
|
||||
#import "MAPIStoreTypes.h"
|
||||
#import "NSData+MAPIStore.h"
|
||||
#import "NSObject+MAPIStore.h"
|
||||
#import "NSString+MAPIStore.h"
|
||||
|
||||
#import "MAPIStoreDraftsMessage.h"
|
||||
|
||||
#undef DEBUG
|
||||
#include <mapistore/mapistore.h>
|
||||
#include <mapistore/mapistore_errors.h>
|
||||
|
||||
static Class NGMailAddressK, NSArrayK, SOGoDraftObjectK;
|
||||
|
||||
typedef void (*getMessageData_inMemCtx_) (MAPIStoreMessage *, SEL,
|
||||
struct mapistore_message **,
|
||||
TALLOC_CTX *);
|
||||
|
||||
@implementation SOGoDraftObject (MAPIStoreExtension)
|
||||
|
||||
- (Class) mapistoreMessageClass
|
||||
{
|
||||
return [MAPIStoreDraftsMessage class];
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@implementation MAPIStoreDraftsMessage
|
||||
|
||||
+ (void) initialize
|
||||
{
|
||||
NGMailAddressK = [NGMailAddress class];
|
||||
NSArrayK = [NSArray class];
|
||||
SOGoDraftObjectK = [SOGoDraftObject class];
|
||||
}
|
||||
|
||||
- (uint64_t) objectVersion
|
||||
{
|
||||
return ([sogoObject isKindOfClass: SOGoDraftObjectK]
|
||||
? ULLONG_MAX
|
||||
: [super objectVersion]);
|
||||
}
|
||||
|
||||
- (void) _fetchHeaderData
|
||||
{
|
||||
[sogoObject fetchInfo];
|
||||
ASSIGN (headerMimeType, ([sogoObject isHTML] ? @"text/html" : @"text/plain"));
|
||||
ASSIGN (headerEncoding, @"8bit");
|
||||
ASSIGN (headerCharset, @"utf-8");
|
||||
headerSetup = YES;
|
||||
}
|
||||
|
||||
- (void) _fetchBodyData
|
||||
{
|
||||
ASSIGN (bodyContent,
|
||||
[[sogoObject text] dataUsingEncoding: NSUTF8StringEncoding]);
|
||||
bodySetup = YES;
|
||||
}
|
||||
|
||||
- (void) getMessageData: (struct mapistore_message **) dataPtr
|
||||
inMemCtx: (TALLOC_CTX *) memCtx
|
||||
{
|
||||
NSArray *to;
|
||||
NSInteger count, max, p;
|
||||
NSString *username, *cn, *email;
|
||||
NSData *entryId;
|
||||
NSDictionary *contactInfos;
|
||||
SOGoUserManager *mgr;
|
||||
NSDictionary *headers;
|
||||
NGMailAddress *currentAddress;
|
||||
NGMailAddressParser *parser;
|
||||
struct mapistore_message *msgData;
|
||||
struct mapistore_message_recipient *recipient;
|
||||
getMessageData_inMemCtx_ superMethod;
|
||||
|
||||
if ([sogoObject isKindOfClass: SOGoDraftObjectK])
|
||||
{
|
||||
/* FIXME: this is a hack designed to work-around a hierarchy issue between
|
||||
SOGoMailObject and SOGoDraftObject */
|
||||
superMethod = (getMessageData_inMemCtx_)
|
||||
[MAPIStoreMessage instanceMethodForSelector: _cmd];
|
||||
superMethod (self, _cmd, &msgData, memCtx);
|
||||
|
||||
/* Retrieve recipients from the message */
|
||||
if (!headerSetup)
|
||||
[self _fetchHeaderData];
|
||||
headers = [sogoObject headers];
|
||||
|
||||
to = [headers objectForKey: @"to"];
|
||||
max = [to count];
|
||||
|
||||
msgData->columns = set_SPropTagArray (msgData, 9,
|
||||
PR_OBJECT_TYPE,
|
||||
PR_DISPLAY_TYPE,
|
||||
PR_7BIT_DISPLAY_NAME_UNICODE,
|
||||
PR_SMTP_ADDRESS_UNICODE,
|
||||
PR_SEND_INTERNET_ENCODING,
|
||||
PR_RECIPIENT_DISPLAY_NAME_UNICODE,
|
||||
PR_RECIPIENT_FLAGS,
|
||||
PR_RECIPIENT_ENTRYID,
|
||||
PR_RECIPIENT_TRACKSTATUS);
|
||||
|
||||
if (max > 0)
|
||||
{
|
||||
mgr = [SOGoUserManager sharedUserManager];
|
||||
msgData->recipients_count = max;
|
||||
msgData->recipients = talloc_array (msgData, struct mapistore_message_recipient *, max);
|
||||
for (count = 0; count < max; count++)
|
||||
{
|
||||
msgData->recipients[count]
|
||||
= talloc_zero (msgData, struct mapistore_message_recipient);
|
||||
recipient = msgData->recipients[count];
|
||||
recipient->data = talloc_array (msgData, void *, msgData->columns->cValues);
|
||||
memset (recipient->data, 0, msgData->columns->cValues * sizeof (void *));
|
||||
|
||||
email = nil;
|
||||
cn = nil;
|
||||
|
||||
parser = [NGMailAddressParser
|
||||
mailAddressParserWithString: [to objectAtIndex: count]];
|
||||
currentAddress = [parser parse];
|
||||
if ([currentAddress isKindOfClass: NGMailAddressK])
|
||||
{
|
||||
email = [currentAddress address];
|
||||
cn = [currentAddress displayName];
|
||||
contactInfos = [mgr contactInfosForUserWithUIDorEmail: email];
|
||||
|
||||
// PR_ACCOUNT_UNICODE
|
||||
if (contactInfos)
|
||||
{
|
||||
username = [contactInfos objectForKey: @"c_uid"];
|
||||
recipient->username = [username asUnicodeInMemCtx: msgData];
|
||||
entryId = MAPIStoreInternalEntryId (username);
|
||||
}
|
||||
else
|
||||
entryId = MAPIStoreExternalEntryId (cn, email);
|
||||
}
|
||||
else
|
||||
{
|
||||
entryId = nil;
|
||||
[self warnWithFormat: @"address could not be parsed"
|
||||
@" properly (ignored)"];
|
||||
}
|
||||
recipient->type = MAPI_TO;
|
||||
|
||||
/* properties */
|
||||
p = 0;
|
||||
recipient->data = talloc_array (msgData, void *, msgData->columns->cValues);
|
||||
memset (recipient->data, 0, msgData->columns->cValues * sizeof (void *));
|
||||
|
||||
// PR_OBJECT_TYPE = MAPI_MAILUSER (see MAPI_OBJTYPE)
|
||||
recipient->data[p] = MAPILongValue (msgData, MAPI_MAILUSER);
|
||||
p++;
|
||||
|
||||
// PR_DISPLAY_TYPE = DT_MAILUSER (see MS-NSPI)
|
||||
recipient->data[p] = MAPILongValue (msgData, 0);
|
||||
p++;
|
||||
|
||||
// PR_7BIT_DISPLAY_NAME_UNICODE
|
||||
recipient->data[p] = [cn asUnicodeInMemCtx: msgData];
|
||||
p++;
|
||||
|
||||
// PR_SMTP_ADDRESS_UNICODE
|
||||
recipient->data[p] = [email asUnicodeInMemCtx: msgData];
|
||||
p++;
|
||||
|
||||
// PR_SEND_INTERNET_ENCODING = 0x00060000 (plain text, see OXCMAIL)
|
||||
recipient->data[p] = MAPILongValue (msgData, 0x00060000);
|
||||
p++;
|
||||
|
||||
// PR_RECIPIENT_DISPLAY_NAME_UNICODE
|
||||
recipient->data[p] = [cn asUnicodeInMemCtx: msgData];
|
||||
p++;
|
||||
|
||||
// PR_RECIPIENT_FLAGS
|
||||
recipient->data[p] = NULL;
|
||||
p++;
|
||||
|
||||
// PR_RECIPIENT_ENTRYID
|
||||
recipient->data[p] = [entryId asShortBinaryInMemCtx: msgData];
|
||||
p++;
|
||||
}
|
||||
}
|
||||
*dataPtr = msgData;
|
||||
}
|
||||
else
|
||||
[super getMessageData: dataPtr inMemCtx: memCtx];
|
||||
}
|
||||
|
||||
- (int) getPrIconIndex: (void **) data
|
||||
inMemCtx: (TALLOC_CTX *) memCtx
|
||||
{
|
||||
int rc;
|
||||
|
||||
if ([sogoObject isKindOfClass: SOGoDraftObjectK])
|
||||
{
|
||||
*data = MAPILongValue (memCtx, 0xffffffff);
|
||||
rc = MAPISTORE_SUCCESS;
|
||||
}
|
||||
else
|
||||
rc = [super getPrIconIndex: data inMemCtx: memCtx];
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
- (int) getPrImportance: (void **) data
|
||||
inMemCtx: (TALLOC_CTX *) memCtx
|
||||
{
|
||||
int rc;
|
||||
uint32_t v;
|
||||
NSString *s;
|
||||
|
||||
if ([sogoObject isKindOfClass: SOGoDraftObjectK])
|
||||
{
|
||||
if (!headerSetup)
|
||||
[self _fetchHeaderData];
|
||||
s = [[sogoObject headers] objectForKey: @"X-Priority"];
|
||||
v = 0x1;
|
||||
|
||||
if ([s hasPrefix: @"1"]) v = 0x2;
|
||||
else if ([s hasPrefix: @"2"]) v = 0x2;
|
||||
else if ([s hasPrefix: @"4"]) v = 0x0;
|
||||
else if ([s hasPrefix: @"5"]) v = 0x0;
|
||||
|
||||
*data = MAPILongValue (memCtx, v);
|
||||
|
||||
rc = MAPISTORE_SUCCESS;
|
||||
}
|
||||
else
|
||||
rc = [super getPrImportance: data inMemCtx: memCtx];
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
- (int) getPrMessageFlags: (void **) data
|
||||
inMemCtx: (TALLOC_CTX *) memCtx
|
||||
{
|
||||
unsigned int v = MSGFLAG_FROMME;
|
||||
int rc;
|
||||
|
||||
if ([sogoObject isKindOfClass: SOGoDraftObjectK])
|
||||
{
|
||||
if ([[self attachmentKeys] count] > 0)
|
||||
v |= MSGFLAG_HASATTACH;
|
||||
|
||||
*data = MAPILongValue (memCtx, v);
|
||||
rc = MAPISTORE_SUCCESS;
|
||||
}
|
||||
else
|
||||
rc = [super getPrMessageFlags: data inMemCtx: memCtx];
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
- (int) getPrFlagStatus: (void **) data
|
||||
inMemCtx: (TALLOC_CTX *) memCtx
|
||||
{
|
||||
return ([sogoObject isKindOfClass: SOGoDraftObjectK]
|
||||
? [self getLongZero: data inMemCtx: memCtx]
|
||||
: [super getPrFlagStatus: data inMemCtx: memCtx]);
|
||||
}
|
||||
|
||||
- (int) getPrFollowupIcon: (void **) data
|
||||
inMemCtx: (TALLOC_CTX *) memCtx
|
||||
{
|
||||
return ([sogoObject isKindOfClass: SOGoDraftObjectK]
|
||||
? [self getLongZero: data inMemCtx: memCtx]
|
||||
: [super getPrFollowupIcon: data inMemCtx: memCtx]);
|
||||
}
|
||||
|
||||
- (int) getPrChangeKey: (void **) data
|
||||
inMemCtx: (TALLOC_CTX *) memCtx
|
||||
{
|
||||
return MAPISTORE_ERR_NOT_FOUND;
|
||||
}
|
||||
|
||||
- (int) getPrPredecessorChangeList: (void **) data
|
||||
inMemCtx: (TALLOC_CTX *) memCtx
|
||||
{
|
||||
return MAPISTORE_ERR_NOT_FOUND;
|
||||
}
|
||||
|
||||
- (int) getPidLidImapDeleted: (void **) data
|
||||
inMemCtx: (TALLOC_CTX *) memCtx
|
||||
{
|
||||
return MAPISTORE_ERR_NOT_FOUND;
|
||||
}
|
||||
|
||||
- (int) getPrInternetMessageId: (void **) data
|
||||
inMemCtx: (TALLOC_CTX *) memCtx
|
||||
{
|
||||
return MAPISTORE_ERR_NOT_FOUND;
|
||||
}
|
||||
|
||||
- (void) _saveAttachment: (NSString *) attachmentKey
|
||||
{
|
||||
NSDictionary *attProperties, *metadata;
|
||||
NSString *filename, *mimeType;
|
||||
NSData *content;
|
||||
MAPIStoreAttachment *attachment;
|
||||
|
||||
attachment = [attachmentParts objectForKey: attachmentKey];
|
||||
attProperties = [attachment properties];
|
||||
filename
|
||||
= [attProperties
|
||||
objectForKey: MAPIPropertyKey (PR_ATTACH_LONG_FILENAME_UNICODE)];
|
||||
if (![filename length])
|
||||
{
|
||||
filename
|
||||
= [attProperties
|
||||
objectForKey: MAPIPropertyKey (PR_ATTACH_FILENAME_UNICODE)];
|
||||
if (![filename length])
|
||||
filename = @"untitled.bin";
|
||||
}
|
||||
|
||||
mimeType = [attProperties
|
||||
objectForKey: MAPIPropertyKey (PR_ATTACH_MIME_TAG_UNICODE)];
|
||||
if (!mimeType)
|
||||
mimeType = [[MAPIStoreMIME sharedMAPIStoreMIME]
|
||||
mimeTypeForExtension: [filename pathExtension]];
|
||||
if (!mimeType)
|
||||
mimeType = @"application/octet-stream";
|
||||
|
||||
content = [attProperties objectForKey: MAPIPropertyKey (PR_ATTACH_DATA_BIN)];
|
||||
if (content)
|
||||
{
|
||||
metadata = [NSDictionary dictionaryWithObjectsAndKeys:
|
||||
filename, @"filename",
|
||||
mimeType, @"mimetype", nil];
|
||||
[sogoObject saveAttachment: content withMetadata: metadata];
|
||||
}
|
||||
else
|
||||
[self errorWithFormat: @"no content for attachment"];
|
||||
}
|
||||
|
||||
- (void) _commitProperties
|
||||
{
|
||||
static NSString *recIds[] = { @"to", @"cc", @"bcc" };
|
||||
NSArray *list;
|
||||
NSDictionary *recipients, *identity;
|
||||
NSMutableDictionary *newHeaders;
|
||||
NSString *recId, *body;
|
||||
NSMutableString *subject;
|
||||
NSUInteger count, max;
|
||||
WOContext *woContext;
|
||||
id value;
|
||||
|
||||
newHeaders = [NSMutableDictionary dictionaryWithCapacity: 7];
|
||||
|
||||
/* save the recipients */
|
||||
recipients = [properties objectForKey: @"recipients"];
|
||||
if (recipients)
|
||||
{
|
||||
for (count = 0; count < 3; count++)
|
||||
{
|
||||
recId = recIds[count];
|
||||
list = [recipients objectForKey: recId];
|
||||
if ([list count] > 0)
|
||||
[newHeaders setObject: [list objectsForKey: @"email"
|
||||
notFoundMarker: nil]
|
||||
forKey: recId];
|
||||
}
|
||||
}
|
||||
else
|
||||
[self errorWithFormat: @"message without recipients"];
|
||||
|
||||
/*
|
||||
message properties (20):
|
||||
recipients: {to = ({email = "wsourdeau@inverse.ca"; fullName = "wsourdeau@inverse.ca"; }); }
|
||||
0x1000001f (PR_BODY_UNICODE): text body (GSCBufferString)
|
||||
0x0037001f (PR_SUBJECT_UNICODE): Test without (GSCBufferString)
|
||||
0x30070040 (PR_CREATION_TIME): 2010-11-24 13:45:38 -0500 (NSCalendarDate)
|
||||
e)
|
||||
2010-11-24 13:45:38.715 samba[25685] 0x0e62000b (PR_URL_COMP_NAME_SET):
|
||||
0 (NSIntNumber) */
|
||||
|
||||
/* save the subject */
|
||||
subject = [NSMutableString stringWithCapacity: 128];
|
||||
value = [properties objectForKey: MAPIPropertyKey (PR_SUBJECT_UNICODE)];
|
||||
if (value)
|
||||
[subject appendString: value];
|
||||
else
|
||||
{
|
||||
value = [properties
|
||||
objectForKey: MAPIPropertyKey (PR_SUBJECT_PREFIX_UNICODE)];
|
||||
if (value)
|
||||
[subject appendString: value];
|
||||
value = [properties
|
||||
objectForKey: MAPIPropertyKey (PR_NORMALIZED_SUBJECT_UNICODE)];
|
||||
if (value)
|
||||
[subject appendString: value];
|
||||
}
|
||||
if (subject)
|
||||
[newHeaders setObject: subject forKey: @"subject"];
|
||||
|
||||
/* generate a valid from */
|
||||
woContext = [[self context] woContext];
|
||||
identity = [[woContext activeUser] primaryIdentity];
|
||||
[newHeaders setObject: [identity keysWithFormat: @"%{fullName} <%{email}>"]
|
||||
forKey: @"from"];
|
||||
|
||||
/* set the proper priority:
|
||||
0x00000000 == Low importance
|
||||
0x00000001 == Normal importance
|
||||
0x00000002 == High importance */
|
||||
value = [properties objectForKey: MAPIPropertyKey (PR_IMPORTANCE)];
|
||||
if (value && [value intValue] == 0x0)
|
||||
{
|
||||
[newHeaders setObject: @"LOW" forKey: @"priority"];
|
||||
}
|
||||
else if (value && [value intValue] == 0x2)
|
||||
{
|
||||
[newHeaders setObject: @"HIGH" forKey: @"priority"];
|
||||
}
|
||||
|
||||
/* save the newly generated headers */
|
||||
[sogoObject setHeaders: newHeaders];
|
||||
|
||||
value = [properties objectForKey: MAPIPropertyKey (PR_HTML)];
|
||||
if (value)
|
||||
{
|
||||
[sogoObject setIsHTML: YES];
|
||||
// TODO: encoding
|
||||
body = [[NSString alloc] initWithData: value
|
||||
encoding: NSUTF8StringEncoding];
|
||||
[sogoObject setText: body];
|
||||
[body release];
|
||||
}
|
||||
else
|
||||
{
|
||||
value = [properties objectForKey: MAPIPropertyKey (PR_BODY_UNICODE)];
|
||||
if (value)
|
||||
{
|
||||
[sogoObject setIsHTML: NO];
|
||||
[sogoObject setText: value];
|
||||
}
|
||||
}
|
||||
|
||||
/* save attachments */
|
||||
max = [[self attachmentKeys] count];
|
||||
for (count = 0; count < max; count++)
|
||||
[self _saveAttachment: [attachmentKeys objectAtIndex: count]];
|
||||
}
|
||||
|
||||
- (int) _getAddressHeader: (void **) data
|
||||
addressKey: (NSString *) key
|
||||
inMemCtx: (TALLOC_CTX *) memCtx
|
||||
{
|
||||
NSString *stringValue, *address;
|
||||
NGMailAddress *currentAddress;
|
||||
NGMailAddressParser *parser;
|
||||
id to;
|
||||
|
||||
if (!headerSetup)
|
||||
[self _fetchHeaderData];
|
||||
|
||||
stringValue = @"";
|
||||
|
||||
to = [[sogoObject headers] objectForKey: key];
|
||||
if ([to isKindOfClass: NSArrayK])
|
||||
{
|
||||
if ([to count] > 0)
|
||||
address = [to objectAtIndex: 0];
|
||||
else
|
||||
address = @"";
|
||||
}
|
||||
else
|
||||
address = to;
|
||||
|
||||
parser = [NGMailAddressParser mailAddressParserWithString: address];
|
||||
currentAddress = [parser parse];
|
||||
if ([currentAddress isKindOfClass: NGMailAddressK])
|
||||
{
|
||||
stringValue = [currentAddress address];
|
||||
if (!stringValue)
|
||||
stringValue = @"";
|
||||
}
|
||||
*data = [stringValue asUnicodeInMemCtx: memCtx];
|
||||
|
||||
return MAPISTORE_SUCCESS;
|
||||
}
|
||||
|
||||
- (int) getPrSenderEmailAddress: (void **) data
|
||||
inMemCtx: (TALLOC_CTX *) memCtx
|
||||
{
|
||||
return ([sogoObject isKindOfClass: SOGoDraftObjectK]
|
||||
? [self _getAddressHeader: data
|
||||
addressKey: @"from"
|
||||
inMemCtx: memCtx]
|
||||
: [super getPrSenderEmailAddress: data inMemCtx: memCtx]);
|
||||
}
|
||||
|
||||
- (int) getPrSenderName: (void **) data
|
||||
inMemCtx: (TALLOC_CTX *) memCtx
|
||||
{
|
||||
return MAPISTORE_ERR_NOT_FOUND;
|
||||
}
|
||||
|
||||
- (int) getPrSenderEntryid: (void **) data inMemCtx: (TALLOC_CTX *) memCtx
|
||||
{
|
||||
return MAPISTORE_ERR_NOT_FOUND;
|
||||
}
|
||||
|
||||
- (int) getPrReceivedByEmailAddress: (void **) data
|
||||
inMemCtx: (TALLOC_CTX *) memCtx
|
||||
{
|
||||
return ([sogoObject isKindOfClass: SOGoDraftObjectK]
|
||||
? [self _getAddressHeader: data
|
||||
addressKey: @"to"
|
||||
inMemCtx: memCtx]
|
||||
: [super getPrReceivedByEmailAddress: data inMemCtx: memCtx]);
|
||||
}
|
||||
|
||||
- (int) getPrDisplayTo: (void **) data
|
||||
inMemCtx: (TALLOC_CTX *) memCtx
|
||||
{
|
||||
return ([sogoObject isKindOfClass: SOGoDraftObjectK]
|
||||
? [self _getAddressHeader: data
|
||||
addressKey: @"to"
|
||||
inMemCtx: memCtx]
|
||||
: [super getPrDisplayTo: data inMemCtx: memCtx]);
|
||||
}
|
||||
|
||||
- (int) getPrDisplayCc: (void **) data
|
||||
inMemCtx: (TALLOC_CTX *) memCtx
|
||||
{
|
||||
return ([sogoObject isKindOfClass: SOGoDraftObjectK]
|
||||
? [self _getAddressHeader: data
|
||||
addressKey: @"cc"
|
||||
inMemCtx: memCtx]
|
||||
: [super getPrDisplayCc: data inMemCtx: memCtx]);
|
||||
}
|
||||
|
||||
- (int) getPrDisplayBcc: (void **) data
|
||||
inMemCtx: (TALLOC_CTX *) memCtx
|
||||
{
|
||||
return ([sogoObject isKindOfClass: SOGoDraftObjectK]
|
||||
? [self _getAddressHeader: data
|
||||
addressKey: @"cc"
|
||||
inMemCtx: memCtx]
|
||||
: [super getPrDisplayBcc: data inMemCtx: memCtx]);
|
||||
}
|
||||
|
||||
- (NSArray *) attachmentKeysMatchingQualifier: (EOQualifier *) qualifier
|
||||
andSortOrderings: (NSArray *) sortOrderings
|
||||
{
|
||||
NSArray *keys;
|
||||
|
||||
if ([sogoObject isKindOfClass: SOGoDraftObjectK])
|
||||
{
|
||||
if (qualifier)
|
||||
[self errorWithFormat: @"qualifier is not used for attachments"];
|
||||
if (sortOrderings)
|
||||
[self errorWithFormat: @"sort orderings are not used for attachments"];
|
||||
|
||||
keys = [attachmentParts allKeys];
|
||||
}
|
||||
else
|
||||
keys = [super attachmentKeysMatchingQualifier: qualifier
|
||||
andSortOrderings: sortOrderings];
|
||||
|
||||
return keys;
|
||||
}
|
||||
|
||||
- (id) lookupAttachment: (NSString *) childKey
|
||||
{
|
||||
return ([sogoObject isKindOfClass: SOGoDraftObjectK]
|
||||
? [attachmentParts objectForKey: childKey]
|
||||
: [super lookupAttachment: childKey]);
|
||||
}
|
||||
|
||||
- (void) submit
|
||||
{
|
||||
NSString *msgClass;
|
||||
NSException *error;
|
||||
MAPIStoreMapping *mapping;
|
||||
|
||||
msgClass = [properties
|
||||
objectForKey: MAPIPropertyKey (PR_MESSAGE_CLASS_UNICODE)];
|
||||
if (![msgClass isEqualToString: @"IPM.Schedule.Meeting.Request"])
|
||||
{
|
||||
[self logWithFormat: @"sending message"];
|
||||
[self _commitProperties];
|
||||
error = [(SOGoDraftObject *) sogoObject sendMailAndCopyToSent: NO];
|
||||
if (error)
|
||||
[self errorWithFormat: @"an exception occurred: %@", error];
|
||||
}
|
||||
else
|
||||
[self logWithFormat: @"ignored scheduling message"];
|
||||
|
||||
mapping = [[self context] mapping];
|
||||
[mapping unregisterURLWithID: [self objectId]];
|
||||
}
|
||||
|
||||
- (int) submitWithFlags: (enum SubmitFlags) flags
|
||||
{
|
||||
int rc;
|
||||
|
||||
if ([sogoObject isKindOfClass: SOGoDraftObjectK])
|
||||
{
|
||||
[self submit];
|
||||
[self setIsNew: NO];
|
||||
[self resetProperties];
|
||||
[[self container] cleanupCaches];
|
||||
rc = MAPISTORE_SUCCESS;
|
||||
}
|
||||
else
|
||||
{
|
||||
[self errorWithFormat: @"'submit' cannot be invoked on instances of '%@'",
|
||||
NSStringFromClass ([sogoObject class])];
|
||||
rc = MAPISTORE_ERROR;
|
||||
}
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
- (void) save
|
||||
{
|
||||
NSString *msgClass;
|
||||
|
||||
if ([sogoObject isKindOfClass: SOGoDraftObjectK])
|
||||
{
|
||||
msgClass = [properties
|
||||
objectForKey: MAPIPropertyKey (PR_MESSAGE_CLASS_UNICODE)];
|
||||
if (![msgClass isEqualToString: @"IPM.Schedule.Meeting.Request"])
|
||||
{
|
||||
[self logWithFormat: @"saving message"];
|
||||
[self _commitProperties];
|
||||
[(SOGoDraftObject *) sogoObject save];
|
||||
}
|
||||
else
|
||||
[self logWithFormat: @"ignored scheduling message"];
|
||||
}
|
||||
else
|
||||
[self errorWithFormat: @"'save' cannot be invoked on instances of '%@'",
|
||||
NSStringFromClass ([sogoObject class])];
|
||||
}
|
||||
|
||||
- (NSString *) subject
|
||||
{
|
||||
NSString *subject;
|
||||
|
||||
if ([sogoObject isKindOfClass: SOGoDraftObjectK])
|
||||
{
|
||||
subject = [properties objectForKey: MAPIPropertyKey (PR_SUBJECT_UNICODE)];
|
||||
if (!subject)
|
||||
{
|
||||
if (!headerSetup)
|
||||
[self _fetchHeaderData];
|
||||
subject = [[sogoObject headers] objectForKey: @"subject"];
|
||||
}
|
||||
}
|
||||
else
|
||||
subject = [super subject];
|
||||
|
||||
return subject;
|
||||
}
|
||||
|
||||
- (NSDate *) creationTime
|
||||
{
|
||||
return ([sogoObject isKindOfClass: SOGoDraftObjectK]
|
||||
? (NSDate *) [properties objectForKey: MAPIPropertyKey (PR_CREATION_TIME)]
|
||||
: [super creationTime]);
|
||||
}
|
||||
|
||||
- (NSDate *) lastModificationTime
|
||||
{
|
||||
return ([sogoObject isKindOfClass: SOGoDraftObjectK]
|
||||
? (NSDate *) [properties
|
||||
objectForKey: MAPIPropertyKey (PR_LAST_MODIFICATION_TIME)]
|
||||
: [super lastModificationTime]);
|
||||
}
|
||||
|
||||
@end
|
||||
@@ -23,13 +23,9 @@
|
||||
#ifndef MAPISTOREFSMESSAGE_H
|
||||
#define MAPISTOREFSMESSAGE_H
|
||||
|
||||
#import "MAPIStoreMessage.h"
|
||||
|
||||
@interface MAPIStoreFSMessage : MAPIStoreMessage
|
||||
{
|
||||
BOOL fetchedAttachments;
|
||||
}
|
||||
#import "MAPIStoreMemMessage.h"
|
||||
|
||||
@interface MAPIStoreFSMessage : MAPIStoreMemMessage
|
||||
@end
|
||||
|
||||
#endif /* MAPISTOREFSMESSAGE_H */
|
||||
|
||||
@@ -20,36 +20,24 @@
|
||||
* Boston, MA 02111-1307, USA.
|
||||
*/
|
||||
|
||||
#import <Foundation/NSArray.h>
|
||||
#import <Foundation/NSData.h>
|
||||
#import <Foundation/NSDictionary.h>
|
||||
#import <Foundation/NSString.h>
|
||||
#import <Foundation/NSValue.h>
|
||||
#import <NGExtensions/NSObject+Logs.h>
|
||||
|
||||
#import "MAPIStoreContext.h"
|
||||
#import "MAPIStorePropertySelectors.h"
|
||||
#import "MAPIStoreTypes.h"
|
||||
#import "NSData+MAPIStore.h"
|
||||
#import "NSObject+MAPIStore.h"
|
||||
#import "NSString+MAPIStore.h"
|
||||
#import "SOGoMAPIFSMessage.h"
|
||||
|
||||
#import "MAPIStoreFSMessage.h"
|
||||
#import "MAPIStoreTypes.h"
|
||||
#import "NSData+MAPIStore.h"
|
||||
|
||||
#undef DEBUG
|
||||
#include <mapistore/mapistore.h>
|
||||
#include <mapistore/mapistore_errors.h>
|
||||
|
||||
Class NSNumberK;
|
||||
|
||||
@implementation MAPIStoreFSMessage
|
||||
|
||||
+ (void) initialize
|
||||
{
|
||||
NSNumberK = [NSNumber class];
|
||||
}
|
||||
|
||||
+ (int) getAvailableProperties: (struct SPropTagArray **) propertiesP
|
||||
inMemCtx: (TALLOC_CTX *) memCtx
|
||||
{
|
||||
@@ -77,145 +65,6 @@ Class NSNumberK;
|
||||
return MAPISTORE_SUCCESS;
|
||||
}
|
||||
|
||||
- (id) init
|
||||
{
|
||||
if ((self = [super init]))
|
||||
fetchedAttachments = NO;
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
- (uint64_t) objectVersion
|
||||
{
|
||||
NSNumber *version;
|
||||
|
||||
version = [[sogoObject properties] objectForKey: @"version"];
|
||||
|
||||
return (version
|
||||
? exchange_globcnt ([version unsignedLongLongValue])
|
||||
: ULLONG_MAX);
|
||||
}
|
||||
|
||||
- (int) getProperty: (void **) data
|
||||
withTag: (enum MAPITAGS) propTag
|
||||
inMemCtx: (TALLOC_CTX *) memCtx
|
||||
{
|
||||
id value;
|
||||
int rc;
|
||||
|
||||
value = [[sogoObject properties] objectForKey: MAPIPropertyKey (propTag)];
|
||||
if (value)
|
||||
rc = [value getMAPIValue: data forTag: propTag inMemCtx: memCtx];
|
||||
else
|
||||
rc = [super getProperty: data withTag: propTag inMemCtx: memCtx];
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
- (int) getPrSubject: (void **) data inMemCtx: (TALLOC_CTX *) memCtx
|
||||
{
|
||||
/* if we get here, it means that the properties file didn't contain a
|
||||
relevant value */
|
||||
return [self getEmptyString: data inMemCtx: memCtx];
|
||||
}
|
||||
|
||||
- (int) getPrMessageClass: (void **) data inMemCtx: (TALLOC_CTX *) memCtx
|
||||
{
|
||||
/* if we get here, it means that the properties file didn't contain a
|
||||
relevant value */
|
||||
|
||||
*data = [@"IPM.Note" asUnicodeInMemCtx: memCtx];
|
||||
|
||||
return MAPISTORE_SUCCESS;
|
||||
}
|
||||
|
||||
- (int) getPrChangeKey: (void **) data inMemCtx: (TALLOC_CTX *) memCtx
|
||||
{
|
||||
NSData *changeKey;
|
||||
int rc;
|
||||
|
||||
changeKey = [[sogoObject properties]
|
||||
objectForKey: MAPIPropertyKey (PR_CHANGE_KEY)];
|
||||
if (changeKey)
|
||||
{
|
||||
*data = [changeKey asBinaryInMemCtx: memCtx];
|
||||
rc = MAPISTORE_SUCCESS;
|
||||
}
|
||||
else
|
||||
rc = [super getPrChangeKey: data inMemCtx: memCtx];
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
- (int) getAvailableProperties: (struct SPropTagArray **) propertiesP
|
||||
inMemCtx: (TALLOC_CTX *) memCtx
|
||||
{
|
||||
NSArray *keys;
|
||||
NSUInteger count, max;
|
||||
NSString *key;
|
||||
struct SPropTagArray *availableProps;
|
||||
|
||||
keys = [[sogoObject properties] allKeys];
|
||||
max = [keys count];
|
||||
|
||||
availableProps = talloc_zero (NULL, struct SPropTagArray);
|
||||
availableProps->cValues = max;
|
||||
availableProps->aulPropTag = talloc_array (availableProps, enum MAPITAGS, max);
|
||||
for (count = 0; count < max; count++)
|
||||
{
|
||||
// #if (GS_SIZEOF_LONG == 4)
|
||||
// return [NSNumber numberWithUnsignedLong: propTag];
|
||||
// #elif (GS_SIZEOF_INT == 4)
|
||||
// return [NSNumber numberWithUnsignedInt: propTag];
|
||||
// #else
|
||||
key = [keys objectAtIndex: count];
|
||||
if ([key isKindOfClass: NSNumberK])
|
||||
{
|
||||
#if (GS_SIZEOF_LONG == 4)
|
||||
availableProps->aulPropTag[count] = [[keys objectAtIndex: count] unsignedLongValue];
|
||||
#elif (GS_SIZEOF_INT == 4)
|
||||
availableProps->aulPropTag[count] = [[keys objectAtIndex: count] unsignedIntValue];
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
*propertiesP = availableProps;
|
||||
|
||||
return MAPISTORE_SUCCESS;
|
||||
}
|
||||
|
||||
- (NSArray *) attachmentsKeysMatchingQualifier: (EOQualifier *) qualifier
|
||||
andSortOrderings: (NSArray *) sortOrderings
|
||||
{
|
||||
NSDictionary *attachments;
|
||||
NSArray *keys;
|
||||
NSString *key, *newKey;
|
||||
NSUInteger count, max, aid;
|
||||
MAPIStoreAttachment *attachment;
|
||||
|
||||
if (!fetchedAttachments)
|
||||
{
|
||||
attachments = [[sogoObject properties] objectForKey: @"attachments"];
|
||||
keys = [attachments allKeys];
|
||||
max = [keys count];
|
||||
if (max > 0)
|
||||
{
|
||||
aid = [keys count];
|
||||
for (count = 0; count < max; count++)
|
||||
{
|
||||
key = [keys objectAtIndex: count];
|
||||
attachment = [attachments objectForKey: key];
|
||||
newKey = [NSString stringWithFormat: @"%ul", (aid + count)];
|
||||
[attachmentParts setObject: attachment forKey: newKey];
|
||||
}
|
||||
}
|
||||
fetchedAttachments = YES;
|
||||
}
|
||||
|
||||
return [super attachmentKeysMatchingQualifier: qualifier
|
||||
andSortOrderings: sortOrderings];
|
||||
}
|
||||
|
||||
- (void) save
|
||||
{
|
||||
uint64_t newVersion;
|
||||
|
||||
@@ -47,7 +47,7 @@
|
||||
#import "MAPIApplication.h"
|
||||
#import "MAPIStoreAppointmentWrapper.h"
|
||||
#import "MAPIStoreContext.h"
|
||||
#import "MAPIStoreDraftsMessage.h"
|
||||
// #import "MAPIStoreDraftsMessage.h"
|
||||
#import "MAPIStoreFAIMessage.h"
|
||||
#import "MAPIStoreMailMessageTable.h"
|
||||
#import "MAPIStoreMapping.h"
|
||||
@@ -59,7 +59,7 @@
|
||||
/* Those are parts of a hack that enables creating mails to IMAP folders from
|
||||
Exchange properties */
|
||||
#import "SOGoMAPIMemMessage.h"
|
||||
#import "MAPIStoreMemMailMessage.h"
|
||||
#import "MAPIStoreMailVolatileMessage.h"
|
||||
|
||||
#import "MAPIStoreMailFolder.h"
|
||||
|
||||
@@ -991,18 +991,17 @@ _parseCOPYUID (NSString *line, NSArray **destUIDsP)
|
||||
return MAPISTORE_SUCCESS;
|
||||
}
|
||||
|
||||
/* FIXME: this method makes use of the hacky MAPIStoreMemMailMessage */
|
||||
- (MAPIStoreMessage *) createMessage
|
||||
{
|
||||
MAPIStoreDraftsMessage *newMessage;
|
||||
MAPIStoreMailVolatileMessage *newMessage;
|
||||
SOGoMAPIMemMessage *newObject;
|
||||
|
||||
newObject = [SOGoMAPIMemMessage
|
||||
objectWithName: [SOGoObject globallyUniqueObjectId]
|
||||
inContainer: sogoObject];
|
||||
newMessage
|
||||
= [MAPIStoreMemMailMessage mapiStoreObjectWithSOGoObject: newObject
|
||||
inContainer: self];
|
||||
= [MAPIStoreMailVolatileMessage mapiStoreObjectWithSOGoObject: newObject
|
||||
inContainer: self];
|
||||
|
||||
return newMessage;
|
||||
}
|
||||
@@ -1145,19 +1144,6 @@ _parseCOPYUID (NSString *line, NSArray **destUIDsP)
|
||||
return [accountFolder draftsFolderInContext: woContext];
|
||||
}
|
||||
|
||||
- (MAPIStoreMessage *) createMessage
|
||||
{
|
||||
MAPIStoreDraftsMessage *newMessage;
|
||||
SOGoDraftObject *newDraft;
|
||||
|
||||
newDraft = [sogoObject newDraft];
|
||||
newMessage
|
||||
= [MAPIStoreDraftsMessage mapiStoreObjectWithSOGoObject: newDraft
|
||||
inContainer: self];
|
||||
|
||||
return newMessage;
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
// @implementation MAPIStoreDeletedItemsFolder : MAPIStoreMailFolder
|
||||
@@ -1182,17 +1168,4 @@ _parseCOPYUID (NSString *line, NSArray **destUIDsP)
|
||||
return [accountFolder draftsFolderInContext: woContext];
|
||||
}
|
||||
|
||||
- (MAPIStoreMessage *) createMessage
|
||||
{
|
||||
MAPIStoreDraftsMessage *newMessage;
|
||||
SOGoDraftObject *newDraft;
|
||||
|
||||
newDraft = [sogoObject newDraft];
|
||||
newMessage
|
||||
= [MAPIStoreDraftsMessage mapiStoreObjectWithSOGoObject: newDraft
|
||||
inContainer: self];
|
||||
|
||||
return newMessage;
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
/* MAPIStoreMemMailMessage.h - this file is part of SOGo
|
||||
/* MAPIStoreMailVolatileMessage.h - this file is part of SOGo
|
||||
*
|
||||
* Copyright (C) 2011 Inverse inc
|
||||
*
|
||||
@@ -20,13 +20,15 @@
|
||||
* Boston, MA 02111-1307, USA.
|
||||
*/
|
||||
|
||||
#ifndef MAPISTOREMEMMAILMESSAGE_H
|
||||
#define MAPISTOREMEMMAILMESSAGE_H
|
||||
#ifndef MAPISTOREMAILVOLATILEMESSAGE_H
|
||||
#define MAPISTOREMAILVOLATILEMESSAGE_H
|
||||
|
||||
#import "MAPIStoreMessage.h"
|
||||
#import "MAPIStoreMemMessage.h"
|
||||
|
||||
@interface MAPIStoreMemMailMessage : MAPIStoreMessage
|
||||
@interface MAPIStoreMailVolatileMessage : MAPIStoreMemMessage
|
||||
|
||||
- (int) submitWithFlags: (enum SubmitFlags) flags;
|
||||
|
||||
@end
|
||||
|
||||
#endif /* MAPISTOREMEMMAILMESSAGE_H */
|
||||
#endif /* MAPISTOREMAILVOLATILEMESSAGE_H */
|
||||
562
OpenChange/MAPIStoreMailVolatileMessage.m
Normal file
562
OpenChange/MAPIStoreMailVolatileMessage.m
Normal file
@@ -0,0 +1,562 @@
|
||||
/* MAPIStoreMailVolatileMessage.m - this file is part of SOGo
|
||||
*
|
||||
* Copyright (C) 2011 Inverse inc
|
||||
*
|
||||
* Author: Wolfgang Sourdeau <wsourdeau@inverse.ca>
|
||||
*
|
||||
* This file is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 3, or (at your option)
|
||||
* any later version.
|
||||
*
|
||||
* This file is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; see the file COPYING. If not, write to
|
||||
* the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
|
||||
* Boston, MA 02111-1307, USA.
|
||||
*/
|
||||
|
||||
/* TODO:
|
||||
- proper handling of multipart/alternative and multipart/related mime
|
||||
body types
|
||||
- calendar invitations
|
||||
*/
|
||||
|
||||
#import <Foundation/NSArray.h>
|
||||
#import <Foundation/NSData.h>
|
||||
#import <Foundation/NSDictionary.h>
|
||||
#import <Foundation/NSString.h>
|
||||
#import <Foundation/NSValue.h>
|
||||
#import <NGExtensions/NGHashMap.h>
|
||||
#import <NGExtensions/NSObject+Logs.h>
|
||||
#import <NGExtensions/NSString+Encoding.h>
|
||||
#import <NGMime/NGMimeBodyPart.h>
|
||||
#import <NGMime/NGMimeMultipartBody.h>
|
||||
#import <NGMail/NGMimeMessage.h>
|
||||
#import <NGMail/NGMimeMessageGenerator.h>
|
||||
#import <NGImap4/NGImap4Client.h>
|
||||
#import <NGImap4/NGImap4Connection.h>
|
||||
#import <SOGo/NSArray+Utilities.h>
|
||||
#import <SOGo/NSCalendarDate+SOGo.h>
|
||||
#import <SOGo/NSString+Utilities.h>
|
||||
#import <SOGo/SOGoDomainDefaults.h>
|
||||
#import <SOGo/SOGoMailer.h>
|
||||
#import <SOGo/SOGoUser.h>
|
||||
#import <Mailer/SOGoMailFolder.h>
|
||||
#import <Mailer/NSString+Mail.h>
|
||||
|
||||
#import "MAPIStoreAttachment.h"
|
||||
#import "MAPIStoreContext.h"
|
||||
#import "MAPIStoreMailFolder.h"
|
||||
#import "MAPIStoreMIME.h"
|
||||
#import "MAPIStoreMapping.h"
|
||||
#import "MAPIStoreTypes.h"
|
||||
#import "NSObject+MAPIStore.h"
|
||||
#import "NSString+MAPIStore.h"
|
||||
#import "SOGoMAPIMemMessage.h"
|
||||
|
||||
#import "MAPIStoreMailVolatileMessage.h"
|
||||
|
||||
#undef DEBUG
|
||||
#include <mapistore/mapistore.h>
|
||||
#include <mapistore/mapistore_errors.h>
|
||||
|
||||
static NSString *recIds[] = { @"to", @"cc", @"bcc" };
|
||||
|
||||
//
|
||||
// Useful extension that comes from Pantomime which is also
|
||||
// released under the LGPL. We should eventually merge
|
||||
// this with the same category found in SOPE's NGSmtpClient.m
|
||||
// or simply drop sope-mime in favor of Pantomime
|
||||
//
|
||||
@interface NSMutableData (DataCleanupExtension)
|
||||
|
||||
- (NSRange) rangeOfCString: (const char *) theCString;
|
||||
- (NSRange) rangeOfCString: (const char *) theCString
|
||||
options: (unsigned int) theOptions
|
||||
range: (NSRange) theRange;
|
||||
@end
|
||||
|
||||
@implementation NSMutableData (DataCleanupExtension)
|
||||
|
||||
- (NSRange) rangeOfCString: (const char *) theCString
|
||||
{
|
||||
return [self rangeOfCString: theCString
|
||||
options: 0
|
||||
range: NSMakeRange(0,[self length])];
|
||||
}
|
||||
|
||||
-(NSRange) rangeOfCString: (const char *) theCString
|
||||
options: (unsigned int) theOptions
|
||||
range: (NSRange) theRange
|
||||
{
|
||||
const char *b, *bytes;
|
||||
int i, len, slen;
|
||||
|
||||
if (!theCString)
|
||||
{
|
||||
return NSMakeRange(NSNotFound,0);
|
||||
}
|
||||
|
||||
bytes = [self bytes];
|
||||
len = [self length];
|
||||
slen = strlen(theCString);
|
||||
|
||||
b = bytes;
|
||||
|
||||
if (len > theRange.location + theRange.length)
|
||||
{
|
||||
len = theRange.location + theRange.length;
|
||||
}
|
||||
|
||||
if (theOptions == NSCaseInsensitiveSearch)
|
||||
{
|
||||
i = theRange.location;
|
||||
b += i;
|
||||
|
||||
for (; i <= len-slen; i++, b++)
|
||||
{
|
||||
if (!strncasecmp(theCString,b,slen))
|
||||
{
|
||||
return NSMakeRange(i,slen);
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
i = theRange.location;
|
||||
b += i;
|
||||
|
||||
for (; i <= len-slen; i++, b++)
|
||||
{
|
||||
if (!memcmp(theCString,b,slen))
|
||||
{
|
||||
return NSMakeRange(i,slen);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return NSMakeRange(NSNotFound,0);
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@interface MAPIStoreAttachment (MAPIStoreMIME)
|
||||
|
||||
- (NGMimeBodyPart *) asMIMEBodyPart;
|
||||
|
||||
@end
|
||||
|
||||
@implementation MAPIStoreAttachment (MAPIStoreMIME)
|
||||
|
||||
- (NGMimeBodyPart *) asMIMEBodyPart
|
||||
{
|
||||
NGMimeBodyPart *bodyPart = nil;
|
||||
NSString *filename, *mimeType, *baseDisposition, *contentType,
|
||||
*contentDisposition, *contentId;
|
||||
NSData *content;
|
||||
struct mapistore_connection_info *connInfo;
|
||||
SOGoDomainDefaults *dd;
|
||||
SOGoUser *activeUser;
|
||||
NGMutableHashMap *map;
|
||||
|
||||
content = [properties objectForKey: MAPIPropertyKey (PR_ATTACH_DATA_BIN)];
|
||||
if (content)
|
||||
{
|
||||
filename
|
||||
= [properties
|
||||
objectForKey: MAPIPropertyKey (PR_ATTACH_LONG_FILENAME_UNICODE)];
|
||||
if (![filename length])
|
||||
filename
|
||||
= [properties
|
||||
objectForKey: MAPIPropertyKey (PR_ATTACH_FILENAME_UNICODE)];
|
||||
|
||||
mimeType = [properties
|
||||
objectForKey: MAPIPropertyKey (PR_ATTACH_MIME_TAG_UNICODE)];
|
||||
if (!mimeType && [filename length])
|
||||
mimeType = [[MAPIStoreMIME sharedMAPIStoreMIME]
|
||||
mimeTypeForExtension: [filename pathExtension]];
|
||||
if (!mimeType)
|
||||
mimeType = @"application/octet-stream";
|
||||
|
||||
if ([mimeType hasPrefix: @"text/"])
|
||||
{
|
||||
connInfo = [[self context] connectionInfo];
|
||||
activeUser = [SOGoUser userWithLogin: [NSString stringWithUTF8String: connInfo->username]];
|
||||
dd = [activeUser domainDefaults];
|
||||
baseDisposition = ([dd mailAttachTextDocumentsInline]
|
||||
? @"inline" : @"attachment");
|
||||
}
|
||||
else if ([mimeType hasPrefix: @"image/"] || [mimeType hasPrefix: @"message"])
|
||||
baseDisposition = @"inline";
|
||||
else
|
||||
baseDisposition = @"attachment";
|
||||
|
||||
if ([filename length] > 0)
|
||||
{
|
||||
contentType = [NSString stringWithFormat: @"%@; name=\"%@\"",
|
||||
mimeType, filename];
|
||||
contentDisposition = [NSString stringWithFormat: @"%@; filename=\"%@\"",
|
||||
baseDisposition, filename];
|
||||
}
|
||||
else
|
||||
{
|
||||
contentType = mimeType;
|
||||
contentDisposition = baseDisposition;
|
||||
}
|
||||
|
||||
map = [[NGMutableHashMap alloc] initWithCapacity: 16];
|
||||
[map addObject: contentType forKey: @"content-type"];
|
||||
[map addObject: contentDisposition forKey: @"content-disposition"];
|
||||
contentId = [properties
|
||||
objectForKey: MAPIPropertyKey (PR_ATTACH_CONTENT_ID_UNICODE)];
|
||||
if (contentId)
|
||||
[map setObject: [NSString stringWithFormat: @"<%@>", contentId]
|
||||
forKey: @"content-id"];
|
||||
bodyPart = [NGMimeBodyPart bodyPartWithHeader: map];
|
||||
[bodyPart setBody: content];
|
||||
[map release];
|
||||
}
|
||||
else
|
||||
[self errorWithFormat: @"no content for attachment"];
|
||||
|
||||
return bodyPart;
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@implementation MAPIStoreMailVolatileMessage
|
||||
|
||||
/* FIXME: copied from SOGoDraftMessage... */
|
||||
- (NSString *) _quoteSpecials: (NSString *) address
|
||||
{
|
||||
NSString *result, *part, *s2;
|
||||
int i, len;
|
||||
|
||||
// We want to correctly send mails to recipients such as :
|
||||
// foo.bar
|
||||
// foo (bar) <foo@zot.com>
|
||||
// bar, foo <foo@zot.com>
|
||||
if ([address indexOf: '('] >= 0 || [address indexOf: ')'] >= 0
|
||||
|| [address indexOf: '<'] >= 0 || [address indexOf: '>'] >= 0
|
||||
|| [address indexOf: '@'] >= 0 || [address indexOf: ','] >= 0
|
||||
|| [address indexOf: ';'] >= 0 || [address indexOf: ':'] >= 0
|
||||
|| [address indexOf: '\\'] >= 0 || [address indexOf: '"'] >= 0
|
||||
|| [address indexOf: '.'] >= 0
|
||||
|| [address indexOf: '['] >= 0 || [address indexOf: ']'] >= 0)
|
||||
{
|
||||
// We search for the first instance of < from the end
|
||||
// and we quote what was before if we need to
|
||||
len = [address length];
|
||||
i = -1;
|
||||
while (len--)
|
||||
if ([address characterAtIndex: len] == '<')
|
||||
{
|
||||
i = len;
|
||||
break;
|
||||
}
|
||||
|
||||
if (i > 0)
|
||||
{
|
||||
part = [address substringToIndex: i - 1];
|
||||
s2 = [[part stringByReplacingString: @"\\" withString: @"\\\\"]
|
||||
stringByReplacingString: @"\"" withString: @"\\\""];
|
||||
result = [NSString stringWithFormat: @"\"%@\" %@", s2, [address substringFromIndex: i]];
|
||||
}
|
||||
else
|
||||
{
|
||||
s2 = [[address stringByReplacingString: @"\\" withString: @"\\\\"]
|
||||
stringByReplacingString: @"\"" withString: @"\\\""];
|
||||
result = [NSString stringWithFormat: @"\"%@\"", s2];
|
||||
}
|
||||
}
|
||||
else
|
||||
result = address;
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
- (NSArray *) _attachmentBodyParts
|
||||
{
|
||||
NSMutableArray *attachmentBodyParts;
|
||||
NSArray *keys;
|
||||
MAPIStoreAttachment *attachment;
|
||||
NSUInteger count, max;
|
||||
NGMimeBodyPart *mimePart;
|
||||
|
||||
keys = [attachmentParts allKeys];
|
||||
max = [keys count];
|
||||
attachmentBodyParts = [NSMutableArray arrayWithCapacity: max];
|
||||
for (count = 0; count < max; count++)
|
||||
{
|
||||
attachment = [attachmentParts
|
||||
objectForKey: [keys objectAtIndex: count]];
|
||||
mimePart = [attachment asMIMEBodyPart];
|
||||
if (mimePart)
|
||||
[attachmentBodyParts addObject: mimePart];
|
||||
}
|
||||
|
||||
return attachmentBodyParts;
|
||||
}
|
||||
|
||||
- (NSData *) _generateMailData
|
||||
{
|
||||
NSDictionary *mailProperties;
|
||||
NSMutableString *subject;
|
||||
NSString *from, *recId, *messageId, *subjectData, *charset,
|
||||
*mailContentType, *textContentType;
|
||||
NSData *textData, *messageData;
|
||||
NSArray *list, *attParts;
|
||||
NSNumber *codePage;
|
||||
NSCalendarDate *date;
|
||||
NSDictionary *recipients;
|
||||
NGMimeMessage *message;
|
||||
NGMutableHashMap *map, *textMap;
|
||||
NGMimeBodyPart *textBodyPart;
|
||||
NGMimeMultipartBody *multiPart;
|
||||
NGMimeMessageGenerator *generator;
|
||||
NSUInteger count, max;
|
||||
struct mapistore_connection_info *connInfo;
|
||||
SOGoUser *activeUser;
|
||||
|
||||
mailProperties = [sogoObject properties];
|
||||
|
||||
/* headers */
|
||||
map = [[NGMutableHashMap alloc] initWithCapacity: 16];
|
||||
|
||||
connInfo = [[self context] connectionInfo];
|
||||
activeUser
|
||||
= [SOGoUser
|
||||
userWithLogin: [NSString stringWithUTF8String: connInfo->username]];
|
||||
|
||||
from = [NSString stringWithFormat: @"%@ <%@>",
|
||||
[activeUser cn], [[activeUser allEmails] objectAtIndex: 0]];
|
||||
[map setObject: [self _quoteSpecials: from] forKey: @"from"];
|
||||
|
||||
/* save the recipients */
|
||||
recipients = [mailProperties objectForKey: @"recipients"];
|
||||
if (recipients)
|
||||
{
|
||||
for (count = 0; count < 3; count++)
|
||||
{
|
||||
recId = recIds[count];
|
||||
list = [recipients objectForKey: recId];
|
||||
if ([list count] > 0)
|
||||
[map setObjects: [list keysWithFormat: @"%{fullName} <%{email}>"]
|
||||
forKey: recId];
|
||||
}
|
||||
}
|
||||
else
|
||||
[self errorWithFormat: @"message without recipients"];
|
||||
|
||||
subject = [NSMutableString stringWithCapacity: 128];
|
||||
subjectData = [mailProperties objectForKey: MAPIPropertyKey (PR_SUBJECT_PREFIX_UNICODE)];
|
||||
if (subjectData)
|
||||
[subject appendString: subjectData];
|
||||
subjectData = [mailProperties objectForKey: MAPIPropertyKey (PR_NORMALIZED_SUBJECT_UNICODE)];
|
||||
if (subjectData)
|
||||
[subject appendString: subjectData];
|
||||
[map setObject: [subject asQPSubjectString: @"utf-8"] forKey: @"subject"];
|
||||
|
||||
messageId = [mailProperties objectForKey: MAPIPropertyKey (PR_INTERNET_MESSAGE_ID_UNICODE)];
|
||||
if ([messageId length])
|
||||
[map setObject: messageId forKey: @"message-id"];
|
||||
|
||||
date = [mailProperties objectForKey: MAPIPropertyKey (PR_CLIENT_SUBMIT_TIME)];
|
||||
if (date)
|
||||
[map addObject: [date rfc822DateString] forKey: @"date"];
|
||||
[map addObject: @"1.0" forKey: @"MIME-Version"];
|
||||
|
||||
message = [[[NGMimeMessage alloc] initWithHeader: map] autorelease];
|
||||
[map release];
|
||||
|
||||
textData = [mailProperties objectForKey: MAPIPropertyKey (PR_HTML)];
|
||||
if (textData)
|
||||
{
|
||||
/* charset */
|
||||
codePage = [mailProperties objectForKey: MAPIPropertyKey (PR_INTERNET_CPID)];
|
||||
switch ([codePage intValue])
|
||||
{
|
||||
case 20127:
|
||||
charset = @"us-ascii";
|
||||
break;
|
||||
case 28605:
|
||||
charset = @"iso-8859-15";
|
||||
break;
|
||||
case 65001:
|
||||
charset = @"utf-8";
|
||||
break;
|
||||
case 28591:
|
||||
default:
|
||||
charset = @"iso-8859-1";
|
||||
}
|
||||
textContentType = [NSString stringWithFormat: @"text/html; charset=%@",
|
||||
charset];
|
||||
}
|
||||
else
|
||||
{
|
||||
textContentType = @"text/plain; charset=utf-8";
|
||||
textData = [[mailProperties
|
||||
objectForKey: MAPIPropertyKey (PR_BODY_UNICODE)]
|
||||
dataUsingEncoding: NSUTF8StringEncoding];
|
||||
}
|
||||
|
||||
attParts = [self _attachmentBodyParts];
|
||||
max = [attParts count];
|
||||
if (max > 0)
|
||||
{
|
||||
mailContentType = @"multipart/mixed";
|
||||
multiPart = [[NGMimeMultipartBody alloc] initWithPart: message];
|
||||
|
||||
/* text part */
|
||||
textMap = [[NGMutableHashMap alloc] initWithCapacity: 1];
|
||||
[textMap setObject: textContentType forKey: @"content-type"];
|
||||
textBodyPart = [NGMimeBodyPart bodyPartWithHeader: textMap];
|
||||
[textBodyPart setBody: textData];
|
||||
[textMap release];
|
||||
|
||||
[multiPart addBodyPart: textBodyPart];
|
||||
for (count = 0; count < max; count++)
|
||||
[multiPart addBodyPart: [attParts objectAtIndex: count]];
|
||||
|
||||
[message setBody: multiPart];
|
||||
[multiPart release];
|
||||
}
|
||||
else
|
||||
{
|
||||
mailContentType = textContentType;
|
||||
[message setBody: textData];
|
||||
}
|
||||
[map setObject: mailContentType forKey: @"content-type"];
|
||||
|
||||
/* mime message generation */
|
||||
generator = [NGMimeMessageGenerator new];
|
||||
messageData = [generator generateMimeFromPart: message];
|
||||
[generator release];
|
||||
[messageData writeToFile: @"/tmp/mimegen.eml" atomically: NO];
|
||||
|
||||
return messageData;
|
||||
}
|
||||
|
||||
- (int) submitWithFlags: (enum SubmitFlags) flags
|
||||
{
|
||||
NSDictionary *mailProperties, *recipients;
|
||||
NSData *message;
|
||||
NSMutableData *cleanedMessage;
|
||||
NSMutableArray *recipientEmails;
|
||||
NSArray *list;
|
||||
NSString *recId;
|
||||
NSRange r1, r2;
|
||||
NSUInteger count;
|
||||
struct mapistore_connection_info *connInfo;
|
||||
SOGoUser *activeUser;
|
||||
NSString *from;
|
||||
// SOGoMailFolder *sentFolder;
|
||||
SOGoDomainDefaults *dd;
|
||||
NSException *error;
|
||||
MAPIStoreMapping *mapping;
|
||||
|
||||
/* send mail */
|
||||
|
||||
message = [self _generateMailData];
|
||||
cleanedMessage = [message mutableCopy];
|
||||
r1 = [cleanedMessage rangeOfCString: "\r\n\r\n"];
|
||||
r1 = [cleanedMessage rangeOfCString: "\r\nbcc: "
|
||||
options: 0
|
||||
range: NSMakeRange(0,r1.location-1)];
|
||||
if (r1.location != NSNotFound)
|
||||
{
|
||||
// We search for the first \r\n AFTER the Bcc: header and
|
||||
// replace the whole thing with \r\n.
|
||||
r2 = [cleanedMessage rangeOfCString: "\r\n"
|
||||
options: 0
|
||||
range: NSMakeRange(NSMaxRange(r1)+1,[cleanedMessage length]-NSMaxRange(r1)-1)];
|
||||
[cleanedMessage replaceBytesInRange: NSMakeRange(r1.location, NSMaxRange(r2)-r1.location)
|
||||
withBytes: "\r\n"
|
||||
length: 2];
|
||||
}
|
||||
|
||||
mailProperties = [sogoObject properties];
|
||||
recipientEmails = [NSMutableArray arrayWithCapacity: 32];
|
||||
recipients = [mailProperties objectForKey: @"recipients"];
|
||||
for (count = 0; count < 3; count++)
|
||||
{
|
||||
recId = recIds[count];
|
||||
list = [recipients objectForKey: recId];
|
||||
[recipientEmails
|
||||
addObjectsFromArray: [list objectsForKey: @"email"
|
||||
notFoundMarker: nil]];
|
||||
}
|
||||
|
||||
connInfo = [[self context] connectionInfo];
|
||||
activeUser = [SOGoUser userWithLogin: [NSString stringWithUTF8String: connInfo->username]];
|
||||
|
||||
[self logWithFormat: @"recipients: %@", recipientEmails];
|
||||
dd = [activeUser domainDefaults];
|
||||
from = [[activeUser allEmails] objectAtIndex: 0];
|
||||
error = [[SOGoMailer mailerWithDomainDefaults: dd]
|
||||
sendMailData: cleanedMessage
|
||||
toRecipients: recipientEmails
|
||||
sender: from];
|
||||
if (error)
|
||||
[self logWithFormat: @"an error occurred: '%@'", error];
|
||||
|
||||
mapping = [[self context] mapping];
|
||||
[mapping unregisterURLWithID: [self objectId]];
|
||||
[self setIsNew: NO];
|
||||
[self resetProperties];
|
||||
[[self container] cleanupCaches];
|
||||
|
||||
return MAPISTORE_SUCCESS;
|
||||
}
|
||||
|
||||
- (void) save
|
||||
{
|
||||
NSString *folderName, *flag, *newIdString;
|
||||
NSData *changeKey, *messageData;
|
||||
NGImap4Connection *connection;
|
||||
NGImap4Client *client;
|
||||
SOGoMailFolder *containerFolder;
|
||||
NSDictionary *result, *responseResult;
|
||||
MAPIStoreMapping *mapping;
|
||||
uint64_t mid;
|
||||
|
||||
messageData = [self _generateMailData];
|
||||
|
||||
/* appending to imap folder */
|
||||
containerFolder = [container sogoObject];
|
||||
connection = [containerFolder imap4Connection];
|
||||
client = [connection client];
|
||||
folderName = [connection imap4FolderNameForURL: [containerFolder imap4URL]];
|
||||
result = [client append: messageData toFolder: folderName
|
||||
withFlags: [NSArray arrayWithObjects: @"seen", nil]];
|
||||
if ([[result objectForKey: @"result"] boolValue])
|
||||
{
|
||||
/* we reregister the new message URL with the id mapper */
|
||||
responseResult = [[result objectForKey: @"RawResponse"]
|
||||
objectForKey: @"ResponseResult"];
|
||||
flag = [responseResult objectForKey: @"flag"];
|
||||
newIdString = [[flag componentsSeparatedByString: @" "]
|
||||
objectAtIndex: 2];
|
||||
mid = [self objectId];
|
||||
mapping = [[self context] mapping];
|
||||
[mapping unregisterURLWithID: mid];
|
||||
[sogoObject setNameInContainer: [NSString stringWithFormat: @"%@.eml", newIdString]];
|
||||
[mapping registerURL: [self url] withID: mid];
|
||||
}
|
||||
|
||||
/* synchronise the cache and update the change key with the one provided by
|
||||
the client */
|
||||
[(MAPIStoreMailFolder *) container synchroniseCache];
|
||||
changeKey = [[sogoObject properties]
|
||||
objectForKey: MAPIPropertyKey (PR_CHANGE_KEY)];
|
||||
if (changeKey)
|
||||
[(MAPIStoreMailFolder *) container
|
||||
setChangeKey: changeKey forMessageWithKey: [self nameInContainer]];
|
||||
}
|
||||
|
||||
@end
|
||||
@@ -1,356 +0,0 @@
|
||||
/* MAPIStoreMemMailMessage.m - this file is part of SOGo
|
||||
*
|
||||
* Copyright (C) 2011 Inverse inc
|
||||
*
|
||||
* Author: Wolfgang Sourdeau <wsourdeau@inverse.ca>
|
||||
*
|
||||
* This file is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 3, or (at your option)
|
||||
* any later version.
|
||||
*
|
||||
* This file is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; see the file COPYING. If not, write to
|
||||
* the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
|
||||
* Boston, MA 02111-1307, USA.
|
||||
*/
|
||||
|
||||
#import <Foundation/NSArray.h>
|
||||
#import <Foundation/NSDictionary.h>
|
||||
#import <Foundation/NSString.h>
|
||||
#import <Foundation/NSValue.h>
|
||||
#import <NGExtensions/NGHashMap.h>
|
||||
#import <NGExtensions/NSObject+Logs.h>
|
||||
#import <NGExtensions/NSString+Encoding.h>
|
||||
#import <NGMail/NGMimeMessage.h>
|
||||
#import <NGMail/NGMimeMessageGenerator.h>
|
||||
#import <NGImap4/NGImap4Client.h>
|
||||
#import <NGImap4/NGImap4Connection.h>
|
||||
#import <SOGo/NSArray+Utilities.h>
|
||||
#import <SOGo/NSCalendarDate+SOGo.h>
|
||||
#import <SOGo/NSString+Utilities.h>
|
||||
#import <Mailer/SOGoMailFolder.h>
|
||||
#import <Mailer/NSString+Mail.h>
|
||||
|
||||
#import "MAPIStoreContext.h"
|
||||
#import "MAPIStoreMailFolder.h"
|
||||
#import "MAPIStoreMapping.h"
|
||||
#import "MAPIStoreTypes.h"
|
||||
#import "NSObject+MAPIStore.h"
|
||||
#import "NSString+MAPIStore.h"
|
||||
#import "SOGoMAPIMemMessage.h"
|
||||
|
||||
#import "MAPIStoreMemMailMessage.h"
|
||||
|
||||
#undef DEBUG
|
||||
#include <mapistore/mapistore.h>
|
||||
#include <mapistore/mapistore_errors.h>
|
||||
|
||||
Class NSNumberK;
|
||||
|
||||
@implementation MAPIStoreMemMailMessage
|
||||
|
||||
+ (void) initialize
|
||||
{
|
||||
NSNumberK = [NSNumber class];
|
||||
}
|
||||
|
||||
- (int) addPropertiesFromRow: (struct SRow *) aRow
|
||||
{
|
||||
int rc;
|
||||
|
||||
rc = [super addPropertiesFromRow: aRow];
|
||||
if (rc == MAPISTORE_SUCCESS)
|
||||
{
|
||||
[sogoObject appendProperties: properties];
|
||||
[properties removeAllObjects];
|
||||
}
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
- (int) getProperty: (void **) data
|
||||
withTag: (enum MAPITAGS) propTag
|
||||
inMemCtx: (TALLOC_CTX *) memCtx
|
||||
{
|
||||
id value;
|
||||
int rc;
|
||||
|
||||
value = [[sogoObject properties] objectForKey: MAPIPropertyKey (propTag)];
|
||||
if (value)
|
||||
rc = [value getMAPIValue: data forTag: propTag inMemCtx: memCtx];
|
||||
else
|
||||
rc = [super getProperty: data withTag: propTag inMemCtx: memCtx];
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
- (int) getPrMessageClass: (void **) data inMemCtx: (TALLOC_CTX *) memCtx
|
||||
{
|
||||
*data = [@"IPM.Note" asUnicodeInMemCtx: memCtx];
|
||||
|
||||
return MAPISTORE_SUCCESS;
|
||||
}
|
||||
|
||||
- (int) getAvailableProperties: (struct SPropTagArray **) propertiesP
|
||||
inMemCtx: (TALLOC_CTX *) memCtx
|
||||
{
|
||||
NSArray *keys;
|
||||
NSUInteger count, max;
|
||||
NSString *key;
|
||||
struct SPropTagArray *availableProps;
|
||||
|
||||
keys = [[sogoObject properties] allKeys];
|
||||
max = [keys count];
|
||||
|
||||
availableProps = talloc_zero (NULL, struct SPropTagArray);
|
||||
availableProps->cValues = max;
|
||||
availableProps->aulPropTag = talloc_array (availableProps, enum MAPITAGS, max);
|
||||
for (count = 0; count < max; count++)
|
||||
{
|
||||
key = [keys objectAtIndex: count];
|
||||
if ([key isKindOfClass: NSNumberK])
|
||||
{
|
||||
#if (GS_SIZEOF_LONG == 4)
|
||||
availableProps->aulPropTag[count] = [[keys objectAtIndex: count] unsignedLongValue];
|
||||
#elif (GS_SIZEOF_INT == 4)
|
||||
availableProps->aulPropTag[count] = [[keys objectAtIndex: count] unsignedIntValue];
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
*propertiesP = availableProps;
|
||||
|
||||
return MAPISTORE_SUCCESS;
|
||||
}
|
||||
|
||||
- (NSArray *) attachmentsKeysMatchingQualifier: (EOQualifier *) qualifier
|
||||
andSortOrderings: (NSArray *) sortOrderings
|
||||
{
|
||||
NSDictionary *attachments;
|
||||
NSArray *keys;
|
||||
NSString *key, *newKey;
|
||||
NSUInteger count, max, aid;
|
||||
MAPIStoreAttachment *attachment;
|
||||
|
||||
attachments = [[sogoObject properties] objectForKey: @"attachments"];
|
||||
keys = [attachments allKeys];
|
||||
max = [keys count];
|
||||
if (max > 0)
|
||||
{
|
||||
aid = [keys count];
|
||||
for (count = 0; count < max; count++)
|
||||
{
|
||||
key = [keys objectAtIndex: count];
|
||||
attachment = [attachments objectForKey: key];
|
||||
newKey = [NSString stringWithFormat: @"%ul", (aid + count)];
|
||||
[attachmentParts setObject: attachment forKey: newKey];
|
||||
}
|
||||
}
|
||||
|
||||
return [super attachmentKeysMatchingQualifier: qualifier
|
||||
andSortOrderings: sortOrderings];
|
||||
}
|
||||
|
||||
/* FIXME: copied from SOGoDraftMessage... */
|
||||
- (NSString *) _quoteSpecials: (NSString *) address
|
||||
{
|
||||
NSString *result, *part, *s2;
|
||||
int i, len;
|
||||
|
||||
// We want to correctly send mails to recipients such as :
|
||||
// foo.bar
|
||||
// foo (bar) <foo@zot.com>
|
||||
// bar, foo <foo@zot.com>
|
||||
if ([address indexOf: '('] >= 0 || [address indexOf: ')'] >= 0
|
||||
|| [address indexOf: '<'] >= 0 || [address indexOf: '>'] >= 0
|
||||
|| [address indexOf: '@'] >= 0 || [address indexOf: ','] >= 0
|
||||
|| [address indexOf: ';'] >= 0 || [address indexOf: ':'] >= 0
|
||||
|| [address indexOf: '\\'] >= 0 || [address indexOf: '"'] >= 0
|
||||
|| [address indexOf: '.'] >= 0
|
||||
|| [address indexOf: '['] >= 0 || [address indexOf: ']'] >= 0)
|
||||
{
|
||||
// We search for the first instance of < from the end
|
||||
// and we quote what was before if we need to
|
||||
len = [address length];
|
||||
i = -1;
|
||||
while (len--)
|
||||
if ([address characterAtIndex: len] == '<')
|
||||
{
|
||||
i = len;
|
||||
break;
|
||||
}
|
||||
|
||||
if (i > 0)
|
||||
{
|
||||
part = [address substringToIndex: i - 1];
|
||||
s2 = [[part stringByReplacingString: @"\\" withString: @"\\\\"]
|
||||
stringByReplacingString: @"\"" withString: @"\\\""];
|
||||
result = [NSString stringWithFormat: @"\"%@\" %@", s2, [address substringFromIndex: i]];
|
||||
}
|
||||
else
|
||||
{
|
||||
s2 = [[address stringByReplacingString: @"\\" withString: @"\\\\"]
|
||||
stringByReplacingString: @"\"" withString: @"\\\""];
|
||||
result = [NSString stringWithFormat: @"\"%@\"", s2];
|
||||
}
|
||||
}
|
||||
else
|
||||
result = address;
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
- (void) save
|
||||
{
|
||||
static NSString *recIds[] = { @"to", @"cc", @"bcc" };
|
||||
NSDictionary *mailProperties;
|
||||
NSMutableString *subject;
|
||||
NSString *from, *recId, *messageId, *subjectData, *body, *folderName, *flag,
|
||||
*newIdString, *charset;
|
||||
NSData *changeKey, *htmlData, *messageData;
|
||||
NSArray *list;
|
||||
NSNumber *codePage;
|
||||
NSCalendarDate *date;
|
||||
NSDictionary *recipients;
|
||||
NGMimeMessage *message;
|
||||
NGMutableHashMap *map;
|
||||
NGMimeMessageGenerator *generator;
|
||||
NGImap4Connection *connection;
|
||||
NGImap4Client *client;
|
||||
SOGoMailFolder *containerFolder;
|
||||
NSDictionary *result, *responseResult;
|
||||
MAPIStoreMapping *mapping;
|
||||
uint64_t mid;
|
||||
NSUInteger count;
|
||||
|
||||
mailProperties = [sogoObject properties];
|
||||
|
||||
/* headers */
|
||||
map = [[[NGMutableHashMap alloc] initWithCapacity:16] autorelease];
|
||||
|
||||
from = [self _quoteSpecials: [mailProperties objectForKey: MAPIPropertyKey (PR_ORIGINAL_AUTHOR_NAME_UNICODE)]];
|
||||
if ([from length])
|
||||
[map setObject: from forKey: @"from"];
|
||||
|
||||
/* save the recipients */
|
||||
recipients = [mailProperties objectForKey: @"recipients"];
|
||||
if (recipients)
|
||||
{
|
||||
for (count = 0; count < 3; count++)
|
||||
{
|
||||
recId = recIds[count];
|
||||
list = [recipients objectForKey: recId];
|
||||
if ([list count] > 0)
|
||||
[map setObjects: [list keysWithFormat: @"%{fullName} <%{email}>"]
|
||||
forKey: recId];
|
||||
}
|
||||
}
|
||||
else
|
||||
[self errorWithFormat: @"message without recipients"];
|
||||
|
||||
subject = [NSMutableString stringWithCapacity: 128];
|
||||
subjectData = [mailProperties objectForKey: MAPIPropertyKey (PR_SUBJECT_PREFIX_UNICODE)];
|
||||
if (subjectData)
|
||||
[subject appendString: subjectData];
|
||||
subjectData = [mailProperties objectForKey: MAPIPropertyKey (PR_NORMALIZED_SUBJECT_UNICODE)];
|
||||
if (subjectData)
|
||||
[subject appendString: subjectData];
|
||||
[map setObject: [subject asQPSubjectString: @"utf-8"] forKey: @"subject"];
|
||||
|
||||
messageId = [mailProperties objectForKey: MAPIPropertyKey (PR_INTERNET_MESSAGE_ID_UNICODE)];
|
||||
if ([messageId length])
|
||||
[map setObject: messageId forKey: @"message-id"];
|
||||
|
||||
date = [mailProperties objectForKey: MAPIPropertyKey (PR_CLIENT_SUBMIT_TIME)];
|
||||
if (date)
|
||||
[map addObject: [date rfc822DateString] forKey: @"date"];
|
||||
[map addObject: @"1.0" forKey: @"MIME-Version"];
|
||||
|
||||
htmlData = [mailProperties objectForKey: MAPIPropertyKey (PR_HTML)];
|
||||
if (htmlData)
|
||||
{
|
||||
/* charset */
|
||||
charset = @"us-ascii";
|
||||
codePage = [mailProperties objectForKey: MAPIPropertyKey (PR_INTERNET_CPID)];
|
||||
switch ([codePage intValue])
|
||||
{
|
||||
case 20127:
|
||||
charset = @"us-ascii";
|
||||
break;
|
||||
case 28605:
|
||||
charset = @"iso-8859-15";
|
||||
break;
|
||||
case 65001:
|
||||
charset = @"utf-8";
|
||||
break;
|
||||
case 28591:
|
||||
default:
|
||||
charset = @"iso-8859-1";
|
||||
}
|
||||
[map setObject: [NSString stringWithFormat: @"text/html; charset=%@",
|
||||
charset]
|
||||
forKey: @"content-type"];
|
||||
}
|
||||
else
|
||||
[map setObject: @"text/plain; charset=utf-8"
|
||||
forKey: @"content-type"];
|
||||
|
||||
message = [[[NGMimeMessage alloc] initWithHeader: map] autorelease];
|
||||
|
||||
/* body */
|
||||
if (htmlData)
|
||||
{
|
||||
body = [NSString stringWithData: htmlData
|
||||
usingEncodingNamed: charset];
|
||||
[message setBody: body];
|
||||
}
|
||||
else
|
||||
{
|
||||
body = [mailProperties objectForKey: MAPIPropertyKey (PR_BODY_UNICODE)];
|
||||
if (body)
|
||||
[message setBody: body];
|
||||
}
|
||||
|
||||
/* mime message generation */
|
||||
generator = [NGMimeMessageGenerator new];
|
||||
messageData = [generator generateMimeFromPart: message];
|
||||
[generator release];
|
||||
|
||||
/* appending to imap folder */
|
||||
containerFolder = [container sogoObject];
|
||||
connection = [containerFolder imap4Connection];
|
||||
client = [connection client];
|
||||
folderName = [connection imap4FolderNameForURL: [containerFolder imap4URL]];
|
||||
result = [client append: messageData toFolder: folderName
|
||||
withFlags: [NSArray arrayWithObjects: @"seen", nil]];
|
||||
if ([[result objectForKey: @"result"] boolValue])
|
||||
{
|
||||
/* we reregister the new message URL with the id mapper */
|
||||
responseResult = [[result objectForKey: @"RawResponse"]
|
||||
objectForKey: @"ResponseResult"];
|
||||
flag = [responseResult objectForKey: @"flag"];
|
||||
newIdString = [[flag componentsSeparatedByString: @" "]
|
||||
objectAtIndex: 2];
|
||||
mid = [self objectId];
|
||||
mapping = [[self context] mapping];
|
||||
[mapping unregisterURLWithID: mid];
|
||||
[sogoObject setNameInContainer: [NSString stringWithFormat: @"%@.eml", newIdString]];
|
||||
[mapping registerURL: [self url] withID: mid];
|
||||
}
|
||||
|
||||
/* synchronise the cache and update the change key with the one provided by
|
||||
the client */
|
||||
[(MAPIStoreMailFolder *) container synchroniseCache];
|
||||
changeKey = [mailProperties objectForKey: MAPIPropertyKey (PR_CHANGE_KEY)];
|
||||
if (changeKey)
|
||||
[(MAPIStoreMailFolder *) container
|
||||
setChangeKey: changeKey forMessageWithKey: [self nameInContainer]];
|
||||
}
|
||||
|
||||
@end
|
||||
@@ -36,9 +36,9 @@
|
||||
#import "MAPIApplication.h"
|
||||
#import "MAPIStoreAttachment.h"
|
||||
#import "MAPIStoreContext.h"
|
||||
#import "MAPIStoreDraftsMessage.h"
|
||||
#import "MAPIStoreFolder.h"
|
||||
#import "MAPIStoreMessage.h"
|
||||
#import "MAPIStoreMailVolatileMessage.h"
|
||||
#import "MAPIStoreObject.h"
|
||||
#import "MAPIStoreTable.h"
|
||||
#import "NSObject+MAPIStore.h"
|
||||
@@ -740,7 +740,7 @@ sogo_message_submit (void *message_object, enum SubmitFlags flags)
|
||||
{
|
||||
struct MAPIStoreTallocWrapper *wrapper;
|
||||
NSAutoreleasePool *pool;
|
||||
MAPIStoreDraftsMessage *message;
|
||||
MAPIStoreMailVolatileMessage *message;
|
||||
int rc;
|
||||
|
||||
DEBUG (5, ("[SOGo: %s:%d]\n", __FUNCTION__, __LINE__));
|
||||
|
||||
Reference in New Issue
Block a user