Files
MailCore/Source/CTCoreMessage.m
2014-10-30 11:35:29 -07:00

946 lines
30 KiB
Objective-C

/*
* MailCore
*
* Copyright (C) 2007 - Matt Ronge
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. Neither the name of the MailCore project nor the names of its
* contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHORS AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*/
#import <libetpan/libetpan.h>
#import "CTCoreMessage.h"
#import "CTCoreFolder.h"
#import "MailCoreTypes.h"
#import "CTCoreAddress.h"
#import "CTMIMEFactory.h"
#import "CTMIME_MessagePart.h"
#import "CTMIME_TextPart.h"
#import "CTMIME_MultiPart.h"
#import "CTMIME_SinglePart.h"
#import "CTBareAttachment.h"
#import "CTMIME_HtmlPart.h"
#import "MailCoreUtilities.h"
@implementation CTCoreMessage
@synthesize mime=myParsedMIME, lastError, parentFolder;
- (id)init {
self = [super init];
if (self) {
struct mailimf_fields *fields = mailimf_fields_new_empty();
myFields = mailimf_single_fields_new(fields);
mailimf_fields_free(fields);
}
return self;
}
- (id)initWithMessageStruct:(struct mailmessage *)message {
self = [super init];
if (self) {
assert(message != NULL);
myMessage = message;
myFields = mailimf_single_fields_new(message->msg_fields);
}
return self;
}
- (id)initWithFileAtPath:(NSString *)path {
return [self initWithString:[NSString stringWithContentsOfFile:path encoding:NSUTF8StringEncoding error:NULL]];
}
- (id)initWithString:(NSString *)msgData {
struct mailmessage *msg = data_message_init((char *)[msgData cStringUsingEncoding:NSUTF8StringEncoding],
[msgData lengthOfBytesUsingEncoding:NSUTF8StringEncoding]);
int err;
struct mailmime *dummyMime;
/* mailmessage_get_bodystructure will fill the mailmessage struct for us */
err = mailmessage_get_bodystructure(msg, &dummyMime);
if (err != MAIL_NO_ERROR) {
return nil;
}
return [self initWithMessageStruct:msg];
}
- (void)dealloc {
if (myMessage != NULL) {
mailmessage_flush(myMessage);
mailmessage_free(myMessage);
}
if (myFields != NULL) {
mailimf_single_fields_free(myFields);
}
self.lastError = nil;
self.parentFolder = nil;
[myParsedMIME release];
[super dealloc];
}
- (NSError *)lastError {
return lastError;
}
- (BOOL)hasBodyStructure {
if (myParsedMIME == nil) {
return NO;
}
return YES;
}
- (BOOL)fetchBodyStructure {
if (myMessage == NULL) {
return NO;
}
int err;
struct mailmime *dummyMime;
//Retrieve message mime and message field
err = mailmessage_get_bodystructure(myMessage, &dummyMime);
if (err != MAIL_NO_ERROR) {
self.lastError = MailCoreCreateErrorFromIMAPCode(err);
return NO;
}
CTMIME *oldMIME = myParsedMIME;
myParsedMIME = [[CTMIMEFactory createMIMEWithMIMEStruct:[self messageStruct]->msg_mime
forMessage:[self messageStruct]] retain];
[oldMIME release];
return YES;
}
- (void)setBodyStructure:(struct mailmime *)mime {
CTMIME *oldMIME = myParsedMIME;
myMessage->msg_mime = mime;
myParsedMIME = [[CTMIMEFactory createMIMEWithMIMEStruct:[self messageStruct]->msg_mime
forMessage:[self messageStruct]] retain];
[oldMIME release];
}
- (void)setFields:(struct mailimf_fields *)fields {
if (myFields != NULL)
mailimf_single_fields_free(myFields);
myFields = mailimf_single_fields_new(fields);
}
- (NSString *)body {
if (myFields == NULL || myParsedMIME == nil) {
[self fetchBodyStructure];
}
NSMutableString *result = [NSMutableString string];
BOOL success = [self _buildUpBodyText:myParsedMIME result:result];
if (!success) {
return nil;
}
return result;
}
- (BOOL)hasHtmlBody:(CTMIME *)mime {
if ([mime isKindOfClass:[CTMIME_MessagePart class]]) {
return [self hasHtmlBody:[mime content]];
}
else if ([mime isKindOfClass:[CTMIME_TextPart class]]) {
if ([[mime.contentType lowercaseString] rangeOfString:@"text/html"].location != NSNotFound) {
return YES;
}
}
else if ([mime isKindOfClass:[CTMIME_MultiPart class]]) {
return YES;
}
return NO;
}
- (BOOL)hasHtmlBody {
CTMIME* mime = myParsedMIME;
return [self hasHtmlBody:mime];
}
- (NSString *)htmlBody {
if (myFields == NULL || myParsedMIME == nil) {
[self fetchBodyStructure];
}
NSMutableString *result = [NSMutableString string];
BOOL success = [self _buildUpHtmlBodyText:myParsedMIME result:result];
if (!success) {
return nil;
}
return result;
}
- (NSString *)bodyPreferringPlainText:(BOOL *)isHTML {
NSString *body = [self body];
*isHTML = NO;
if ([body length] == 0) {
body = [self htmlBody];
*isHTML = YES;
}
return body;
}
- (BOOL)_buildUpBodyText:(CTMIME *)mime result:(NSMutableString *)result {
if (mime == nil)
return NO;
if ([mime isKindOfClass:[CTMIME_MessagePart class]]) {
return [self _buildUpBodyText:[mime content] result:result];
}
else if ([mime isKindOfClass:[CTMIME_TextPart class]]) {
if ([[mime.contentType lowercaseString] rangeOfString:@"text/plain"].location != NSNotFound) {
BOOL success = [(CTMIME_TextPart *)mime fetchPart];
if (!success) {
return NO;
}
NSString* y = [mime content];
if(y == nil) {
return NO;
}
[result appendString:y];
}
}
else if ([mime isKindOfClass:[CTMIME_MultiPart class]]) {
//TODO need to take into account the different kinds of multipart
NSEnumerator *enumer = [[mime content] objectEnumerator];
CTMIME *subpart;
while ((subpart = [enumer nextObject])) {
BOOL success = [self _buildUpBodyText:subpart result:result];
if (!success) {
return NO;
}
}
}
return YES;
}
- (BOOL)_buildUpHtmlBodyText:(CTMIME *)mime result:(NSMutableString *)result {
if (mime == nil)
return NO;
if ([mime isKindOfClass:[CTMIME_MessagePart class]]) {
return [self _buildUpHtmlBodyText:[mime content] result:result];
}
else if ([mime isKindOfClass:[CTMIME_TextPart class]]) {
if ([[mime.contentType lowercaseString] rangeOfString:@"text/html"].location != NSNotFound) {
BOOL success = [(CTMIME_TextPart *)mime fetchPart];
if (!success) {
return NO;
}
NSString* y = [mime content];
if(y == nil) {
return NO;
}
[result appendString:y];
}
}
else if ([mime isKindOfClass:[CTMIME_MultiPart class]]) {
//TODO need to take into account the different kinds of multipart
NSEnumerator *enumer = [[mime content] objectEnumerator];
CTMIME *subpart;
while ((subpart = [enumer nextObject])) {
BOOL success = [self _buildUpHtmlBodyText:subpart result:result];
if (!success) {
return NO;
}
}
}
return YES;
}
- (void) setHTMLBody:(NSString*)htmlString textBody:(NSString*)plainString {
/*
* Message structure:
*
* [ msg root ]
* --> multipart/alternative (Body)
* -----> text
* -----> html
*/
// Body (multipart/alternative)
CTMIME_MultiPart* bodyMultipart = [CTMIME_MultiPart mimeMultiPart];
bodyMultipart.contentType = @"multipart/alternative";
CTMIME_TextPart* bodyTextPart = [CTMIME_TextPart mimeTextPartWithString:plainString];
CTMIME_HtmlPart* bodyHtmlPart = [CTMIME_HtmlPart mimeTextPartWithString:htmlString];
[bodyMultipart addMIMEPart: bodyTextPart ];
[bodyMultipart addMIMEPart: bodyHtmlPart ];
// Root
CTMIME_MessagePart* messagePartRoot = [CTMIME_MessagePart mimeMessagePartWithContent:bodyMultipart];
[myParsedMIME release];
myParsedMIME = [messagePartRoot retain];
}
- (void)setBody:(NSString *)body {
CTMIME *oldMIME = myParsedMIME;
CTMIME_TextPart *text = [CTMIME_TextPart mimeTextPartWithString:body];
// If myParsedMIME is already a multi-part mime, just add it. otherwise replace it.
//TODO: If setBody is called multiple times it will add text parts multiple times. Instead
// it should find the existing text part (if there is one) and replace it
if ([myParsedMIME isKindOfClass:[CTMIME_MultiPart class]]) {
[(CTMIME_MultiPart *)myParsedMIME addMIMEPart:text];
} else {
CTMIME_MessagePart *messagePart = [CTMIME_MessagePart mimeMessagePartWithContent:text];
myParsedMIME = [messagePart retain];
[oldMIME release];
}
}
- (void)setHTMLBody:(NSString *)body{
CTMIME *oldMIME = myParsedMIME;
CTMIME_HtmlPart *text = [CTMIME_HtmlPart mimeTextPartWithString:body];
CTMIME_MessagePart *messagePart = [CTMIME_MessagePart mimeMessagePartWithContent:text];
myParsedMIME = [messagePart retain];
[oldMIME release];
}
- (NSArray *)attachments {
NSMutableArray *attachments = [NSMutableArray array];
CTMIME_Enumerator *enumerator = [myParsedMIME mimeEnumerator];
CTMIME *mime;
while ((mime = [enumerator nextObject])) {
if ([mime isKindOfClass:[CTMIME_SinglePart class]]) {
CTMIME_SinglePart *singlePart = (CTMIME_SinglePart *)mime;
if (singlePart.attached) {
CTBareAttachment *attach = [[CTBareAttachment alloc]
initWithMIMESinglePart:singlePart];
[attachments addObject:attach];
[attach release];
}
}
}
return attachments;
}
- (void)addAttachment:(CTCoreAttachment *)attachment {
CTMIME_MultiPart *multi;
CTMIME_MessagePart *msg;
if ([myParsedMIME isKindOfClass:[CTMIME_MessagePart class]]) {
msg = (CTMIME_MessagePart *)myParsedMIME;
CTMIME *sub = [msg content];
// Creat new multimime part if needed
if ([sub isKindOfClass:[CTMIME_MultiPart class]]) {
// Assuming this is the body
if ([sub.contentType isEqualToString:@"multipart/alternative"]) {
/*
* Transform the following
* message structure:
*
* [ msg root ]
* --> multipart/alternative
* -----> text
* -----> html
*
* into:
*
* [ MessagePart Root ]
* --> multipart/mixed
* -----> attachments
* -----> multipart/alternative
* --------> text
* --------> html
*/
CTMIME_MultiPart* multipartMixed = [CTMIME_MultiPart mimeMultiPart];
[multipartMixed addMIMEPart:sub];
multi = multipartMixed;
[msg setContent:multipartMixed];
}
else
multi = (CTMIME_MultiPart *)sub;
}
else {
multi = [CTMIME_MultiPart mimeMultiPart];
[multi addMIMEPart:sub];
[msg setContent:multi];
}
// add new SinglePart which encodes the attachment in base64
CTMIME_SinglePart *attpart = [CTMIME_SinglePart mimeSinglePartWithData:[attachment data]];
attpart.contentType = [attachment contentType];
attpart.filename = [attachment filename];
[multi addMIMEPart:attpart];
}
}
- (NSString *)subject {
if (myFields->fld_subject == NULL)
return nil;
NSString *decodedSubject = MailCoreDecodeMIMEPhrase(myFields->fld_subject->sbj_value);
if (decodedSubject == nil)
return nil;
return decodedSubject;
}
- (void)setSubject:(NSString *)subject {
struct mailimf_subject *subjectStruct;
subjectStruct = mailimf_subject_new(strdup([subject cStringUsingEncoding:NSUTF8StringEncoding]));
if (myFields->fld_subject != NULL)
mailimf_subject_free(myFields->fld_subject);
myFields->fld_subject = subjectStruct;
}
- (struct mailimf_date_time*)libetpanDateTime {
if(!myFields || !myFields->fld_orig_date || !myFields->fld_orig_date->dt_date_time)
return NULL;
return myFields->fld_orig_date->dt_date_time;
}
- (NSTimeZone*)senderTimeZone {
struct mailimf_date_time *d;
if((d = [self libetpanDateTime]) == NULL)
return nil;
NSInteger timezoneOffsetInSeconds = 3600*d->dt_zone/100;
return [NSTimeZone timeZoneForSecondsFromGMT:timezoneOffsetInSeconds];
}
- (NSDate *)senderDate {
if ( myFields->fld_orig_date == NULL) {
return nil;
} else {
struct mailimf_date_time *d;
if ((d = [self libetpanDateTime]) == NULL)
return nil;
NSCalendar *calendar = [[NSCalendar alloc] initWithCalendarIdentifier:NSGregorianCalendar];
calendar.timeZone = [self senderTimeZone];
NSDateComponents *comps = [[NSDateComponents alloc] init];
[comps setYear:d->dt_year];
[comps setMonth:d->dt_month];
[comps setDay:d->dt_day];
[comps setHour:d->dt_hour];
[comps setMinute:d->dt_min];
[comps setSecond:d->dt_sec];
NSDate *messageDate = [calendar dateFromComponents:comps];
[comps release];
[calendar release];
return messageDate;
}
}
- (BOOL)isUnread {
return ![self isFlagSet:MAIL_FLAG_SEEN withDefault:YES];
}
- (BOOL)isDeleted {
return [self isFlagSet:MAIL_FLAG_DELETED withDefault:NO];
}
- (BOOL)isStarred {
return [self isFlagSet:MAIL_FLAG_FLAGGED withDefault:NO];
}
- (BOOL)isFlagSet:(NSUInteger)flag withDefault:(BOOL)def {
struct mail_flags *flags = myMessage ? myMessage->msg_flags : NULL;
return flags == NULL ? def : flags->fl_flags & flag;
}
- (BOOL)isNew {
struct mail_flags *flags = myMessage ? myMessage->msg_flags : NULL;
if (flags != NULL) {
BOOL flag_seen = (flags->fl_flags & MAIL_FLAG_SEEN);
BOOL flag_new = (flags->fl_flags & MAIL_FLAG_NEW);
return !flag_seen && flag_new;
}
return NO;
}
- (NSString *)messageId {
if (myFields->fld_message_id != NULL) {
char *value = myFields->fld_message_id->mid_value;
return [NSString stringWithCString:value encoding:NSUTF8StringEncoding];
}
return nil;
}
- (NSUInteger)uid {
if (myMessage && myMessage->msg_uid) {
NSString *uidString = [[NSString alloc] initWithCString:myMessage->msg_uid encoding:NSASCIIStringEncoding];
NSUInteger uid = (NSUInteger)[[[uidString componentsSeparatedByString:@"-"] objectAtIndex:1] intValue];
[uidString release];
return uid;
}
return 0;
}
- (NSUInteger)messageSize {
return [self messageStruct]->msg_size;
}
- (NSUInteger)flags {
if (myMessage != NULL && myMessage->msg_flags != NULL) {
return myMessage->msg_flags->fl_flags;
}
return 0;
}
- (NSArray *)extionsionFlags {
if (myMessage != NULL && myMessage->msg_flags != NULL) {
return MailCoreStringArrayFromClist(myMessage->msg_flags->fl_extension);
}
return nil;
}
- (NSUInteger)sequenceNumber {
return mySequenceNumber;
}
- (void)setSequenceNumber:(NSUInteger)sequenceNumber {
mySequenceNumber = sequenceNumber;
}
- (NSSet *)from {
if (myFields->fld_from == NULL)
return nil;
return [self _addressListFromMailboxList:myFields->fld_from->frm_mb_list];
}
- (void)setFrom:(NSSet *)addresses {
struct mailimf_mailbox_list *imf = [self _mailboxListFromAddressList:addresses];
if (myFields->fld_from != NULL)
mailimf_from_free(myFields->fld_from);
myFields->fld_from = mailimf_from_new(imf);
}
- (CTCoreAddress *)sender {
if (myFields->fld_sender == NULL)
return nil;
return [self _addressFromMailbox:myFields->fld_sender->snd_mb];
}
- (NSSet *)to {
if (myFields->fld_to == NULL)
return nil;
else
return [self _addressListFromIMFAddressList:myFields->fld_to->to_addr_list];
}
- (void)setTo:(NSSet *)addresses {
struct mailimf_address_list *imf = [self _IMFAddressListFromAddresssList:addresses];
if (!imf) {
return;
}
if (myFields->fld_to != NULL) {
mailimf_address_list_free(myFields->fld_to->to_addr_list);
myFields->fld_to->to_addr_list = imf;
}
else
myFields->fld_to = mailimf_to_new(imf);
}
- (NSArray *)inReplyTo {
if (myFields->fld_in_reply_to == NULL)
return nil;
else
return MailCoreStringArrayFromClist(myFields->fld_in_reply_to->mid_list);
}
- (void)setInReplyTo:(NSArray *)messageIds {
struct mailimf_in_reply_to *imf = mailimf_in_reply_to_new(MailCoreClistFromStringArray(messageIds));
if (myFields->fld_in_reply_to != NULL) {
mailimf_in_reply_to_free(myFields->fld_in_reply_to);
myFields->fld_in_reply_to = imf;
}
else
myFields->fld_in_reply_to = imf;
}
- (NSArray *)references {
if (myFields->fld_references == NULL)
return nil;
else
return MailCoreStringArrayFromClist(myFields->fld_references->mid_list);
}
- (void)setReferences:(NSArray *)messageIds {
struct mailimf_references *imf = mailimf_references_new(MailCoreClistFromStringArray(messageIds));
if (myFields->fld_references != NULL) {
mailimf_references_free(myFields->fld_references);
myFields->fld_references = imf;
}
else
myFields->fld_references = imf;
}
- (NSSet *)cc {
if (myFields->fld_cc == NULL)
return nil;
else
return [self _addressListFromIMFAddressList:myFields->fld_cc->cc_addr_list];
}
- (void)setCc:(NSSet *)addresses {
struct mailimf_address_list *imf = [self _IMFAddressListFromAddresssList:addresses];
if (!imf) {
return;
}
if (myFields->fld_cc != NULL) {
mailimf_address_list_free(myFields->fld_cc->cc_addr_list);
myFields->fld_cc->cc_addr_list = imf;
}
else
myFields->fld_cc = mailimf_cc_new(imf);
}
- (NSSet *)bcc {
if (myFields->fld_bcc == NULL)
return nil;
else
return [self _addressListFromIMFAddressList:myFields->fld_bcc->bcc_addr_list];
}
- (void)setBcc:(NSSet *)addresses {
struct mailimf_address_list *imf = [self _IMFAddressListFromAddresssList:addresses];
if (!imf) {
return;
}
if (myFields->fld_bcc != NULL) {
mailimf_address_list_free(myFields->fld_bcc->bcc_addr_list);
myFields->fld_bcc->bcc_addr_list = imf;
}
else
myFields->fld_bcc = mailimf_bcc_new(imf);
}
- (NSSet *)replyTo {
if (myFields->fld_reply_to == NULL)
return nil;
else
return [self _addressListFromIMFAddressList:myFields->fld_reply_to->rt_addr_list];
}
- (void)setReplyTo:(NSSet *)addresses {
struct mailimf_address_list *imf = [self _IMFAddressListFromAddresssList:addresses];
if (!imf) {
return;
}
if (myFields->fld_reply_to != NULL) {
mailimf_address_list_free(myFields->fld_reply_to->rt_addr_list);
myFields->fld_reply_to->rt_addr_list = imf;
}
else
myFields->fld_reply_to = mailimf_reply_to_new(imf);
}
- (NSString *)render {
CTMIME *msgPart = myParsedMIME;
if ([myParsedMIME isKindOfClass:[CTMIME_MessagePart class]]) {
/* It's a message part, so let's set it's fields */
struct mailimf_fields *fields;
struct mailimf_mailbox *sender = (myFields->fld_sender != NULL) ? (myFields->fld_sender->snd_mb) : NULL;
struct mailimf_mailbox_list *from = (myFields->fld_from != NULL) ? (myFields->fld_from->frm_mb_list) : NULL;
struct mailimf_address_list *replyTo = (myFields->fld_reply_to != NULL) ? (myFields->fld_reply_to->rt_addr_list) : NULL;
struct mailimf_address_list *to = (myFields->fld_to != NULL) ? (myFields->fld_to->to_addr_list) : NULL;
struct mailimf_address_list *cc = (myFields->fld_cc != NULL) ? (myFields->fld_cc->cc_addr_list) : NULL;
struct mailimf_address_list *bcc = (myFields->fld_bcc != NULL) ? (myFields->fld_bcc->bcc_addr_list) : NULL;
clist *inReplyTo = (myFields->fld_in_reply_to != NULL) ? (myFields->fld_in_reply_to->mid_list) : NULL;
clist *references = (myFields->fld_references != NULL) ? (myFields->fld_references->mid_list) : NULL;
char *subject = (myFields->fld_subject != NULL) ? (myFields->fld_subject->sbj_value) : NULL;
fields = mailimf_fields_new_with_data(from, sender, replyTo, to, cc, bcc, inReplyTo, references, subject);
if (self->mailPriority != 0) {
char * xPriorityValue;
char * rfcPriorityValue;
switch (self->mailPriority) {
case CTCoreMessageUrgentPriority: {
xPriorityValue = "1";
rfcPriorityValue = "urgent";
break;
}
case CTCoreMessageNormalPriority: {
xPriorityValue = "3";
rfcPriorityValue = "normal";
break;
}
case CTCoreMessageNonUrgentPriority: {
xPriorityValue = "5";
rfcPriorityValue = "non-urgent";
break;
}
default:
break;
}
struct mailimf_optional_field * priority = mailimf_optional_field_new("X-Priority", xPriorityValue);
struct mailimf_field * priorityField = mailimf_field_new(MAILIMF_FIELD_OPTIONAL_FIELD, NULL, NULL, NULL,
NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
NULL, NULL, priority);
mailimf_fields_add(fields, priorityField);
priority = mailimf_optional_field_new("Priority", rfcPriorityValue);
priorityField = mailimf_field_new(MAILIMF_FIELD_OPTIONAL_FIELD, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
NULL, NULL, priority);
mailimf_fields_add(fields, priorityField);
}
//TODO uh oh, when this get freed it frees stuff in the CTCoreMessage
//TODO Need to make sure that fields gets freed somewhere
[(CTMIME_MessagePart *)msgPart setIMFFields:fields];
}
return [myParsedMIME render];
}
- (NSData *)messageAsEmlx {
NSString *msgContent = [[self rfc822] stringByReplacingOccurrencesOfString:@"\r\n" withString:@"\n"];
NSData *msgContentAsData = [msgContent dataUsingEncoding:NSUTF8StringEncoding];
NSMutableData *emlx = [NSMutableData data];
[emlx appendData:[[NSString stringWithFormat:@"%-10d\n", (uint32_t)msgContentAsData.length] dataUsingEncoding:NSUTF8StringEncoding]];
[emlx appendData:msgContentAsData];
struct mail_flags *flagsStruct = myMessage ? myMessage->msg_flags : NULL;
uint64_t flags = 0;
if (flagsStruct != NULL) {
BOOL seen = (flagsStruct->fl_flags & CTFlagSeen) > 0;
flags |= seen << 0;
BOOL answered = (flagsStruct->fl_flags & CTFlagAnswered) > 0;
flags |= answered << 2;
BOOL flagged = (flagsStruct->fl_flags & CTFlagFlagged) > 0;
flags |= flagged << 4;
BOOL forwarded = (flagsStruct->fl_flags & CTFlagForwarded) > 0;
flags |= forwarded << 8;
}
NSMutableDictionary *dictionary = [NSMutableDictionary dictionary];
[dictionary setValue:[NSNumber numberWithDouble:[[self senderDate] timeIntervalSince1970]] forKey:@"date-sent"];
[dictionary setValue:[NSNumber numberWithUnsignedLongLong:flags] forKey:@"flags"];
[dictionary setValue:[self subject] forKey:@"subject"];
NSError *error;
NSData *propertyList = [NSPropertyListSerialization dataWithPropertyList:dictionary
format:NSPropertyListXMLFormat_v1_0
options:0
error:&error];
[emlx appendData:propertyList];
return emlx;
}
- (NSString *)rfc822 {
char *result = NULL;
NSString *nsresult;
int r = mailimap_fetch_rfc822([self imapSession], [self sequenceNumber], &result);
if (r == MAIL_NO_ERROR) {
nsresult = [[NSString alloc] initWithCString:result encoding:NSUTF8StringEncoding];
} else {
self.lastError = MailCoreCreateErrorFromIMAPCode(r);
return nil;
}
mailimap_msg_att_rfc822_free(result);
return [nsresult autorelease];
}
- (NSString *)rfc822Header {
char *result = NULL;
NSString *nsresult;
int r = mailimap_fetch_rfc822_header([self imapSession], [self sequenceNumber], &result);
if (r == MAIL_NO_ERROR) {
nsresult = [[NSString alloc] initWithCString:result encoding:NSUTF8StringEncoding];
} else {
self.lastError = MailCoreCreateErrorFromIMAPCode(r);
return nil;
}
mailimap_msg_att_rfc822_free(result);
return [nsresult autorelease];
}
- (void)setMailPriority:(CTCoreMessagePriority)priority {
mailPriority = priority;
}
- (struct mailmessage *)messageStruct {
return myMessage;
}
- (mailimap *)imapSession; {
struct imap_cached_session_state_data * cached_data;
struct imap_session_state_data * data;
mailsession *session = [self messageStruct]->msg_session;
if (strcasecmp(session->sess_driver->sess_name, "imap-cached") == 0) {
cached_data = session->sess_data;
session = cached_data->imap_ancestor;
}
data = session->sess_data;
return data->imap_session;
}
- (CTCoreAddress *)_addressFromMailbox:(struct mailimf_mailbox *)mailbox; {
CTCoreAddress *address = [CTCoreAddress address];
if (mailbox == NULL) {
return address;
}
if (mailbox->mb_display_name != NULL) {
NSString *decodedName = MailCoreDecodeMIMEPhrase(mailbox->mb_display_name);
if (decodedName == nil) {
decodedName = @"";
}
[address setName:decodedName];
}
if (mailbox->mb_addr_spec != NULL) {
[address setEmail:[NSString stringWithCString:mailbox->mb_addr_spec encoding:NSUTF8StringEncoding]];
}
return address;
}
- (NSSet *)_addressListFromMailboxList:(struct mailimf_mailbox_list *)mailboxList; {
clist *list;
clistiter * iter;
struct mailimf_mailbox *address;
NSMutableSet *addressSet = [NSMutableSet set];
if (mailboxList == NULL)
return addressSet;
list = mailboxList->mb_list;
for(iter = clist_begin(list); iter != NULL; iter = clist_next(iter)) {
address = clist_content(iter);
[addressSet addObject:[self _addressFromMailbox:address]];
}
return addressSet;
}
- (struct mailimf_mailbox_list *)_mailboxListFromAddressList:(NSSet *)addresses {
struct mailimf_mailbox_list *imfList = mailimf_mailbox_list_new_empty();
NSEnumerator *objEnum = [addresses objectEnumerator];
CTCoreAddress *address;
int err;
const char *addressName;
const char *addressEmail;
while((address = [objEnum nextObject])) {
addressName = [[address name] cStringUsingEncoding:NSUTF8StringEncoding];
addressEmail = [[address email] cStringUsingEncoding:NSUTF8StringEncoding];
err = mailimf_mailbox_list_add_mb(imfList, strdup(addressName), strdup(addressEmail));
assert(err == 0);
}
return imfList;
}
- (NSSet *)_addressListFromIMFAddressList:(struct mailimf_address_list *)imfList {
clist *list;
clistiter * iter;
struct mailimf_address *address;
NSMutableSet *addressSet = [NSMutableSet set];
if (imfList == NULL)
return addressSet;
list = imfList->ad_list;
for(iter = clist_begin(list); iter != NULL; iter = clist_next(iter)) {
address = clist_content(iter);
/* Check to see if it's a solo address a group */
if (address->ad_type == MAILIMF_ADDRESS_MAILBOX) {
[addressSet addObject:[self _addressFromMailbox:address->ad_data.ad_mailbox]];
}
else {
if (address->ad_data.ad_group->grp_mb_list != NULL)
[addressSet unionSet:[self _addressListFromMailboxList:address->ad_data.ad_group->grp_mb_list]];
}
}
return addressSet;
}
- (struct mailimf_address_list *)_IMFAddressListFromAddresssList:(NSSet *)addresses {
if (!addresses) {
return NULL;
}
struct mailimf_address_list *imfList = mailimf_address_list_new_empty();
NSEnumerator *objEnum = [addresses objectEnumerator];
CTCoreAddress *address;
int err;
const char *addressName;
const char *addressEmail;
while((address = [objEnum nextObject])) {
addressName = [[address name] cStringUsingEncoding:NSUTF8StringEncoding];
addressEmail = [[address email] cStringUsingEncoding:NSUTF8StringEncoding];
err = mailimf_address_list_add_mb(imfList, strdup(addressName), strdup(addressEmail));
assert(err == 0);
}
return imfList;
}
@end