diff --git a/ChangeLog b/ChangeLog index 1fab4ff1d..e95dabf85 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,5 +1,8 @@ 2011-02-24 Wolfgang Sourdeau + * OpenChange/MAPIStoreMessage.m: new super class used as for + wrapper objects replacing the category modules on the SOGoObject. + * OpenChange/MAPIStoreObject.m: new super class used as base the MAPI objects counterparts (MAPIStoreMessage, MAPIStoreFolder and MAPIStoreAttachment). diff --git a/OpenChange/SOGoContentObject+MAPIStore.h b/OpenChange/MAPIStoreCalendarMessage.h similarity index 65% rename from OpenChange/SOGoContentObject+MAPIStore.h rename to OpenChange/MAPIStoreCalendarMessage.h index 45433eb9b..cfc6b1c5f 100644 --- a/OpenChange/SOGoContentObject+MAPIStore.h +++ b/OpenChange/MAPIStoreCalendarMessage.h @@ -1,12 +1,12 @@ -/* SOGoContentObject+MAPIStore.h - this file is part of SOGo +/* MAPIStoreCalendarMessage.h - this file is part of SOGo * - * Copyright (C) 2010 Inverse inc. + * Copyright (C) 2011 Inverse inc * * Author: Wolfgang Sourdeau * * 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) + * 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, @@ -20,15 +20,13 @@ * Boston, MA 02111-1307, USA. */ -#ifndef SOGOCONTENTOBJECT_MAPISTORE_H -#define SOGOCONTENTOBJECT_MAPISTORE_H +#ifndef MAPISTORECALENDARMESSAGE_H +#define MAPISTORECALENDARMESSAGE_H -#import +#import "MAPIStoreGCSMessage.h" -@interface SOGoContentObject (MAPIStoreMessage) - -- (void) MAPISave; +@interface MAPIStoreCalendarMessage : MAPIStoreGCSMessage @end -#endif /* SOGOCONTENTOBJECT_MAPISTORE_H */ +#endif /* MAPISTORECALENDARMESSAGE_H */ diff --git a/OpenChange/MAPIStoreCalendarMessage.m b/OpenChange/MAPIStoreCalendarMessage.m new file mode 100644 index 000000000..057d4079d --- /dev/null +++ b/OpenChange/MAPIStoreCalendarMessage.m @@ -0,0 +1,323 @@ +/* MAPIStoreCalendarMessage.m - this file is part of SOGo + * + * Copyright (C) 2011 Inverse inc + * + * Author: Wolfgang Sourdeau + * + * 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. + */ + +#import +#import +#import +#import +#import +#import +#import +#import +#import +#import +#import +#import +#import + +#import "MAPIStoreContext.h" +#import "MAPIStoreTypes.h" +#import "NSCalendarDate+MAPIStore.h" +#import "NSString+MAPIStore.h" + +#import "MAPIStoreCalendarMessage.h" + +#undef DEBUG +#include +#include +#include +#include + +@implementation MAPIStoreCalendarMessage + +- (enum MAPISTATUS) getProperty: (void **) data + withTag: (enum MAPITAGS) propTag +{ + NSTimeInterval timeValue; + id event; + int rc; + + rc = MAPI_E_SUCCESS; + switch (propTag) + { + case PR_ICON_INDEX: // TODO + /* see http://msdn.microsoft.com/en-us/library/cc815472.aspx */ + // *longValue = 0x00000401 for recurring event + // *longValue = 0x00000402 for meeting + // *longValue = 0x00000403 for recurring meeting + // *longValue = 0x00000404 for invitation + *data = MAPILongValue (memCtx, 0x00000400); + break; + case PR_MESSAGE_CLASS_UNICODE: + *data = talloc_strdup(memCtx, "IPM.Appointment"); + break; + case PidLidAppointmentStartWhole: // DTSTART + event = [sogoObject component: NO secure: NO]; + *data = [[event startDate] asFileTimeInMemCtx: memCtx]; + break; + case PidLidAppointmentEndWhole: // DTEND + event = [sogoObject component: NO secure: NO]; + *data = [[event endDate] asFileTimeInMemCtx: memCtx]; + break; + case PidLidAppointmentDuration: + event = [sogoObject component: NO secure: NO]; + timeValue = [[event endDate] timeIntervalSinceDate: [event startDate]]; + *data = MAPILongValue (memCtx, (uint32_t) (timeValue / 60)); + break; + case PidLidAppointmentSubType: + event = [sogoObject component: NO secure: NO]; + *data = MAPIBoolValue (memCtx, [event isAllDay]); + break; + case PidLidBusyStatus: // TODO + *data = MAPILongValue (memCtx, 0x02); + break; + case PidLidRecurring: // TODO + *data = MAPIBoolValue (memCtx, NO); + break; + + // case 0x82410003: // TODO + // *data = MAPILongValue (memCtx, 0); + // break; + case PR_SUBJECT_UNICODE: // SUMMARY + event = [sogoObject component: NO secure: NO]; + *data = [[event summary] asUnicodeInMemCtx: memCtx]; + break; + case PidLidLocation: // LOCATION + event = [sogoObject component: NO secure: NO]; + *data = [[event location] asUnicodeInMemCtx: memCtx]; + break; + case PidLidPrivate: // private (bool), should depend on CLASS and permissions + *data = MAPIBoolValue (memCtx, NO); + break; + case PR_SENSITIVITY: // not implemented, depends on CLASS + // normal = 0, personal?? = 1, private = 2, confidential = 3 + *data = MAPILongValue (memCtx, 0); + break; + case PR_CREATION_TIME: + event = [sogoObject component: NO secure: NO]; + *data = [[event created] asFileTimeInMemCtx: memCtx]; + break; + + case PR_IMPORTANCE: + { + unsigned int v; + + event = [sogoObject component: NO secure: NO]; + + if ([[event priority] isEqualToString: @"9"]) + v = 0x0; + else if ([[event priority] isEqualToString: @"1"]) + v = 0x2; + else + v = 0x1; + + *data = MAPILongValue (memCtx, v); + } + break; + + // case PidLidTimeZoneStruct: + // case PR_VD_NAME_UNICODE: + // *data = talloc_strdup(memCtx, "PR_VD_NAME_UNICODE"); + // break; + // case PR_EMS_AB_DXA_REMOTE_CLIENT_UNICODE: "Home:" ??? + // *data = talloc_strdup(memCtx, "PR_EMS..."); + // break; + default: + rc = [super getProperty: data + withTag: propTag]; + } + + // #define PR_REPLY_TIME PROP_TAG(PT_SYSTIME , 0x0030) /* 0x00300040 */ + // #define PR_INTERNET_MESSAGE_ID_UNICODE PROP_TAG(PT_UNICODE , 0x1035) /* 0x1035001f */ + // #define PR_FLAG_STATUS PROP_TAG(PT_LONG , 0x1090) /* 0x10900003 */ + + return rc; +} + +- (void) openMessage: (struct mapistore_message *) msg +{ + NSString *name, *email; + NSArray *attendees; + iCalPerson *person; + id event; + struct SRowSet *recipients; + int count, max; + + [super openMessage: msg]; + event = [sogoObject component: NO secure: NO]; + attendees = [event attendees]; + max = [attendees count]; + + recipients = msg->recipients; + recipients->cRows = max; + recipients->aRow = talloc_array (recipients, struct SRow, max); + + for (count = 0; count < max; count++) + { + recipients->aRow[count].ulAdrEntryPad = 0; + recipients->aRow[count].cValues = 3; + recipients->aRow[count].lpProps = talloc_array (recipients->aRow, + struct SPropValue, + 3); + + // TODO (0x01 = primary recipient) + set_SPropValue_proptag (&(recipients->aRow[count].lpProps[0]), + PR_RECIPIENT_TYPE, + MAPILongValue (memCtx, 0x01)); + + person = [attendees objectAtIndex: count]; + + name = [person cn]; + if (!name) + name = @""; + + email = [person email]; + if (!email) + email = @""; + + set_SPropValue_proptag (&(recipients->aRow[count].lpProps[1]), + PR_DISPLAY_NAME, + [name asUnicodeInMemCtx: recipients->aRow[count].lpProps]); + set_SPropValue_proptag (&(recipients->aRow[count].lpProps[2]), + PR_EMAIL_ADDRESS, + [email asUnicodeInMemCtx: recipients->aRow[count].lpProps]); + } +} + +/* TODO: merge with tasks */ +- (void) save +{ + iCalCalendar *vCalendar; + iCalEvent *vEvent; + id value; + SOGoUserDefaults *ud; + NSCalendarDate *now; + iCalTimeZone *tz; + NSString *owner, *content; + iCalDateTime *start, *end; + WOContext *woContext; + + [self logWithFormat: @"-save, event props:"]; + // MAPIStoreDumpMessageProperties (newProperties); + + content = [sogoObject contentAsString]; + if (![content length]) + { + vEvent = [sogoObject component: YES secure: NO]; + vCalendar = [vEvent parent]; + [vCalendar setProdID: @"-//Inverse inc.//OpenChange+SOGo//EN"]; + content = [vCalendar versitString]; + } + + vCalendar = [iCalCalendar parseSingleFromSource: content]; + vEvent = [[vCalendar events] objectAtIndex: 0]; + + // summary + value = [newProperties + objectForKey: MAPIPropertyKey (PR_NORMALIZED_SUBJECT_UNICODE)]; + if (value) + [vEvent setSummary: value]; + + // Location + value = [newProperties objectForKey: MAPIPropertyKey (PidLidLocation)]; + if (value) + [vEvent setLocation: value]; + + woContext = [[self context] woContext]; + owner = [sogoObject ownerInContext: woContext]; + ud = [[SOGoUser userWithLogin: owner] userDefaults]; + tz = [iCalTimeZone timeZoneForName: [ud timeZoneName]]; + [vCalendar addTimeZone: tz]; + + // start + value = [newProperties objectForKey: MAPIPropertyKey (PR_START_DATE)]; + if (!value) + value = [newProperties objectForKey: MAPIPropertyKey (PidLidAppointmentStartWhole)]; + if (value) + { + start = (iCalDateTime *) [vEvent uniqueChildWithTag: @"dtstart"]; + [start setTimeZone: tz]; + [start setDateTime: value]; + } + + // end + value = [newProperties objectForKey: MAPIPropertyKey (PR_END_DATE)]; + if (!value) + value = [newProperties objectForKey: MAPIPropertyKey (PidLidAppointmentEndWhole)]; + if (value) + { + end = (iCalDateTime *) [vEvent uniqueChildWithTag: @"dtend"]; + [end setTimeZone: tz]; + [end setDateTime: value]; + } + + now = [NSCalendarDate date]; + if ([sogoObject isNew]) + { + [vEvent setCreated: now]; + } + [vEvent setTimeStampAsDate: now]; + + // Organizer and attendees + value = [newProperties objectForKey: @"recipients"]; + + if (value) + { + NSArray *recipients; + NSDictionary *dict; + iCalPerson *person; + int i; + + dict = [[woContext activeUser] primaryIdentity]; + person = [iCalPerson new]; + [person setCn: [dict objectForKey: @"fullName"]]; + [person setEmail: [dict objectForKey: @"email"]]; + [vEvent setOrganizer: person]; + [person release]; + + recipients = [value objectForKey: @"to"]; + + for (i = 0; i < [recipients count]; i++) + { + dict = [recipients objectAtIndex: i]; + person = [iCalPerson new]; + + [person setCn: [dict objectForKey: @"fullName"]]; + [person setEmail: [dict objectForKey: @"email"]]; + [person setParticipationStatus: iCalPersonPartStatNeedsAction]; + [person setRsvp: @"TRUE"]; + [person setRole: @"REQ-PARTICIPANT"]; + + // FIXME: We must NOT always rely on this + if (![dict objectForKey: @"x500dn"] + && ![vEvent isAttendee: [person rfc822Email]]) + [vEvent addToAttendees: person]; + + [person release]; + } + } + + // [sogoObject saveContentString: [vCalendar versitString]]; + [sogoObject saveComponent: vEvent]; +} + +@end diff --git a/OpenChange/SOGoContentObject+MAPIStore.m b/OpenChange/MAPIStoreContactsMessage.h similarity index 63% rename from OpenChange/SOGoContentObject+MAPIStore.m rename to OpenChange/MAPIStoreContactsMessage.h index 3e2066db5..68f75c93d 100644 --- a/OpenChange/SOGoContentObject+MAPIStore.m +++ b/OpenChange/MAPIStoreContactsMessage.h @@ -1,12 +1,12 @@ -/* SOGoContentObject+MAPIStore.m - this file is part of SOGo +/* MAPIStoreContactsMessage.h - this file is part of SOGo * - * Copyright (C) 2010 Inverse inc. + * Copyright (C) 2011 Inverse inc * * Author: Wolfgang Sourdeau * * 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) + * 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, @@ -20,18 +20,13 @@ * Boston, MA 02111-1307, USA. */ -#import -#import +#ifndef MAPISTORECONTACTSMESSAGE_H +#define MAPISTORECONTACTSMESSAGE_H -#import "SOGoContentObject+MAPIStore.h" +#import "MAPIStoreGCSMessage.h" -@implementation SOGoContentObject (MAPIStoreMessage) - -- (void) MAPISave -{ - [self logWithFormat: @"-MAPISave"]; - - [self saveContentString: content]; -} +@interface MAPIStoreContactsMessage : MAPIStoreGCSMessage @end + +#endif /* MAPISTORECONTACTSMESSAGE_H */ diff --git a/OpenChange/MAPIStoreContactsMessage.m b/OpenChange/MAPIStoreContactsMessage.m new file mode 100644 index 000000000..5602b2dc8 --- /dev/null +++ b/OpenChange/MAPIStoreContactsMessage.m @@ -0,0 +1,616 @@ +/* MAPIStoreContactsMessage.m - this file is part of SOGo + * + * Copyright (C) 2011 Inverse inc + * + * Author: Wolfgang Sourdeau + * + * 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. + */ + +#import +#import +#import +#import +#import +#import +#import +#import + +#import "MAPIStoreTypes.h" +#import "NSArray+MAPIStore.h" +#import "NSCalendarDate+MAPIStore.h" +#import "NSData+MAPIStore.h" +#import "NSString+MAPIStore.h" + +#import "MAPIStoreContactsMessage.h" + +#undef DEBUG +#include +#include +#include +#include + +@implementation MAPIStoreContactsMessage + +- (CardElement *) _element: (NSString *) elementTag + ofType: (NSString *) aType + excluding: (NSString *) aTypeToExclude + inCard: (NGVCard *) card +{ + NSArray *elements; + CardElement *ce, *found; + NSUInteger count, max; + + found = nil; + + elements = [[card childrenWithTag: elementTag] + cardElementsWithAttribute: @"type" + havingValue: aType]; + max = [elements count]; + for (count = 0; !found && count < max; count++) + { + ce = [elements objectAtIndex: count]; + if (!aTypeToExclude + || ![ce hasAttribute: @"type" havingValue: aTypeToExclude]) + found = ce; + } + + return found; +} + +- (enum MAPISTATUS) getProperty: (void **) data + withTag: (enum MAPITAGS) proptag +{ + NSString *stringValue, *stringValue2; + CardElement *element; + uint32_t longValue; + NGVCard *vCard; + enum MAPISTATUS rc; + + rc = MAPI_E_SUCCESS; + switch (proptag) + { + case PR_ICON_INDEX: // TODO + /* see http://msdn.microsoft.com/en-us/library/cc815472.aspx */ + *data = MAPILongValue (memCtx, 0x00000200); + break; + case PR_MESSAGE_CLASS_UNICODE: + *data = talloc_strdup (memCtx, "IPM.Contact"); + break; + // case PR_VD_NAME_UNICODE: + // *data = talloc_strdup (memCtx, "PR_VD_NAME_UNICODE"); + // break; + // case PR_EMS_AB_DXA_REMOTE_CLIENT_UNICODE: "Home:" ??? + // *data = talloc_strdup (memCtx, "PR_EMS..."); + // break; + case PR_OAB_NAME_UNICODE: + *data = talloc_strdup (memCtx, "PR_OAB_NAME_UNICODE"); + break; + case PR_OAB_LANGID: + /* see http://msdn.microsoft.com/en-us/goglobal/bb895996.asxp */ + /* English US */ + *data = MAPILongValue (memCtx, 0x0409); + break; + + case PR_TITLE_UNICODE: + stringValue = [[sogoObject vCard] title]; + *data = [stringValue asUnicodeInMemCtx: memCtx]; + break; + + case PR_COMPANY_NAME_UNICODE: + case PR_DEPARTMENT_NAME_UNICODE: + { + NSArray *values; + + values = [[sogoObject vCard] org]; + stringValue = nil; + + if (proptag == PR_COMPANY_NAME_UNICODE && [values count] > 0) + stringValue = [values objectAtIndex: 0]; + else if (proptag == PR_DEPARTMENT_NAME_UNICODE && [values count] > 1) + stringValue = [values objectAtIndex: 1]; + + if (!stringValue) + stringValue = @""; + *data = [stringValue asUnicodeInMemCtx: memCtx]; + } + break; + + case PR_SEND_INTERNET_ENCODING: + *data = MAPILongValue (memCtx, 0x00065001); + break; + + case PR_SUBJECT_UNICODE: + case PR_DISPLAY_NAME_UNICODE: // Full Name + case PidLidFileUnder: // contact block title name + rc = [super getProperty: data + withTag: PR_DISPLAY_NAME_UNICODE]; + break; + case PidLidFileUnderId: + *data = MAPILongValue (memCtx, 0xffffffff); + break; + + case PidLidEmail1OriginalDisplayName: + case PidLidEmail1DisplayName: + vCard = [sogoObject vCard]; + stringValue = [vCard fn]; + stringValue2 = [vCard preferredEMail]; + *data = [[NSString stringWithFormat: @"%@ <%@>", + stringValue, stringValue2] + asUnicodeInMemCtx: memCtx]; + break; + + case PidLidEmail1EmailAddress: + case PR_ACCOUNT_UNICODE: + stringValue = [[sogoObject vCard] preferredEMail]; + *data = [stringValue asUnicodeInMemCtx: memCtx]; + break; + + case PR_CONTACT_EMAIL_ADDRESSES_UNICODE: + stringValue = [[sogoObject vCard] preferredEMail]; + *data = [[NSArray arrayWithObject: stringValue] + asArrayOfUnicodeStringsInCtx: memCtx]; + break; + + case PR_EMS_AB_TARGET_ADDRESS_UNICODE: + stringValue = [[sogoObject vCard] preferredEMail]; + *data = [[NSString stringWithFormat: @"SMTP:%@", stringValue] + asUnicodeInMemCtx: memCtx]; + break; + + case PR_SEARCH_KEY: // TODO + stringValue = [[sogoObject vCard] preferredEMail]; + *data = [[stringValue dataUsingEncoding: NSASCIIStringEncoding] + asBinaryInMemCtx: memCtx]; + break; + + case PR_MAIL_PERMISSION: + *data = MAPIBoolValue (memCtx, YES); + break; + + // + // TODO - same logic as -secondaryEmail in UI/Contacts/UIxContactView.m + // We should eventually merge that in order to not duplicate the code. + // We should also eventually handle PidLidEmail3OriginalDisplayName in + // SOGo, Thunderbird, etc. + // + case PidLidEmail2EmailAddress: + case PidLidEmail2OriginalDisplayName: // Other email + { + NSMutableArray *emails; + NSString *email; + NGVCard *card; + + emails = [NSMutableArray array]; + stringValue = nil; + + card = [sogoObject vCard]; + [emails addObjectsFromArray: [card childrenWithTag: @"email"]]; + [emails removeObjectsInArray: [card childrenWithTag: @"email" + andAttribute: @"type" + havingValue: @"pref"]]; + + if ([emails count] > 0) + { + int i; + + for (i = 0; i < [emails count]; i++) + { + email = [[emails objectAtIndex: i] value: 0]; + + if ([email caseInsensitiveCompare: [card preferredEMail]] != NSOrderedSame) + { + stringValue = email; + break; + } + } + } + + if (!stringValue) + stringValue = @""; + + *data = [stringValue asUnicodeInMemCtx: memCtx]; + } + break; + + // FIXME: this property does NOT work + case PR_BODY_UNICODE: + stringValue = [[sogoObject vCard] note]; + *data = [stringValue asUnicodeInMemCtx: memCtx]; + break; + + case PR_OFFICE_TELEPHONE_NUMBER_UNICODE: + element = [self _element: @"tel" ofType: @"work" + excluding: @"fax" + inCard: [sogoObject vCard]]; + if (element) + stringValue = [element value: 0]; + else + stringValue = @""; + *data = [stringValue asUnicodeInMemCtx: memCtx]; + break; + case PR_HOME_TELEPHONE_NUMBER_UNICODE: + element = [self _element: @"tel" ofType: @"home" + excluding: @"fax" + inCard: [sogoObject vCard]]; + if (element) + stringValue = [element value: 0]; + else + stringValue = @""; + *data = [stringValue asUnicodeInMemCtx: memCtx]; + break; + case PR_MOBILE_TELEPHONE_NUMBER_UNICODE: + element = [self _element: @"tel" ofType: @"cell" + excluding: nil + inCard: [sogoObject vCard]]; + if (element) + stringValue = [element value: 0]; + else + stringValue = @""; + *data = [stringValue asUnicodeInMemCtx: memCtx]; + break; + case PR_PRIMARY_TELEPHONE_NUMBER_UNICODE: + element = [self _element: @"tel" ofType: @"pref" + excluding: nil + inCard: [sogoObject vCard]]; + if (element) + stringValue = [element value: 0]; + else + stringValue = @""; + *data = [stringValue asUnicodeInMemCtx: memCtx]; + break; + + case PR_BUSINESS_HOME_PAGE_UNICODE: + case PR_PERSONAL_HOME_PAGE_UNICODE: + { + NSString *type; + + type = (proptag == PR_BUSINESS_HOME_PAGE_UNICODE ? @"work" : @"home"); + element = [self _element: @"url" ofType: type + excluding: nil + inCard: [sogoObject vCard]]; + if (element) + stringValue = [element value: 0]; + else + stringValue = @""; + *data = [stringValue asUnicodeInMemCtx: memCtx]; + } + break; + + case PidLidEmail1AddressType: + case PidLidEmail2AddressType: + case PidLidEmail3AddressType: + *data = [@"SMTP" asUnicodeInMemCtx: memCtx]; + break; + + case PidLidInstantMessagingAddress: + stringValue = [[[sogoObject vCard] uniqueChildWithTag: @"x-aim"] value: 0]; + + if (!stringValue) + stringValue = @""; + *data = [stringValue asUnicodeInMemCtx: memCtx]; + break; + + // + // We don't handle 0x00000003 - The Other Address is the mailing address. + // See: http://msdn.microsoft.com/en-us/library/cc815430.aspx + // + case PidLidPostalAddressId: + element = [self _element: @"adr" ofType: @"pref" + excluding: nil + inCard: [sogoObject vCard]]; + if ([element hasAttribute: @"type" + havingValue: @"home"]) + longValue = 1; // The Home Address is the mailing address. + else if ([element hasAttribute: @"type" + havingValue: @"work"]) + longValue = 2; // The Work Address is the mailing address. + else + longValue = 0; // No address is selected as the mailing address. + *data = MAPILongValue (memCtx, longValue); + break; + + /* preferred address */ + case PR_POSTAL_ADDRESS_UNICODE: + element = [self _element: @"label" ofType: @"pref" + excluding: nil + inCard: [sogoObject vCard]]; + if (element) + stringValue = [element value: 0]; + else + stringValue = @""; + *data = [stringValue asUnicodeInMemCtx: memCtx]; + break; + case PR_POST_OFFICE_BOX_UNICODE: + element = [self _element: @"adr" ofType: @"pref" + excluding: nil + inCard: [sogoObject vCard]]; + if (element) + stringValue = [element value: 0]; + else + stringValue = @""; + *data = [stringValue asUnicodeInMemCtx: memCtx]; + break; + case PR_STREET_ADDRESS_UNICODE: + element = [self _element: @"adr" ofType: @"pref" + excluding: nil + inCard: [sogoObject vCard]]; + if (element) + stringValue = [element value: 2]; + else + stringValue = @""; + *data = [stringValue asUnicodeInMemCtx: memCtx]; + break; + case PR_LOCALITY_UNICODE: + element = [self _element: @"adr" ofType: @"pref" + excluding: nil + inCard: [sogoObject vCard]]; + if (element) + stringValue = [element value: 3]; + else + stringValue = @""; + *data = [stringValue asUnicodeInMemCtx: memCtx]; + break; + case PR_STATE_OR_PROVINCE_UNICODE: + element = [self _element: @"adr" ofType: @"pref" + excluding: nil + inCard: [sogoObject vCard]]; + if (element) + stringValue = [element value: 4]; + else + stringValue = @""; + *data = [stringValue asUnicodeInMemCtx: memCtx]; + break; + case PR_POSTAL_CODE_UNICODE: + element = [self _element: @"adr" ofType: @"pref" + excluding: nil + inCard: [sogoObject vCard]]; + if (element) + stringValue = [element value: 5]; + else + stringValue = @""; + *data = [stringValue asUnicodeInMemCtx: memCtx]; + break; + case PR_COUNTRY_UNICODE: + element = [self _element: @"adr" ofType: @"pref" + excluding: nil + inCard: [sogoObject vCard]]; + if (element) + stringValue = [element value: 6]; + else + stringValue = @""; + *data = [stringValue asUnicodeInMemCtx: memCtx]; + break; + + // case PidLidAddressCountryCode: + + case PidLidWorkAddress: + element = [self _element: @"label" ofType: @"work" + excluding: nil + inCard: [sogoObject vCard]]; + if (element) + stringValue = [element value: 0]; + else + stringValue = @""; + *data = [stringValue asUnicodeInMemCtx: memCtx]; + break; + + case PidLidWorkAddressPostOfficeBox: + element = [self _element: @"adr" ofType: @"work" + excluding: nil + inCard: [sogoObject vCard]]; + if (element) + stringValue = [element value: 0]; + else + stringValue = @""; + *data = [stringValue asUnicodeInMemCtx: memCtx]; + break; + case PidLidWorkAddressStreet: + element = [self _element: @"adr" ofType: @"work" + excluding: nil + inCard: [sogoObject vCard]]; + if (element) + stringValue = [element value: 2]; + else + stringValue = @""; + *data = [stringValue asUnicodeInMemCtx: memCtx]; + break; + case PidLidWorkAddressCity: + element = [self _element: @"adr" ofType: @"work" + excluding: nil + inCard: [sogoObject vCard]]; + if (element) + stringValue = [element value: 3]; + else + stringValue = @""; + *data = [stringValue asUnicodeInMemCtx: memCtx]; + break; + case PidLidWorkAddressState: + element = [self _element: @"adr" ofType: @"work" + excluding: nil + inCard: [sogoObject vCard]]; + if (element) + stringValue = [element value: 4]; + else + stringValue = @""; + *data = [stringValue asUnicodeInMemCtx: memCtx]; + break; + case PidLidWorkAddressPostalCode: + element = [self _element: @"adr" ofType: @"work" + excluding: nil + inCard: [sogoObject vCard]]; + if (element) + stringValue = [element value: 5]; + else + stringValue = @""; + *data = [stringValue asUnicodeInMemCtx: memCtx]; + break; + case PidLidWorkAddressCountry: + element = [self _element: @"adr" ofType: @"work" + excluding: nil + inCard: [sogoObject vCard]]; + if (element) + stringValue = [element value: 6]; + else + stringValue = @""; + *data = [stringValue asUnicodeInMemCtx: memCtx]; + break; + + // PidTagNickname + case PR_NICKNAME_UNICODE: + stringValue = [[sogoObject vCard] nickname]; + *data = [stringValue asUnicodeInMemCtx: memCtx]; + break; + + case PR_BIRTHDAY: + { + NSCalendarDate *dateValue; + + stringValue = [[sogoObject vCard] bday]; + + if (stringValue) + { + dateValue = [NSCalendarDate dateWithString: stringValue + calendarFormat: @"%Y-%m-%d"]; + // FIXME: We add a day, otherwise Outlook 2003 will display at day earlier + dateValue = [dateValue addYear: 0 month: 0 day: 1 hour: 0 minute: 0 second: 0]; + *data = [dateValue asFileTimeInMemCtx: memCtx]; + } + else + rc = MAPI_E_NOT_FOUND; + } + break; + + default: + rc = [super getProperty: data withTag: proptag]; + } + + return rc; +} + +- (void) save +{ + NGVCard *newCard; + NSArray *elements; + CardElement *element; + int postalAddressId; + id value; + + [self logWithFormat: @"setMAPIProperties: %@", newProperties]; + + newCard = [sogoObject vCard]; + [newCard setTag: @"vcard"]; + [newCard setVersion: @"3.0"]; + [newCard setProdID: @"-//Inverse inc.//OpenChange+SOGo//EN"]; + [newCard setProfile: @"vCard"]; + + value = [newProperties + objectForKey: MAPIPropertyKey (PR_DISPLAY_NAME_UNICODE)]; + if (value) + [newCard setFn: value]; + + elements = [newCard childrenWithTag: @"email"]; + value = [newProperties objectForKey: MAPIPropertyKey (PidLidEmail1EmailAddress)]; + if (value) + { + if ([elements count] > 0) + [[elements objectAtIndex: 0] setValue: 0 to: value]; + else + [newCard addEmail: value + types: [NSArray arrayWithObject: @"pref"]]; + } + value = [newProperties objectForKey: MAPIPropertyKey (PidLidEmail2EmailAddress)]; + if (value) + { + if ([elements count] > 1) + [[elements objectAtIndex: 1] setValue: 0 to: value]; + else + [newCard addEmail: value types: nil]; + } + value = [newProperties objectForKey: MAPIPropertyKey (PidLidEmail3EmailAddress)]; + if (value) + { + if ([elements count] > 2) + [[elements objectAtIndex: 2] setValue: 0 to: value]; + else + [newCard addEmail: value types: nil]; + } + + postalAddressId = [[newProperties objectForKey: MAPIPropertyKey (PidLidPostalAddressId)] + intValue]; + + /* Work address */ + value = [newProperties objectForKey: MAPIPropertyKey (PidLidWorkAddress)]; + if ([value length]) + { + elements = [newCard childrenWithTag: @"label" + andAttribute: @"type" + havingValue: @"work"]; + if ([elements count] > 0) + element = [elements objectAtIndex: 0]; + else + { + element = [CardElement elementWithTag: @"label"]; + [element addAttribute: @"type" value: @"work"]; + [newCard addChild: element]; + } + if (postalAddressId == 2) + { + [element removeValue: @"pref" + fromAttribute: @"type"]; + [element addAttribute: @"type" + value: @"pref"]; + } + [element setValue: 0 to: value]; + } + + elements = [newCard childrenWithTag: @"adr" + andAttribute: @"type" + havingValue: @"work"]; + if ([elements count] > 0) + element = [elements objectAtIndex: 0]; + else + { + element = [CardElement elementWithTag: @"adr"]; + [element addAttribute: @"type" value: @"work"]; + [newCard addChild: element]; + } + if (postalAddressId == 2) + [element addAttribute: @"type" + value: @"pref"]; + value = [newProperties objectForKey: MAPIPropertyKey (PidLidWorkAddressPostOfficeBox)]; + if (value) + [element setValue: 0 to: value]; + value = [newProperties objectForKey: MAPIPropertyKey (PidLidWorkAddressStreet)]; + if (value) + [element setValue: 2 to: value]; + value = [newProperties objectForKey: MAPIPropertyKey (PidLidWorkAddressCity)]; + if (value) + [element setValue: 3 to: value]; + value = [newProperties objectForKey: MAPIPropertyKey (PidLidWorkAddressState)]; + if (value) + [element setValue: 4 to: value]; + value = [newProperties objectForKey: MAPIPropertyKey (PidLidWorkAddressPostalCode)]; + if (value) + [element setValue: 5 to: value]; + value = [newProperties objectForKey: MAPIPropertyKey (PidLidWorkAddressCountry)]; + if (value) + [element setValue: 6 to: value]; + + [sogoObject saveContentString: [newCard versitString]]; +} + +@end diff --git a/OpenChange/SOGoMailObject+MAPIStore.h b/OpenChange/MAPIStoreDraftsMessage.h similarity index 70% rename from OpenChange/SOGoMailObject+MAPIStore.h rename to OpenChange/MAPIStoreDraftsMessage.h index f4ee033cf..926fab1e2 100644 --- a/OpenChange/SOGoMailObject+MAPIStore.h +++ b/OpenChange/MAPIStoreDraftsMessage.h @@ -1,4 +1,4 @@ -/* SOGoMailObject+MAPIStore.h - this file is part of SOGo +/* MAPIStoreDraftsMessage.h - this file is part of SOGo * * Copyright (C) 2011 Inverse inc * @@ -20,19 +20,15 @@ * Boston, MA 02111-1307, USA. */ -#ifndef SOGOMAILOBJECT_MAPISTORE_H -#define SOGOMAILOBJECT_MAPISTORE_H +#ifndef MAPISTOREDRAFTSMESSAGE_H +#define MAPISTOREDRAFTSMESSAGE_H -#import +#import "MAPIStoreMailMessage.h" -@class NSDictionary; +@class NSMutableArray; -@interface SOGoMailObject (MAPIStoreMessage) - -- (void) setMAPIProperties: (NSDictionary *) properties; - -- (void) MAPISave; +@interface MAPIStoreDraftsMessage : MAPIStoreMailMessage @end -#endif /* SOGOMAILOBJECT_MAPISTORE_H */ +#endif /* MAPISTOREDRAFTSMESSAGE_H */ diff --git a/OpenChange/MAPIStoreDraftsMessage.m b/OpenChange/MAPIStoreDraftsMessage.m new file mode 100644 index 000000000..60e89b203 --- /dev/null +++ b/OpenChange/MAPIStoreDraftsMessage.m @@ -0,0 +1,246 @@ +/* MAPIStoreDraftsMessage.m - this file is part of SOGo + * + * Copyright (C) 2011 Inverse inc + * + * Author: Wolfgang Sourdeau + * + * 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. + */ + +#import +#import +#import +#import +#import +#import +#import +#import + +#import "MAPIStoreAttachmentTable.h" +#import "MAPIStoreContext.h" +#import "MAPIStoreDraftsAttachment.h" +#import "MAPIStoreTypes.h" + +#import "MAPIStoreDraftsMessage.h" + +#include +#include + +@implementation MAPIStoreDraftsMessage + +- (id) init +{ + if ((self = [super init])) + { + attachmentKeys = [NSMutableArray new]; + attachmentParts = [NSMutableDictionary new]; + } + + return self; +} + +- (void) _saveAttachment: (NSString *) attachmentKey +{ + NSDictionary *properties, *metadata; + NSString *filename, *mimeType; + NSData *content; + MAPIStoreAttachment *attachment; + + attachment = [attachmentParts objectForKey: attachmentKey]; + properties = [attachment newProperties]; + mimeType = [properties + objectForKey: MAPIPropertyKey (PR_ATTACH_MIME_TAG_UNICODE)]; + if (!mimeType) + mimeType = @"application/octet-stream"; + filename + = [properties + objectForKey: MAPIPropertyKey (PR_ATTACH_LONG_FILENAME_UNICODE)]; + if (![filename length]) + { + filename + = [properties + objectForKey: MAPIPropertyKey (PR_ATTACH_FILENAME_UNICODE)]; + if (![filename length]) + filename = @"untitled.bin"; + } + + content = [properties 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; + NSUInteger count, max; + WOContext *woContext; + id value; + + newHeaders = [NSMutableDictionary dictionaryWithCapacity: 6]; + recipients = [newProperties 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) */ + + value = [newProperties + objectForKey: MAPIPropertyKey (PR_NORMALIZED_SUBJECT_UNICODE)]; + if (!value) + value = [newProperties objectForKey: MAPIPropertyKey (PR_SUBJECT_UNICODE)]; + if (value) + [newHeaders setObject: value forKey: @"subject"]; + + woContext = [[self context] woContext]; + identity = [[woContext activeUser] primaryIdentity]; + [newHeaders setObject: [identity keysWithFormat: @"%{fullName} <%{email}>"] + forKey: @"from"]; + [sogoObject setHeaders: newHeaders]; + + value = [newProperties 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 = [newProperties objectForKey: MAPIPropertyKey (PR_BODY_UNICODE)]; + if (value) + { + [sogoObject setIsHTML: NO]; + [sogoObject setText: value]; + } + } + + max = [attachmentKeys count]; + for (count = 0; count < max; count++) + [self _saveAttachment: [attachmentKeys objectAtIndex: count]]; +} + +- (MAPIStoreAttachmentTable *) attachmentTable +{ + if (!attachmentTable) + { + attachmentTable = [MAPIStoreAttachmentTable tableForContainer: self]; + [attachmentTable retain]; + } + + return attachmentTable; +} + +- (MAPIStoreAttachment *) createAttachment +{ + MAPIStoreDraftsAttachment *newAttachment; + uint32_t newAid; + NSString *newKey; + + newAid = [attachmentKeys count]; + + newAttachment = [MAPIStoreDraftsAttachment new]; + [newAttachment setAID: newAid]; + newKey = [NSString stringWithFormat: @"%ul", newAid]; + [attachmentParts setObject: newAttachment + forKey: newKey]; + [attachmentKeys addObject: newKey]; + [newAttachment release]; + + return newAttachment; +} + +- (NSArray *) childKeysMatchingQualifier: (EOQualifier *) qualifier + andSortOrderings: (NSArray *) sortOrderings +{ + if (qualifier) + [self errorWithFormat: @"qualifier is not used for attachments"]; + if (sortOrderings) + [self errorWithFormat: @"sort orderings are not used for attachments"]; + + return attachmentKeys; +} + +- (id) lookupChild: (NSString *) childKey +{ + return [attachmentParts objectForKey: childKey]; +} + +- (void) submit +{ + NSString *msgClass; + + msgClass = [newProperties + objectForKey: MAPIPropertyKey (PR_MESSAGE_CLASS_UNICODE)]; + if (![msgClass isEqualToString: @"IPM.Schedule.Meeting.Request"]) + { + [self logWithFormat: @"sending message"]; + [self _commitProperties]; + [(SOGoDraftObject *) sogoObject sendMail]; + } + else + [self logWithFormat: @"ignored scheduling message"]; +} + +- (void) save +{ + NSString *msgClass; + + msgClass = [newProperties + 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"]; +} + +@end diff --git a/OpenChange/MAPIStoreFSMessage.h b/OpenChange/MAPIStoreFSMessage.h new file mode 100644 index 000000000..03a9b3fb4 --- /dev/null +++ b/OpenChange/MAPIStoreFSMessage.h @@ -0,0 +1,31 @@ +/* MAPIStoreFSMessage.h - this file is part of SOGo + * + * Copyright (C) 2011 Inverse inc + * + * Author: Wolfgang Sourdeau + * + * 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 MAPISTOREFSMESSAGE_H +#define MAPISTOREFSMESSAGE_H + +#import "MAPIStoreMessage.h" + +@interface MAPIStoreFSMessage : MAPIStoreMessage +@end + +#endif /* MAPISTOREFSMESSAGE_H */ diff --git a/OpenChange/SOGoMailObject+MAPIStore.m b/OpenChange/MAPIStoreFSMessage.m similarity index 60% rename from OpenChange/SOGoMailObject+MAPIStore.m rename to OpenChange/MAPIStoreFSMessage.m index 2d68656ef..06ee5ac77 100644 --- a/OpenChange/SOGoMailObject+MAPIStore.m +++ b/OpenChange/MAPIStoreFSMessage.m @@ -1,4 +1,4 @@ -/* SOGoMailObject+MAPIStore.m - this file is part of SOGo +/* MAPIStoreFSMessage.m - this file is part of SOGo * * Copyright (C) 2011 Inverse inc * @@ -22,32 +22,33 @@ #import -#import - #import "MAPIStoreTypes.h" +#import "NSObject+MAPIStore.h" +#import "SOGoMAPIFSMessage.h" -#import "SOGoMailObject+MAPIStore.h" +#import "MAPIStoreFSMessage.h" -@implementation SOGoMailObject (MAPIStoreMessage) +@implementation MAPIStoreFSMessage -- (void) setMAPIProperties: (NSDictionary *) properties +- (enum MAPISTATUS) getProperty: (void **) data + withTag: (enum MAPITAGS) propTag { id value; + enum MAPISTATUS rc; - value = [properties objectForKey: MAPIPropertyKey (PR_FLAG_STATUS)]; + value = [[sogoObject properties] objectForKey: MAPIPropertyKey (propTag)]; if (value) - { - /* We don't handle the concept of "Follow Up" */ - if ([value intValue] == 2) - [self addFlags: @"\\Flagged"]; - else /* 0: unflagged, 1: follow up complete */ - [self removeFlags: @"\\Flagged"]; - } + rc = [value getMAPIValue: data forTag: propTag inMemCtx: memCtx]; + else + rc = [super getProperty: data withTag: propTag]; + + return rc; } -- (void) MAPISave +- (void) save { - /* stub */ + [sogoObject appendProperties: newProperties]; + [sogoObject save]; } @end diff --git a/OpenChange/MAPIStoreGCSMessage.h b/OpenChange/MAPIStoreGCSMessage.h new file mode 100644 index 000000000..6c3886db3 --- /dev/null +++ b/OpenChange/MAPIStoreGCSMessage.h @@ -0,0 +1,31 @@ +/* MAPIStoreGCSMessage.h - this file is part of SOGo + * + * Copyright (C) 2011 Inverse inc + * + * Author: Wolfgang Sourdeau + * + * 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 MAPISTOREGCSMESSAGE_H +#define MAPISTOREGCSMESSAGE_H + +#import "MAPIStoreMessage.h" + +@interface MAPIStoreGCSMessage : MAPIStoreMessage +@end + +#endif /* MAPISTOREGCSMESSAGE_H */ diff --git a/OpenChange/MAPIStoreGCSMessage.m b/OpenChange/MAPIStoreGCSMessage.m new file mode 100644 index 000000000..e7569c044 --- /dev/null +++ b/OpenChange/MAPIStoreGCSMessage.m @@ -0,0 +1,27 @@ +/* MAPIStoreGCSMessage.m - this file is part of SOGo + * + * Copyright (C) 2011 Inverse inc + * + * Author: Wolfgang Sourdeau + * + * 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. + */ + +#import "MAPIStoreGCSMessage.h" + +@implementation MAPIStoreGCSMessage + +@end diff --git a/OpenChange/SOGoDraftObject+MAPIStore.h b/OpenChange/MAPIStoreMailMessage.h similarity index 62% rename from OpenChange/SOGoDraftObject+MAPIStore.h rename to OpenChange/MAPIStoreMailMessage.h index ac4bec51c..4f8176655 100644 --- a/OpenChange/SOGoDraftObject+MAPIStore.h +++ b/OpenChange/MAPIStoreMailMessage.h @@ -1,12 +1,12 @@ -/* SOGoDraftObject+MAPIStore.h - this file is part of SOGo +/* MAPIStoreMailMessage.h - this file is part of SOGo * - * Copyright (C) 2010 Inverse inc. + * Copyright (C) 2011 Inverse inc * * Author: Wolfgang Sourdeau * * 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) + * 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, @@ -20,17 +20,13 @@ * Boston, MA 02111-1307, USA. */ -#ifndef SOGODRAFTOBJECT_MAPISTORE_H -#define SOGODRAFTOBJECT_MAPISTORE_H +#ifndef MAPISTOREMAILMESSAGE_H +#define MAPISTOREMAILMESSAGE_H -#import +#import "MAPIStoreMessage.h" -@interface SOGoDraftObject (MAPIStoreMessage) - -- (void) setMAPIProperties: (NSDictionary *) properties; - -- (void) MAPISubmit; +@interface MAPIStoreMailMessage : MAPIStoreMessage @end -#endif /* SOGODRAFTOBJECT_MAPISTORE_H */ +#endif /* MAPISTOREMAILMESSAGE_H */ diff --git a/OpenChange/MAPIStoreMailMessage.m b/OpenChange/MAPIStoreMailMessage.m new file mode 100644 index 000000000..56c7b3322 --- /dev/null +++ b/OpenChange/MAPIStoreMailMessage.m @@ -0,0 +1,703 @@ +/* MAPIStoreMailMessage.m - this file is part of SOGo + * + * Copyright (C) 2011 Inverse inc + * + * Author: Wolfgang Sourdeau + * + * 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. + */ + +#import +#import +#import +#import +#import +#import +#import +#import +#import + +#import "NSCalendarDate+MAPIStore.h" +#import "NSData+MAPIStore.h" +#import "NSString+MAPIStore.h" +#import "MAPIStoreContext.h" +#import "MAPIStoreFolder.h" +#import "MAPIStoreTypes.h" +#import "MAPIStoreMailAttachment.h" + +#import "MAPIStoreMailMessage.h" + +#undef DEBUG +#include +#include +#include +#include + +static Class NSExceptionK; + +@interface NSString (MAPIStoreMIME) + +- (NSString *) _strippedBodyKey; + +@end + +@implementation NSString (MAPIStoreMIME) + +- (NSString *) _strippedBodyKey +{ + NSRange bodyRange; + NSString *strippedKey; + + bodyRange = [self rangeOfString: @"body["]; + if (bodyRange.length > 0) + { + strippedKey = [self substringFromIndex: NSMaxRange (bodyRange)]; + strippedKey = [strippedKey substringToIndex: [strippedKey length] - 1]; + } + else + strippedKey = nil; + + return strippedKey; +} + +@end + +@implementation MAPIStoreMailMessage + ++ (void) initialize +{ + NSExceptionK = [NSException class]; +} + +- (MAPIStoreAttachmentTable *) attachmentTable +{ + if (!attachmentTable) + { + attachmentTable = [MAPIStoreAttachmentTable tableForContainer: self]; + [attachmentTable retain]; + } + + return attachmentTable; +} + +- (enum MAPISTATUS) getProperty: (void **) data + withTag: (enum MAPITAGS) propTag +{ + NSString *subject, *stringValue; + NSInteger colIdx; + uint32_t intValue; + enum MAPISTATUS rc; + + rc = MAPI_E_SUCCESS; + switch (propTag) + { + case PR_ICON_INDEX: + /* see http://msdn.microsoft.com/en-us/library/cc815472.aspx */ + if ([sogoObject isNewMail]) + intValue = 0xffffffff; + else if ([sogoObject replied]) + intValue = 0x105; + else if ([sogoObject forwarded]) + intValue = 0x106; + else if ([sogoObject read]) + intValue = 0x100; + else + intValue = 0x101; + *data = MAPILongValue (memCtx, intValue); + break; + case PidLidImapDeleted: + if ([sogoObject deleted]) + intValue = 1; + else + intValue = 0; + *data = MAPILongValue (memCtx, intValue); + break; + case PR_SUBJECT_UNICODE: + stringValue = [sogoObject decodedSubject]; + if (!stringValue) + stringValue = @""; + *data = [stringValue asUnicodeInMemCtx: memCtx]; + break; + case PR_SUBJECT_PREFIX_UNICODE: + subject = [sogoObject decodedSubject]; + colIdx = [subject rangeOfString: @":"].location; + if (colIdx != NSNotFound && colIdx < 4) + stringValue = [NSString stringWithFormat: @"%@: ", + [subject substringToIndex: colIdx]]; + else + stringValue = @""; + *data = [stringValue asUnicodeInMemCtx: memCtx]; + break; + case PR_NORMALIZED_SUBJECT_UNICODE: + subject = [sogoObject decodedSubject]; + colIdx = [subject rangeOfString: @":"].location; + if (colIdx != NSNotFound && colIdx < 4) + stringValue = [[subject substringFromIndex: colIdx + 1] + stringByTrimmingLeadSpaces]; + else + stringValue = subject; + if (!stringValue) + stringValue = @""; + *data = [stringValue asUnicodeInMemCtx: memCtx]; + break; + + case PR_MESSAGE_CLASS_UNICODE: + case PR_ORIG_MESSAGE_CLASS_UNICODE: + *data = talloc_strdup (memCtx, "IPM.Note"); + break; + // case PidLidRemoteAttachment: // TODO + case PR_REPLY_REQUESTED: // TODO + case PR_RESPONSE_REQUESTED: // TODO + *data = MAPIBoolValue (memCtx, NO); + break; + // case PidLidHeaderItem: + // *data = MAPILongValue (memCtx, 0x00000001); + // break; + // case PidLidRemoteTransferSize: + // rc = [self getChildProperty: data + // forKey: childKey + // withTag: PR_MESSAGE_SIZE]; + // break; + case PR_CREATION_TIME: // DOUBT + case PR_LAST_MODIFICATION_TIME: // DOUBT + case PR_LATEST_DELIVERY_TIME: // DOUBT + case PR_ORIGINAL_SUBMIT_TIME: + case PR_CLIENT_SUBMIT_TIME: + case PR_MESSAGE_DELIVERY_TIME: + // offsetDate = [[sogoObject date] addYear: -1 month: 0 day: 0 + // hour: 0 minute: 0 second: 0]; + // *data = [offsetDate asFileTimeInMemCtx: memCtx]; + *data = [[sogoObject date] asFileTimeInMemCtx: memCtx]; + break; + case PR_MESSAGE_FLAGS: // TODO + { + NSDictionary *coreInfos; + NSArray *flags; + unsigned int v; + + coreInfos = [sogoObject fetchCoreInfos]; + + flags = [coreInfos objectForKey: @"flags"]; + v = MSGFLAG_FROMME; + + if ([flags containsObject: @"seen"]) + v |= MSGFLAG_READ; + + *data = MAPILongValue (memCtx, v); + } + break; + + case PR_FLAG_STATUS: + { + NSDictionary *coreInfos; + NSArray *flags; + unsigned int v; + + coreInfos = [sogoObject fetchCoreInfos]; + + flags = [coreInfos objectForKey: @"flags"]; + if ([flags containsObject: @"flagged"]) + v = 2; + else + v = 0; + + *data = MAPILongValue (memCtx, v); + } + break; + + case PR_FOLLOWUP_ICON: + { + NSDictionary *coreInfos; + NSArray *flags; + unsigned int v; + + coreInfos = [sogoObject fetchCoreInfos]; + + flags = [coreInfos objectForKey: @"flags"]; + if ([flags containsObject: @"flagged"]) + v = 6; + else + v = 0; + + *data = MAPILongValue (memCtx, v); + } + break; + + case PR_HASATTACH: + *data = MAPIBoolValue (memCtx, + [[self childKeysMatchingQualifier: nil + andSortOrderings: nil] count] > 0); + break; + + case PR_SENSITIVITY: // TODO + case PR_ORIGINAL_SENSITIVITY: // TODO + *data = MAPILongValue (memCtx, 0); + break; + + case PR_EXPIRY_TIME: // TODO + case PR_REPLY_TIME: + *data = [[NSCalendarDate date] asFileTimeInMemCtx: memCtx]; + break; + + case PR_SENT_REPRESENTING_ADDRTYPE_UNICODE: + case PR_RCVD_REPRESENTING_ADDRTYPE_UNICODE: + case PR_RECEIVED_BY_ADDRTYPE_UNICODE: + case PR_SENDER_ADDRTYPE_UNICODE: + *data = [@"SMTP" asUnicodeInMemCtx: memCtx]; + break; + case PR_ORIGINAL_AUTHOR_NAME_UNICODE: + case PR_SENDER_NAME_UNICODE: + case PR_SENDER_EMAIL_ADDRESS_UNICODE: + case PR_SENT_REPRESENTING_EMAIL_ADDRESS_UNICODE: + case PR_SENT_REPRESENTING_NAME_UNICODE: + *data = [[sogoObject from] asUnicodeInMemCtx: memCtx]; + break; + + /* TODO: some of the following are supposed to be display names, separated by a semicolumn */ + case PR_RECEIVED_BY_NAME_UNICODE: + case PR_RECEIVED_BY_EMAIL_ADDRESS_UNICODE: + case PR_RCVD_REPRESENTING_NAME_UNICODE: + case PR_RCVD_REPRESENTING_EMAIL_ADDRESS_UNICODE: + case PR_DISPLAY_TO_UNICODE: + case PR_ORIGINAL_DISPLAY_TO_UNICODE: + stringValue = [sogoObject to]; + if (!stringValue) + stringValue = @""; + *data = [stringValue asUnicodeInMemCtx: memCtx]; + break; + case PR_DISPLAY_CC_UNICODE: + case PR_ORIGINAL_DISPLAY_CC_UNICODE: + stringValue = [sogoObject cc]; + if (!stringValue) + stringValue = @""; + *data = [stringValue asUnicodeInMemCtx: memCtx]; + break; + case PR_DISPLAY_BCC_UNICODE: + case PR_ORIGINAL_DISPLAY_BCC_UNICODE: + stringValue = @""; + *data = [stringValue asUnicodeInMemCtx: memCtx]; + break; + + case PidNameContentType: + *data = [@"message/rfc822" asUnicodeInMemCtx: memCtx]; + break; + + + // + // TODO: Merge with the code in UI/MailerUI/UIxMailListActions.m: -messagePriority + // to avoid the duplication of the logic + // + case PR_IMPORTANCE: + { + unsigned int v; + NSString *s; + + s = [[sogoObject mailHeaders] 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); + } + break; + + case PR_BODY_UNICODE: + { + NSMutableArray *keys; + + keys = [NSMutableArray array]; + [sogoObject addRequiredKeysOfStructure: [sogoObject bodyStructure] + path: @"" toArray: keys + acceptedTypes: [NSArray arrayWithObject: + @"text/html"]]; + if ([keys count] > 0) + { + *data = NULL; + rc = MAPI_E_NOT_FOUND; + } + else + { + [keys removeAllObjects]; + [sogoObject addRequiredKeysOfStructure: [sogoObject bodyStructure] + path: @"" toArray: keys + acceptedTypes: [NSArray arrayWithObject: + @"text/plain"]]; + if ([keys count] > 0) + { + id result; + NSData *content; + NSDictionary *partHeaderData; + NSString *partKey, *encoding, *charset; + + result = [sogoObject fetchParts: [keys objectsForKey: @"key" + notFoundMarker: nil]]; + result = [[result valueForKey: @"RawResponse"] objectForKey: @"fetch"]; + partKey = [[keys objectAtIndex: 0] objectForKey: @"key"]; + content = [[result objectForKey: partKey] objectForKey: @"data"]; + + partHeaderData + = [sogoObject lookupInfoForBodyPart: [partKey _strippedBodyKey]]; + encoding = [partHeaderData objectForKey: @"encoding"]; + charset = [[partHeaderData objectForKey: @"parameterList"] + objectForKey: @"charset"]; + stringValue = [[content bodyDataFromEncoding: encoding] + bodyStringFromCharset: charset]; + + *data = [stringValue asUnicodeInMemCtx: memCtx]; + if (strlen (*data) > 16384) + { + /* TODO: currently a hack to transfer properties as + streams */ + [newProperties setObject: stringValue + forKey: MAPIPropertyKey (propTag)]; + *data = NULL; + rc = MAPI_E_NOT_ENOUGH_MEMORY; + [self logWithFormat: @"PR_BODY data too wide"]; + } + } + else + rc = MAPI_E_NOT_FOUND; + } + } + break; + + case PR_INTERNET_CPID: + /* ref: + http://msdn.microsoft.com/en-us/library/dd317756%28v=vs.85%29.aspx + + minimal list that should be handled: + us-ascii: 20127 + iso-8859-1: 28591 + iso-8859-15: 28605 + utf-8: 65001 */ + *data = MAPILongValue(memCtx, 65001); + break; + + case PR_HTML: + { + NSMutableArray *keys; + NSArray *acceptedTypes; + + acceptedTypes = [NSArray arrayWithObject: @"text/html"]; + keys = [NSMutableArray array]; + [sogoObject addRequiredKeysOfStructure: [sogoObject bodyStructure] + path: @"" toArray: keys acceptedTypes: acceptedTypes]; + if ([keys count] > 0) + { + id result; + NSData *content; + NSDictionary *partHeaderData; + NSString *key, *encoding; + char *oldBytes, *newBytes; + NSUInteger c, newC, max, newMax; + + result = [sogoObject fetchParts: [keys objectsForKey: @"key" + notFoundMarker: nil]]; + result = [[result valueForKey: @"RawResponse"] objectForKey: + @"fetch"]; + key = [[keys objectAtIndex: 0] objectForKey: @"key"]; + content = [[result objectForKey: key] objectForKey: @"data"]; + + max = [content length]; + newMax = max; + oldBytes = malloc (max); + newBytes = malloc (max * 2); + [content getBytes: oldBytes]; + newC = 0; + for (c = 0; c < max; c++) + { + if (*(oldBytes + c) == '\n') + { + *(newBytes + newC) = '\r'; + newC++; + newMax++; + } + *(newBytes + newC) = *(oldBytes + c); + newC++; + } + content = [[NSData alloc] initWithBytesNoCopy: newBytes + length: newMax];; + [content autorelease]; + + partHeaderData + = [sogoObject lookupInfoForBodyPart: [key _strippedBodyKey]]; + encoding = [partHeaderData objectForKey: @"encoding"]; + content = [content bodyDataFromEncoding: encoding]; + + if ([content length] > 16384) + { + /* TODO: currently a hack to transfer properties as + streams */ + [newProperties setObject: content + forKey: MAPIPropertyKey (propTag)]; + *data = NULL; + rc = MAPI_E_NOT_ENOUGH_MEMORY; + [self logWithFormat: @"PR_HTML data too wide"]; + } + else + *data = [content asBinaryInMemCtx: memCtx]; + } + else + { + *data = NULL; + rc = MAPI_E_NOT_FOUND; + } + } + break; + + /* We don't handle any RTF content. */ + case PR_RTF_COMPRESSED: + *data = NULL; + rc = MAPI_E_NOT_FOUND; + break; + case PR_RTF_IN_SYNC: + *data = MAPIBoolValue (memCtx, NO); + break; + case PR_INTERNET_MESSAGE_ID_UNICODE: + *data = [[sogoObject messageId] asUnicodeInMemCtx: memCtx]; + break; + case PR_READ_RECEIPT_REQUESTED: // TODO + case PR_DELETE_AFTER_SUBMIT: // TODO + *data = MAPIBoolValue (memCtx, NO); + break; + case PidLidPrivate: + *data = MAPIBoolValue (memCtx, NO); + break; + + case PR_MSG_EDITOR_FORMAT: + { + NSMutableArray *keys; + NSArray *acceptedTypes; + uint32_t format; + + format = 0; /* EDITOR_FORMAT_DONTKNOW */ + + acceptedTypes = [NSArray arrayWithObject: @"text/plain"]; + keys = [NSMutableArray array]; + [sogoObject addRequiredKeysOfStructure: [sogoObject bodyStructure] + path: @"" toArray: keys + acceptedTypes: acceptedTypes]; + if ([keys count] == 1) + format = EDITOR_FORMAT_PLAINTEXT; + + acceptedTypes = [NSArray arrayWithObject: @"text/html"]; + [keys removeAllObjects]; + [sogoObject addRequiredKeysOfStructure: [sogoObject bodyStructure] + path: @"" toArray: keys + acceptedTypes: acceptedTypes]; + if ([keys count] == 1) + format = EDITOR_FORMAT_HTML; + + *data = MAPILongValue (memCtx, format); + } + break; + + case PidLidReminderSet: // TODO + case PidLidUseTnef: // TODO + *data = MAPIBoolValue (memCtx, NO); + break; + case PidLidRemoteStatus: // TODO + *data = MAPILongValue (memCtx, 0); + break; + // case PidLidSmartNoAttach: // TODO + case PidLidAgingDontAgeMe: // TODO + *data = MAPIBoolValue (memCtx, YES); + break; + +// PidLidFlagRequest +// PidLidBillingInformation +// PidLidMileage +// PidLidCommonEnd +// PidLidCommonStart +// PidLidNonSendableBcc +// PidLidNonSendableCc +// PidLidNonSendtableTo +// PidLidNonSendBccTrackStatus +// PidLidNonSendCcTrackStatus +// PidLidNonSendToTrackStatus +// PidLidReminderDelta +// PidLidReminderFileParameter +// PidLidReminderSignalTime +// PidLidReminderOverride +// PidLidReminderPlaySound +// PidLidReminderTime +// PidLidReminderType +// PidLidSmartNoAttach +// PidLidTaskGlobalId +// PidLidTaskMode +// PidLidVerbResponse +// PidLidVerbStream +// PidLidInternetAccountName +// PidLidInternetAccountStamp +// PidLidContactLinkName +// PidLidContactLinkEntry +// PidLidContactLinkSearchKey +// PidLidSpamOriginalFolder + + default: + rc = [super getProperty: data withTag: propTag]; + } + + return rc; +} + +- (void) openMessage: (struct mapistore_message *) msg +{ + struct SRowSet *recipients; + NSArray *to; + NSInteger count, max; + NGImap4EnvelopeAddress *currentAddress; + NSString *name; + + [super openMessage: msg]; + /* Retrieve recipients from the message */ + to = [sogoObject toEnvelopeAddresses]; + max = [to count]; + recipients = talloc_zero (memCtx, struct SRowSet); + recipients->cRows = max; + recipients->aRow = talloc_array (recipients, struct SRow, max); + for (count = 0; count < max; count++) + { + recipients->aRow[count].ulAdrEntryPad = 0; + recipients->aRow[count].cValues = 2; + recipients->aRow[count].lpProps = talloc_array (recipients->aRow, + struct SPropValue, + 2); + + // TODO (0x01 = primary recipient) + set_SPropValue_proptag (&(recipients->aRow[count].lpProps[0]), + PR_RECIPIENT_TYPE, + MAPILongValue (memCtx, 0x01)); + + currentAddress = [to objectAtIndex: count]; + // name = [currentAddress personalName]; + // if (![name length]) + name = [currentAddress baseEMail]; + if (!name) + name = @""; + set_SPropValue_proptag (&(recipients->aRow[count].lpProps[1]), + PR_DISPLAY_NAME, + [name asUnicodeInMemCtx: recipients->aRow[count].lpProps]); + } + msg->recipients = recipients; +} + +- (void) _fetchAttachmentPartsInBodyInfo: (NSDictionary *) bodyInfo + withPrefix: (NSString *) keyPrefix +{ + NSArray *parts; + NSString *dispType; + NSUInteger count, max; + + dispType = [[bodyInfo objectForKey: @"disposition"] + objectForKey: @"type"]; + if ([[dispType lowercaseString] isEqualToString: @"attachment"]) + { + if ([keyPrefix length] == 0) + keyPrefix = @"0"; + [attachmentParts setObject: bodyInfo + forKey: keyPrefix]; + [attachmentKeys addObject: keyPrefix]; + } + else + { + if ([keyPrefix length] > 0) + keyPrefix = [NSString stringWithFormat: @"%@/", keyPrefix]; + parts = [bodyInfo objectForKey: @"parts"]; + max = [parts count]; + for (count = 0; count < max; count++) + [self _fetchAttachmentPartsInBodyInfo: [parts objectAtIndex: count] + withPrefix: [NSString stringWithFormat: @"%@%d", + keyPrefix, count + 1]]; + } +} + +- (NSArray *) childKeysMatchingQualifier: (EOQualifier *) qualifier + andSortOrderings: (NSArray *) sortOrderings +{ + if (!attachmentKeys) + { + attachmentKeys = [NSMutableArray new]; + attachmentParts = [NSMutableDictionary new]; + [self _fetchAttachmentPartsInBodyInfo: [sogoObject bodyStructure] + withPrefix: @""]; + } + + return attachmentKeys; +} + +- (id) lookupChild: (NSString *) childKey +{ + MAPIStoreMailAttachment *attachment; + SOGoMailBodyPart *currentPart; + NSArray *keyParts; + NSUInteger count, max; + + attachment = nil; + + keyParts = [childKey componentsSeparatedByString: @"/"]; + max = [keyParts count]; + if (max > 0) + { + currentPart = [sogoObject lookupName: [keyParts objectAtIndex: 0] + inContext: nil + acquire: NO]; + if ([currentPart isKindOfClass: NSExceptionK]) + currentPart = nil; + + for (count = 1; currentPart && count < max; count++) + { + [parentContainersBag addObject: currentPart]; + currentPart = [currentPart lookupName: [keyParts objectAtIndex: count] + inContext: nil + acquire: NO]; + if ([currentPart isKindOfClass: NSExceptionK]) + currentPart = nil; + } + + if (currentPart) + { + attachment = [MAPIStoreMailAttachment + mapiStoreObjectWithSOGoObject: currentPart + inContainer: self]; + [attachment setBodyInfo: [attachmentParts objectForKey: childKey]]; + [attachment setAID: [attachmentKeys indexOfObject: childKey]]; + } + } + + return attachment; +} + +- (void) save +{ + NSNumber *value; + + value = [newProperties objectForKey: MAPIPropertyKey (PR_FLAG_STATUS)]; + if (value) + { + /* We don't handle the concept of "Follow Up" */ + if ([value intValue] == 2) + [sogoObject addFlags: @"\\Flagged"]; + else /* 0: unflagged, 1: follow up complete */ + [sogoObject removeFlags: @"\\Flagged"]; + } +} + +@end diff --git a/OpenChange/MAPIStoreMessage.h b/OpenChange/MAPIStoreMessage.h new file mode 100644 index 000000000..e34def722 --- /dev/null +++ b/OpenChange/MAPIStoreMessage.h @@ -0,0 +1,61 @@ +/* MAPIStoreMessage.h - this file is part of SOGo + * + * Copyright (C) 2011 Inverse inc + * + * Author: Wolfgang Sourdeau + * + * 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 MAPISTOREMESSAGE_H +#define MAPISTOREMESSAGE_H + +#import + +@class NSMutableArray; +@class NSMutableDictionary; + +@class MAPIStoreAttachment; +@class MAPIStoreAttachmentTable; +@class MAPIStoreFolder; + +#import "MAPIStoreObject.h" + +@interface MAPIStoreMessage : MAPIStoreObject +{ + NSMutableArray *attachmentKeys; + NSMutableDictionary *attachmentParts; + MAPIStoreAttachmentTable *attachmentTable; + uint32_t mapiRetainCount; +} + +/* HACK: MAPI retain count */ +- (void) setMAPIRetainCount: (uint32_t) newCount; +- (uint32_t) mapiRetainCount; + +- (void) openMessage: (struct mapistore_message *) msg; + +/* subclasses */ +- (void) submit; +- (void) save; + +/* attachments (subclasses) */ +- (MAPIStoreAttachment *) createAttachment; +- (MAPIStoreAttachmentTable *) attachmentTable; + +@end + +#endif /* MAPISTOREMESSAGE_H */ diff --git a/OpenChange/MAPIStoreMessage.m b/OpenChange/MAPIStoreMessage.m new file mode 100644 index 000000000..1bcee1f86 --- /dev/null +++ b/OpenChange/MAPIStoreMessage.m @@ -0,0 +1,288 @@ +/* MAPIStoreMessage.m - this file is part of SOGo + * + * Copyright (C) 2011 Inverse inc + * + * Author: Wolfgang Sourdeau + * + * 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. + */ + +#import +#import +#import +#import + +#import "MAPIStoreTypes.h" +#import "NSData+MAPIStore.h" +#import "NSString+MAPIStore.h" + +#import "MAPIStoreMessage.h" + +#undef DEBUG +#include +#include +#include +#include +#include + +@interface SOGoObject (MAPIStoreProtocol) + +- (NSString *) davEntityTag; +- (NSString *) davContentLength; + +@end + +@implementation MAPIStoreMessage + +- (id) init +{ + if ((self = [super init])) + { + mapiRetainCount = 0; + attachmentKeys = nil; + attachmentParts = nil; + attachmentTable = nil; + } + + return self; +} + +- (void) dealloc +{ + [attachmentKeys release]; + [attachmentParts release]; + [attachmentTable release]; + [super dealloc]; +} + +- (void) setMAPIRetainCount: (uint32_t) newCount +{ + mapiRetainCount = newCount; +} + +- (uint32_t) mapiRetainCount +{ + return mapiRetainCount; +} + +- (void) openMessage: (struct mapistore_message *) msg +{ + static enum MAPITAGS tags[] = { PR_SUBJECT_PREFIX_UNICODE, + PR_NORMALIZED_SUBJECT_UNICODE }; + struct SRowSet *recipients; + struct SRow *properties; + NSInteger count, max; + const char *propName; + void *propValue; + + // [self logWithFormat: @"INCOMPLETE METHOD '%s' (%d): no recipient handling", + // __FUNCTION__, __LINE__]; + + recipients = talloc_zero (memCtx, struct SRowSet); + recipients->cRows = 0; + recipients->aRow = NULL; + msg->recipients = recipients; + + max = 2; + properties = talloc_zero (memCtx, struct SRow); + properties->cValues = 0; + properties->ulAdrEntryPad = 0; + properties->lpProps = talloc_array (properties, struct SPropValue, max); + for (count = 0; count < max; count++) + { + if ([self getProperty: &propValue withTag: tags[count]] + == MAPI_E_SUCCESS) + { + if (propValue == NULL) + { + propName = get_proptag_name (tags[count]); + if (!propName) + propName = ""; + [self errorWithFormat: @"both 'success' and NULL data" + @" returned for proptag %s(0x%.8x)", + propName, tags[count]]; + } + else + { + set_SPropValue_proptag (properties->lpProps + properties->cValues, + tags[count], + propValue); + properties->cValues++; + } + } + } + msg->properties = properties; +} + +- (int) getProperty: (void **) data + withTag: (enum MAPITAGS) propTag +{ + int rc; + NSString *stringValue; + NSUInteger length; + + rc = MAPI_E_SUCCESS; + switch (propTag) + { + case PR_INST_ID: // TODO: DOUBT + /* we return a unique id based on the key */ + *data = MAPILongLongValue (memCtx, [[sogoObject nameInContainer] hash]); + break; + case PR_INSTANCE_NUM: // TODO: DOUBT + *data = MAPILongValue (memCtx, 0); + break; + case PR_ROW_TYPE: // TODO: DOUBT + *data = MAPILongValue (memCtx, TBL_LEAF_ROW); + break; + case PR_DEPTH: // TODO: DOUBT + *data = MAPILongLongValue (memCtx, 0); + break; + case PR_ACCESS: // TODO + *data = MAPILongValue (memCtx, 0x03); + break; + case PR_ACCESS_LEVEL: // TODO + *data = MAPILongValue (memCtx, 0x01); + break; + case PR_VIEW_STYLE: + case PR_VIEW_MAJORVERSION: + *data = MAPILongValue (memCtx, 0); + break; + case PidLidSideEffects: // TODO + *data = MAPILongValue (memCtx, 0x00000000); + break; + case PidLidCurrentVersion: + *data = MAPILongValue (memCtx, 115608); // Outlook 11.5608 + break; + case PidLidCurrentVersionName: + *data = [@"11.0" asUnicodeInMemCtx: memCtx]; + break; + case PidLidAutoProcessState: + *data = MAPILongValue (memCtx, 0x00000000); + break; + case PidNameContentClass: + *data = [@"Sharing" asUnicodeInMemCtx: memCtx]; + break; + case PR_FID: + *data = MAPILongLongValue (memCtx, [container objectId]); + break; + case PR_MID: + *data = MAPILongLongValue (memCtx, [self objectId]); + break; + case PR_MESSAGE_LOCALE_ID: + *data = MAPILongValue (memCtx, 0x0409); + break; + case PR_MESSAGE_FLAGS: // TODO + *data = MAPILongValue (memCtx, MSGFLAG_FROMME | MSGFLAG_READ | MSGFLAG_UNMODIFIED); + break; + case PR_MESSAGE_SIZE: // TODO + /* TODO: choose another name in SOGo for that method */ + *data = MAPILongValue (memCtx, [[sogoObject davContentLength] intValue]); + break; + case PR_MSG_STATUS: // TODO + *data = MAPILongValue (memCtx, 0); + break; + case PR_IMPORTANCE: // TODO -> subclass? + *data = MAPILongValue (memCtx, 1); + break; + case PR_PRIORITY: // TODO -> subclass? + *data = MAPILongValue (memCtx, 0); + break; + case PR_SENSITIVITY: // TODO -> subclass in calendar + *data = MAPILongValue (memCtx, 0); + break; + case PR_CHANGE_KEY: + stringValue = [sogoObject davEntityTag]; + if (stringValue) + { + stringValue = @"-1"; + length = [stringValue length]; + if (length < 6) /* guid = 16 bytes */ + { + length += 6; + stringValue = [NSString stringWithFormat: @"000000%@", + stringValue]; + } + if (length > 6) + stringValue = [stringValue substringFromIndex: length - 6]; + stringValue = [NSString stringWithFormat: @"SOGo%@%@%@", + stringValue, stringValue, stringValue]; + *data = [[stringValue dataUsingEncoding: NSASCIIStringEncoding] + asShortBinaryInMemCtx: memCtx]; + } + else + rc = MAPISTORE_ERR_NOT_FOUND; + break; + + case PR_ORIGINAL_SUBJECT_UNICODE: + case PR_CONVERSATION_TOPIC_UNICODE: + rc = [self getProperty: data withTag: PR_NORMALIZED_SUBJECT_UNICODE]; + break; + + case PR_SUBJECT_PREFIX_UNICODE: + *data = [@"" asUnicodeInMemCtx: memCtx]; + break; + case PR_NORMALIZED_SUBJECT_UNICODE: + rc = [self getProperty: data withTag: PR_SUBJECT_UNICODE]; + break; + + case PR_DISPLAY_TO_UNICODE: + case PR_DISPLAY_CC_UNICODE: + case PR_DISPLAY_BCC_UNICODE: + case PR_ORIGINAL_DISPLAY_TO_UNICODE: + case PR_ORIGINAL_DISPLAY_CC_UNICODE: + case PR_ORIGINAL_DISPLAY_BCC_UNICODE: + *data = [@"" asUnicodeInMemCtx: memCtx]; + break; + case PR_LAST_MODIFIER_NAME_UNICODE: + *data = [@"openchange" asUnicodeInMemCtx: memCtx]; + break; + + case PR_ORIG_MESSAGE_CLASS_UNICODE: + rc = [self getProperty: data withTag: PR_MESSAGE_CLASS_UNICODE]; + break; + + default: + rc = [super getProperty: data withTag: propTag]; + } + + return rc; +} + +- (void) save +{ + [self subclassResponsibility: _cmd]; +} + +- (void) submit +{ + [self subclassResponsibility: _cmd]; +} + +- (MAPIStoreAttachment *) createAttachment +{ + [self subclassResponsibility: _cmd]; + + return nil; +} + +- (MAPIStoreAttachmentTable *) attachmentTable +{ + [self subclassResponsibility: _cmd]; + + return nil; +} + +@end diff --git a/OpenChange/MAPIStoreNotesMessage.h b/OpenChange/MAPIStoreNotesMessage.h new file mode 100644 index 000000000..81adfc0dc --- /dev/null +++ b/OpenChange/MAPIStoreNotesMessage.h @@ -0,0 +1,31 @@ +/* MAPIStoreNotesMessage.h - this file is part of SOGo + * + * Copyright (C) 2011 Inverse inc + * + * Author: Wolfgang Sourdeau + * + * 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 MAPISTORENOTESMESSAGE_H +#define MAPISTORENOTESMESSAGE_H + +#import "MAPIStoreFSMessage.h" + +@interface MAPIStoreNotesMessage : MAPIStoreFSMessage +@end + +#endif /* MAPISTORENOTESMESSAGE_H */ diff --git a/OpenChange/MAPIStoreNotesMessage.m b/OpenChange/MAPIStoreNotesMessage.m new file mode 100644 index 000000000..51896f499 --- /dev/null +++ b/OpenChange/MAPIStoreNotesMessage.m @@ -0,0 +1,58 @@ +/* MAPIStoreNotesMessage.m - this file is part of SOGo + * + * Copyright (C) 2011 Inverse inc + * + * Author: Wolfgang Sourdeau + * + * 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. + */ + +#import "MAPIStoreTypes.h" + +#import "MAPIStoreNotesMessage.h" + +@implementation MAPIStoreNotesMessage + +- (enum MAPISTATUS) getProperty: (void **) data + withTag: (enum MAPITAGS) propTag +{ + int rc; + + rc = MAPI_E_SUCCESS; + switch (propTag) + { + case PR_ICON_INDEX: // TODO + /* see http://msdn.microsoft.com/en-us/library/cc815472.aspx */ + // *longValue = 0x00000300 for blue + // *longValue = 0x00000301 for green + // *longValue = 0x00000302 for pink + // *longValue = 0x00000303 for yellow + // *longValue = 0x00000304 for white + *data = MAPILongValue (memCtx, 0x00000303); + break; + + case PR_SUBJECT_UNICODE: + rc = [super getProperty: data + withTag: PR_NORMALIZED_SUBJECT_UNICODE]; + break; + default: + rc = [super getProperty: data withTag: propTag]; + } + + return rc; +} + +@end diff --git a/OpenChange/MAPIStoreTasksMessage.h b/OpenChange/MAPIStoreTasksMessage.h new file mode 100644 index 000000000..273cde7cb --- /dev/null +++ b/OpenChange/MAPIStoreTasksMessage.h @@ -0,0 +1,32 @@ +/* MAPIStoreTasksMessage.h - this file is part of SOGo + * + * Copyright (C) 2011 Inverse inc + * + * Author: Wolfgang Sourdeau + * + * 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 MAPISTORETASKSMESSAGE_H +#define MAPISTORETASKSMESSAGE_H + +#import "MAPIStoreGCSMessage.h" + +@interface MAPIStoreTasksMessage : MAPIStoreGCSMessage + +@end + +#endif /* MAPISTORETASKSMESSAGE_H */ diff --git a/OpenChange/MAPIStoreTasksMessage.m b/OpenChange/MAPIStoreTasksMessage.m new file mode 100644 index 000000000..720a6450e --- /dev/null +++ b/OpenChange/MAPIStoreTasksMessage.m @@ -0,0 +1,377 @@ +/* MAPIStoreTasksMessage.m - this file is part of SOGo + * + * Copyright (C) 2011 Inverse inc + * + * Author: Wolfgang Sourdeau + * + * 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. + */ + +#import +#import +#import +#import +#import +#import +#import +#import +#import +#import +#import +#import +#import + +#import "MAPIStoreTypes.h" +#import "NSCalendarDate+MAPIStore.h" +#import "NSString+MAPIStore.h" + +#import "MAPIStoreTasksMessage.h" + +#undef DEBUG +#include +#include +#include +#include + +@implementation MAPIStoreTasksMessage + +- (enum MAPISTATUS) getProperty: (void **) data + withTag: (enum MAPITAGS) propTag +{ + NSString *status; + iCalToDo *task; + int32_t statusValue; + NSCalendarDate *dateValue; + int rc; + double doubleValue; + + rc = MAPI_E_SUCCESS; + switch (propTag) + { + case PR_ICON_INDEX: // TODO + /* see http://msdn.microsoft.com/en-us/library/cc815472.aspx */ + // Unassigned recurring task 0x00000501 + // Assignee's task 0x00000502 + // Assigner's task 0x00000503 + // Task request 0x00000504 + // Task acceptance 0x00000505 + // Task rejection 0x00000506 + *data = MAPILongValue (memCtx, 0x00000500); + break; + case PR_MESSAGE_CLASS_UNICODE: + *data = talloc_strdup(memCtx, "IPM.Task"); + break; + case PR_SUBJECT_UNICODE: // SUMMARY + task = [sogoObject component: NO secure: NO]; + *data = [[task summary] asUnicodeInMemCtx: memCtx]; + break; + + // + // Not to be confused with PR_PRIORITY + // + // IMPORTANCE_LOW = 0x0, IMPORTANCE_NORMAL = 0x1 and IMPORTANCE_HIGH = 0x2 + // + case PR_IMPORTANCE: + { + unsigned int v; + + task = [sogoObject component: NO secure: NO]; + + if ([[task priority] isEqualToString: @"9"]) + v = 0x0; + else if ([[task priority] isEqualToString: @"1"]) + v = 0x2; + else + v = 0x1; + + *data = MAPILongValue (memCtx, v); + } + break; + case PidLidTaskComplete: + task = [sogoObject component: NO secure: NO]; + *data = MAPIBoolValue (memCtx, + [[task status] isEqualToString: @"COMPLETED"]); + break; + case PidLidPercentComplete: + task = [sogoObject component: NO secure: NO]; + doubleValue = ((double) [[task percentComplete] intValue] / 100); + *data = MAPIDoubleValue (memCtx, doubleValue); + break; + case PidLidTaskDateCompleted: + task = [sogoObject component: NO secure: NO]; + dateValue = [task completed]; + if (dateValue) + *data = [dateValue asFileTimeInMemCtx: memCtx]; + else + rc = MAPI_E_NOT_FOUND; + break; + // http://msdn.microsoft.com/en-us/library/cc765590.aspx + // It's important to have a proper value for PidLidTaskState + // as it'll affect the UI options of Outlook - making the + // task non-editable in some cases. + case PidLidTaskState: + *data = MAPILongValue (memCtx, 0x1); // not assigned + break; + case PidLidTaskMode: // TODO + *data = MAPILongValue (memCtx, 0x0); + break; + case PidLidTaskFRecurring: + case PidLidTaskAccepted: // TODO + *data = MAPIBoolValue (memCtx, NO); + break; + case PidLidTaskActualEffort: // TODO + case PidLidTaskEstimatedEffort: // TODO + *data = MAPILongValue (memCtx, 0); + break; + case PR_HASATTACH: + *data = MAPIBoolValue (memCtx, NO); + break; + + case PidLidTaskDueDate: + task = [sogoObject component: NO secure: NO]; + dateValue = [task due]; + if (dateValue) + *data = [dateValue asFileTimeInMemCtx: memCtx]; + else + rc = MAPI_E_NOT_FOUND; + break; + + case PR_CREATION_TIME: + task = [sogoObject component: NO secure: NO]; + *data = [[task created] asFileTimeInMemCtx: memCtx]; + break; + case PR_MESSAGE_DELIVERY_TIME: + case PR_CLIENT_SUBMIT_TIME: + case PR_LOCAL_COMMIT_TIME: + case PR_LAST_MODIFICATION_TIME: + task = [sogoObject component: NO secure: NO]; + *data = [[task lastModified] asFileTimeInMemCtx: memCtx]; + break; + case PidLidTaskStatus: // status + task = [sogoObject component: NO secure: NO]; + status = [task status]; + if (![status length] + || [status isEqualToString: @"NEEDS-ACTION"]) + statusValue = 0; + else if ([status isEqualToString: @"IN-PROCESS"]) + statusValue = 1; + else if ([status isEqualToString: @"COMPLETED"]) + statusValue = 2; + else + statusValue = 0xff; + *data = MAPILongValue (memCtx, statusValue); + break; + /* Completed */ + // - 0x81380003 = -2000 + // + 0x81380003 = -4000 + + // 68330048 + // 68420102 + case 0x68340003: + case 0x683a0003: + case 0x68410003: + *data = MAPILongValue (memCtx, 0); + break; + + // FIXME - use the current user + case PidLidTaskOwner: + *data = [@"openchange@example.com" asUnicodeInMemCtx: memCtx]; + break; + + // See http://msdn.microsoft.com/en-us/library/cc842113.aspx + case PidLidTaskOwnership: + *data = MAPILongValue (memCtx, 0x0); // not assigned + break; + +// #define PidLidFlagRequest 0x9027001f +// #define PidNameContentType 0x905a001f +// #define PidLidBillingInformation 0x908b001f +// #define PidLidTaskStartDate 0x911e0040 +// #define PidLidTaskOwner 0x9122001f +// #define PidLidTaskDeadOccurrence 0x9127000b +// #define PidLidTaskMultipleRecipients 0x912a0003 +// #define PidLidTaskHistory 0x912b0003 +// #define PidLidAcceptanceState 0x912c0003 +// #define PidLidTaskLastUser 0x912d001f +// #define PidLidTaskLastUpdate 0x912e0040 +// #define PidLidTaskOwnership 0x912f0003 +// #define PidLidTaskNoCompute 0x9130000b +// #define PidLidTaskFFixOffline 0x9131000b +// #define PidLidTaskRole 0x9132001f +// #define PidLidTaskVersion 0x91330003 +// #define PidLidTaskAssigner 0x9134001f +// #define PidLidTeamTask 0x9135000b +// #define PidLidTaskRecurrence 0x91360102 +// #define PidLidTaskResetReminder 0x9137000b +// #define PidLidTaskOrdinal 0x91380003 +// #define PidLidMileage 0x914a001f +// #define PidLidAgingDontAgeMe 0x9185000b +// #define PidLidCommonEnd 0x91980040 +// #define PidLidCommonStart 0x91990040 +// #define PidLidNonSendableBcc 0x91d7001f +// #define PidLidNonSendableCc 0x91d8001f +// #define PidLidNonSendtableTo 0x91d9001f +// #define PidLidNonSendBccTrackStatus 0x91da1003 +// #define PidLidNonSendCcTrackStatus 0x91db1003 +// #define PidLidNonSendToTrackStatus 0x91dc1003 +// #define PidLidReminderDelta 0x91e90003 +// #define PidLidReminderFileParameter 0x91ea001f +// #define PidLidReminderSignalTime 0x91eb0040 +// #define PidLidReminderOverride 0x91ec000b +// #define PidLidReminderPlaySound 0x91ed000b +// #define PidLidReminderSet 0x91ee000b +// #define PidLidReminderTime 0x91ef0040 +// #define PidLidReminderType 0x91f20003 +// #define PidLidRemoteStatus 0x91f30003 +// #define PidLidSmartNoAttach 0x91fa000b +// #define PidLidTaskGlobalId 0x91fe0102 +// #define PidLidVerbResponse 0x9203001f +// #define PidLidVerbStream 0x92040102 +// #define PidLidPrivate 0x9224000b +// #define PidLidInternetAccountName 0x9225001f +// #define PidLidInternetAccountStamp 0x9226001f +// #define PidLidUseTnef 0x9227000b +// #define PidLidContactLinkName 0x9229001f +// #define PidLidContactLinkEntry 0x922a0102 +// #define PidLidContactLinkSearchKey 0x922b0102 +// #define PidLidSpamOriginalFolder 0x92370102 +// #define PidLidTaskUpdates 0x9345000b +// #define PidLidTaskStatusOnComplete 0x9346000b +// #define PidLidTaskLastDelegate 0x9347001f +// #define PidLidTaskAssigners 0x934a0102 +// #define PidLidTaskFCreator 0x934c000b +// #define PidLidImapDeleted 0x94e50003 +// #define PidLidHeaderItem 0x94e60003 + + default: + rc = [super getProperty: data withTag: propTag]; + } + + return rc; +} + +- (void) save +{ + iCalCalendar *vCalendar; + iCalToDo *vToDo; + id value; + SOGoUserDefaults *ud; + iCalTimeZone *tz; + iCalDateTime *date; + NSString *owner, *status, *priority; + NSCalendarDate *now; + + owner = [sogoObject ownerInContext: nil]; + vToDo = [sogoObject component: YES secure: NO]; + vCalendar = [vToDo parent]; + [vCalendar setProdID: @"-//Inverse inc.//OpenChange+SOGo//EN"]; + + // summary + value = [newProperties + objectForKey: MAPIPropertyKey (PR_NORMALIZED_SUBJECT_UNICODE)]; + if (value) + [vToDo setSummary: value]; + + // comment + value = [newProperties + objectForKey: MAPIPropertyKey (PR_BODY_UNICODE)]; + if (value) + [vToDo setComment: value]; + + // Location + value = [newProperties objectForKey: MAPIPropertyKey (PidLidLocation)]; + if (value) + [vToDo setLocation: value]; + + /* created */ + value = [newProperties objectForKey: MAPIPropertyKey (PR_CREATION_TIME)]; + if (value) + [vToDo setCreated: value]; + + /* last-modified + dtstamp */ + value = [newProperties objectForKey: MAPIPropertyKey (PR_LAST_MODIFICATION_TIME)]; + if (value) + { + [vToDo setLastModified: value]; + [vToDo setTimeStampAsDate: value]; + } + + ud = [[SOGoUser userWithLogin: owner] userDefaults]; + tz = [iCalTimeZone timeZoneForName: [ud timeZoneName]]; + [vCalendar addTimeZone: tz]; + + // start + value = [newProperties objectForKey: MAPIPropertyKey (PidLidTaskStartDate)]; + if (value) + { + date = (iCalDateTime *) [vToDo uniqueChildWithTag: @"dtstart"]; + [date setTimeZone: tz]; + [date setDateTime: value]; + } + + // due + value = [newProperties objectForKey: MAPIPropertyKey (PidLidTaskDueDate)]; + if (value) + { + date = (iCalDateTime *) [vToDo uniqueChildWithTag: @"due"]; + [date setTimeZone: tz]; + [date setDateTime: value]; + } + + // status + value = [newProperties objectForKey: MAPIPropertyKey (PidLidTaskStatus)]; + if (value) + { + switch ([value intValue]) + { + case 1: status = @"IN-PROCESS"; break; + case 2: status = @"COMPLETED"; break; + default: status = @"NEEDS-ACTION"; + } + [vToDo setStatus: status]; + } + + // priority + value = [newProperties objectForKey: MAPIPropertyKey (PR_IMPORTANCE)]; + if (value) + { + switch ([value intValue]) + { + case 0: // IMPORTANCE_LOW + priority = @"9"; + break; + case 2: // IMPORTANCE_HIGH + priority = @"1"; + break; + default: // IMPORTANCE_NORMAL + priority = @"5"; + } + } + else + priority = @"0"; // None + [vToDo setPriority: priority]; + + now = [NSCalendarDate date]; + if ([sogoObject isNew]) + { + [vToDo setCreated: now]; + } + [vToDo setTimeStampAsDate: now]; + + [sogoObject saveContentString: [vCalendar versitString]]; +} + +@end diff --git a/OpenChange/SOGoAppointmentObject+MAPIStore.h b/OpenChange/SOGoAppointmentObject+MAPIStore.h deleted file mode 100644 index f44dd90f2..000000000 --- a/OpenChange/SOGoAppointmentObject+MAPIStore.h +++ /dev/null @@ -1,36 +0,0 @@ -/* SOGoAppointmentObject+MAPIStore.h - this file is part of SOGo - * - * Copyright (C) 2010 Inverse inc. - * - * Author: Wolfgang Sourdeau - * - * 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. - */ - -#ifndef SOGOAPPOINTMENTOBJECT_MAPISTORE_H -#define SOGOAPPOINTMENTOBJECT_MAPISTORE_H - -#import - -@class NSDictionary; - -@interface SOGoAppointmentObject (MAPIStoreMessage) - -- (void) setMAPIProperties: (NSDictionary *) properties; - -@end - -#endif /* SOGOAPPOINTMENTOBJECT_MAPISTORE_H */ diff --git a/OpenChange/SOGoAppointmentObject+MAPIStore.m b/OpenChange/SOGoAppointmentObject+MAPIStore.m deleted file mode 100644 index 236432f2a..000000000 --- a/OpenChange/SOGoAppointmentObject+MAPIStore.m +++ /dev/null @@ -1,213 +0,0 @@ -/* SOGoAppointmentObject+MAPIStore.m - this file is part of SOGo - * - * Copyright (C) 2010 Inverse inc. - * - * Author: Wolfgang Sourdeau - * - * 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. - */ - -#include -#include -#include - -#import -#import -#import -#import - -#import -#import - -#import -#import -#import -#import -#import - -#import -#import -#import - -#import - -#import "MAPIStoreTypes.h" - -#import "SOGoAppointmentObject+MAPIStore.h" - - -@implementation SOGoAppointmentObject (MAPIStoreMessage) - -/* TODO: merge with tasks */ -- (void) setMAPIProperties: (NSDictionary *) properties -{ - iCalCalendar *vCalendar; - iCalEvent *vEvent; - id value; - SOGoUserDefaults *ud; - NSCalendarDate *now; - iCalTimeZone *tz; - iCalDateTime *start, *end; - - [self logWithFormat: @"event props:"]; - MAPIStoreDumpMessageProperties (properties); - -#if 1 - // We *ALWAYS* use the MAPIContent - as we do NOT want - // to modify the real object since it'll confuse the - // SOGo calendaring scheduling code. - if (![self MAPIContent]) - { - vEvent = [self component: YES secure: NO]; - vCalendar = [vEvent parent]; - [vCalendar setProdID: @"-//Inverse inc.//OpenChange+SOGo//EN"]; - - [self setMAPIContent: [vCalendar versitString]]; - } - - // FIXME: this will NOT work with recurring events - vCalendar = [iCalCalendar parseSingleFromSource: [self MAPIContent]]; - vEvent = [[vCalendar events] objectAtIndex: 0]; -#else - vEvent = [self component: YES secure: NO]; - vCalendar = [vEvent parent]; -#endif - - // summary - value = [properties - objectForKey: MAPIPropertyKey (PR_NORMALIZED_SUBJECT_UNICODE)]; - if (value) - [vEvent setSummary: value]; - - // Location - value = [properties objectForKey: MAPIPropertyKey (PidLidLocation)]; - if (value) - [vEvent setLocation: value]; - - ud = [[SOGoUser userWithLogin: owner] userDefaults]; - tz = [iCalTimeZone timeZoneForName: [ud timeZoneName]]; - [vCalendar addTimeZone: tz]; - - // start - value = [properties objectForKey: MAPIPropertyKey (PR_START_DATE)]; - if (!value) - value = [properties objectForKey: MAPIPropertyKey (PidLidAppointmentStartWhole)]; - if (value) - { - start = (iCalDateTime *) [vEvent uniqueChildWithTag: @"dtstart"]; - [start setTimeZone: tz]; - [start setDateTime: value]; - } - - // end - value = [properties objectForKey: MAPIPropertyKey (PR_END_DATE)]; - if (!value) - value = [properties objectForKey: MAPIPropertyKey (PidLidAppointmentEndWhole)]; - if (value) - { - end = (iCalDateTime *) [vEvent uniqueChildWithTag: @"dtend"]; - [end setTimeZone: tz]; - [end setDateTime: value]; - } - - now = [NSCalendarDate date]; - if ([self isNew]) - { - [vEvent setCreated: now]; - } - [vEvent setTimeStampAsDate: now]; - - // Organizer and attendees - value = [properties objectForKey: @"recipients"]; - - if (value) - { - NSArray *recipients; - NSDictionary *dict; - iCalPerson *person; - int i; - - - dict = [[context activeUser] primaryIdentity]; - person = [iCalPerson new]; - [person setCn: [dict objectForKey: @"fullName"]]; - [person setEmail: [dict objectForKey: @"email"]]; - [vEvent setOrganizer: person]; - [person release]; - - recipients = [value objectForKey: @"to"]; - - for (i = 0; i < [recipients count]; i++) - { - dict = [recipients objectAtIndex: i]; - person = [iCalPerson new]; - - [person setCn: [dict objectForKey: @"fullName"]]; - [person setEmail: [dict objectForKey: @"email"]]; - [person setParticipationStatus: iCalPersonPartStatNeedsAction]; - [person setRsvp: @"TRUE"]; - [person setRole: @"REQ-PARTICIPANT"]; - - // FIXME: We must NOT always rely on this - if (![dict objectForKey: @"x500dn"] - && ![vEvent isAttendee: [person rfc822Email]]) - [vEvent addToAttendees: person]; - - [person release]; - } - } - - - // MAPIStoreDumpMessageProperties (properties); - -#if 1 - // We set the new MAPI content, but we overwrite -MAPISave and reuse the - // MAPI content in order to trigger SOGo's calendar scheduling code. - [self setMAPIContent: [vCalendar versitString]]; -#else - ASSIGN(content, [vCalendar versitString]); - - [fullCalendar release]; - fullCalendar = nil; - [safeCalendar release]; - safeCalendar = nil; -#endif -} - -#if 1 -- (void) MAPISave -{ - iCalCalendar *calendar; - iCalEvent *event; - - calendar = [iCalCalendar parseSingleFromSource: [self MAPIContent]]; - event = [[calendar events] objectAtIndex: 0]; - - [self logWithFormat: @"-MAPISave in SOGoAppointmentObject+MAPIStore"]; - - [self saveComponent: event]; - - /* ideally, this should be performed from saveComponent: */ - [safeCalendar release]; - safeCalendar = nil; - [fullCalendar release]; - fullCalendar = nil; - [originalCalendar release]; - originalCalendar = nil; -} -#endif - -@end diff --git a/OpenChange/SOGoContactGCSEntry+MAPIStore.h b/OpenChange/SOGoContactGCSEntry+MAPIStore.h deleted file mode 100644 index 54820ba2a..000000000 --- a/OpenChange/SOGoContactGCSEntry+MAPIStore.h +++ /dev/null @@ -1,36 +0,0 @@ -/* SOGoContactGCSEntry+MAPIStore.h - this file is part of SOGo - * - * Copyright (C) 2010 Inverse inc. - * - * Author: Wolfgang Sourdeau - * - * 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. - */ - -#ifndef SOGOCONTACTGCSENTRY_MAPISTORE_H -#define SOGOCONTACTGCSENTRY_MAPISTORE_H - -#import - -@class NSDictionary; - -@interface SOGoContactGCSEntry (MAPIStoreMessage) - -- (void) setMAPIProperties: (NSDictionary *) properties; - -@end - -#endif /* SOGOCONTACTGCSENTRY_MAPISTORE_H */ diff --git a/OpenChange/SOGoContactGCSEntry+MAPIStore.m b/OpenChange/SOGoContactGCSEntry+MAPIStore.m deleted file mode 100644 index be576ace7..000000000 --- a/OpenChange/SOGoContactGCSEntry+MAPIStore.m +++ /dev/null @@ -1,151 +0,0 @@ -/* SOGoContactGCSEntry+MAPIStore.m - this file is part of SOGo - * - * Copyright (C) 2010 Inverse inc. - * - * Author: Wolfgang Sourdeau - * - * 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 -#import -#import -#import - -#import -#import - -#include - -#import "MAPIStoreTypes.h" - -#import "SOGoContactGCSEntry+MAPIStore.h" - -@implementation SOGoContactGCSEntry (MAPIStoreMessage) - -- (void) setMAPIProperties: (NSDictionary *) properties -{ - NGVCard *newCard; - NSArray *elements; - CardElement *element; - int postalAddressId; - id value; - - [self logWithFormat: @"setMAPIProperties: %@", properties]; - - newCard = [self vCard]; - [newCard setTag: @"vcard"]; - [newCard setVersion: @"3.0"]; - [newCard setProdID: @"-//Inverse inc.//OpenChange+SOGo//EN"]; - [newCard setProfile: @"vCard"]; - - value = [properties - objectForKey: MAPIPropertyKey (PR_DISPLAY_NAME_UNICODE)]; - if (value) - [newCard setFn: value]; - - elements = [newCard childrenWithTag: @"email"]; - value = [properties objectForKey: MAPIPropertyKey (PidLidEmail1EmailAddress)]; - if (value) - { - if ([elements count] > 0) - [[elements objectAtIndex: 0] setValue: 0 to: value]; - else - [newCard addEmail: value - types: [NSArray arrayWithObject: @"pref"]]; - } - value = [properties objectForKey: MAPIPropertyKey (PidLidEmail2EmailAddress)]; - if (value) - { - if ([elements count] > 1) - [[elements objectAtIndex: 1] setValue: 0 to: value]; - else - [newCard addEmail: value types: nil]; - } - value = [properties objectForKey: MAPIPropertyKey (PidLidEmail3EmailAddress)]; - if (value) - { - if ([elements count] > 2) - [[elements objectAtIndex: 2] setValue: 0 to: value]; - else - [newCard addEmail: value types: nil]; - } - - postalAddressId = [[properties objectForKey: MAPIPropertyKey (PidLidPostalAddressId)] - intValue]; - - /* Work address */ - value = [properties objectForKey: MAPIPropertyKey (PidLidWorkAddress)]; - if ([value length]) - { - elements = [newCard childrenWithTag: @"label" - andAttribute: @"type" - havingValue: @"work"]; - if ([elements count] > 0) - element = [elements objectAtIndex: 0]; - else - { - element = [CardElement elementWithTag: @"label"]; - [element addAttribute: @"type" value: @"work"]; - [card addChild: element]; - } - if (postalAddressId == 2) - { - [element removeValue: @"pref" - fromAttribute: @"type"]; - [element addAttribute: @"type" - value: @"pref"]; - } - [element setValue: 0 to: value]; - } - - elements = [newCard childrenWithTag: @"adr" - andAttribute: @"type" - havingValue: @"work"]; - if ([elements count] > 0) - element = [elements objectAtIndex: 0]; - else - { - element = [CardElement elementWithTag: @"adr"]; - [element addAttribute: @"type" value: @"work"]; - [card addChild: element]; - } - if (postalAddressId == 2) - [element addAttribute: @"type" - value: @"pref"]; - value = [properties objectForKey: MAPIPropertyKey (PidLidWorkAddressPostOfficeBox)]; - if (value) - [element setValue: 0 to: value]; - value = [properties objectForKey: MAPIPropertyKey (PidLidWorkAddressStreet)]; - if (value) - [element setValue: 2 to: value]; - value = [properties objectForKey: MAPIPropertyKey (PidLidWorkAddressCity)]; - if (value) - [element setValue: 3 to: value]; - value = [properties objectForKey: MAPIPropertyKey (PidLidWorkAddressState)]; - if (value) - [element setValue: 4 to: value]; - value = [properties objectForKey: MAPIPropertyKey (PidLidWorkAddressPostalCode)]; - if (value) - [element setValue: 5 to: value]; - value = [properties objectForKey: MAPIPropertyKey (PidLidWorkAddressCountry)]; - if (value) - [element setValue: 6 to: value]; - - ASSIGN (content, [newCard versitString]); -} - -@end diff --git a/OpenChange/SOGoDraftObject+MAPIStore.m b/OpenChange/SOGoDraftObject+MAPIStore.m deleted file mode 100644 index e541149c1..000000000 --- a/OpenChange/SOGoDraftObject+MAPIStore.m +++ /dev/null @@ -1,123 +0,0 @@ -/* SOGoDraftObject+MAPIStore.m - this file is part of SOGo - * - * Copyright (C) 2010 Inverse inc. - * - * Author: Wolfgang Sourdeau - * - * 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 -#import -#import - -#import - -#import - -#import -#import -#import - -#import "MAPIStoreTypes.h" - -#import "SOGoDraftObject+MAPIStore.h" - -@implementation SOGoDraftObject (MAPIStoreMessage) - -- (void) setMAPIProperties: (NSDictionary *) properties -{ - static NSString *recIds[] = { @"to", @"cc", @"bcc" }; - NSArray *list; - NSDictionary *recipients, *identity; - NSMutableDictionary *newHeaders; - NSString *recId, *body; - NSUInteger count; - id value; - - newHeaders = [NSMutableDictionary dictionaryWithCapacity: 6]; - 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) */ - - value = [properties - objectForKey: MAPIPropertyKey (PR_NORMALIZED_SUBJECT_UNICODE)]; - if (!value) - value = [properties objectForKey: MAPIPropertyKey (PR_SUBJECT_UNICODE)]; - if (value) - [newHeaders setObject: value forKey: @"subject"]; - - identity = [[context activeUser] primaryIdentity]; - [newHeaders setObject: [identity keysWithFormat: @"%{fullName} <%{email}>"] - forKey: @"from"]; - [self setHeaders: newHeaders]; - - value = [properties objectForKey: MAPIPropertyKey (PR_HTML)]; - if (value) - { - [self setIsHTML: YES]; - // TODO: encoding - body = [[NSString alloc] initWithData: value - encoding: NSUTF8StringEncoding]; - [self setText: body]; - [body release]; - } - else - { - value = [properties objectForKey: MAPIPropertyKey (PR_BODY_UNICODE)]; - if (value) - { - [self setIsHTML: NO]; - [self setText: value]; - } - } -} - -- (void) MAPISubmit -{ - [self logWithFormat: @"sending message"]; - [self sendMail]; -} - -- (void) MAPISave -{ - [self logWithFormat: @"saving message"]; - [self save]; -} - -@end diff --git a/OpenChange/SOGoTaskObject+MAPIStore.h b/OpenChange/SOGoTaskObject+MAPIStore.h deleted file mode 100644 index 0a99342e7..000000000 --- a/OpenChange/SOGoTaskObject+MAPIStore.h +++ /dev/null @@ -1,36 +0,0 @@ -/* SOGoTaskObject+MAPIStore.h - this file is part of SOGo - * - * Copyright (C) 2010 Inverse inc. - * - * Author: Wolfgang Sourdeau - * - * 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. - */ - -#ifndef SOGOTASKOBJECT_MAPISTORE_H -#define SOGOTASKOBJECT_MAPISTORE_H - -#import - -@class NSDictionary; - -@interface SOGoTaskObject (MAPIStoreMessage) - -- (void) setMAPIProperties: (NSDictionary *) properties; - -@end - -#endif /* SOGOTASKOBJECT_MAPISTORE_H */ diff --git a/OpenChange/SOGoTaskObject+MAPIStore.m b/OpenChange/SOGoTaskObject+MAPIStore.m deleted file mode 100644 index 6930ca738..000000000 --- a/OpenChange/SOGoTaskObject+MAPIStore.m +++ /dev/null @@ -1,175 +0,0 @@ -/* SOGoTaskObject+MAPIStore.m - this file is part of SOGo - * - * Copyright (C) 2010 Inverse inc. - * - * Author: Wolfgang Sourdeau - * - * 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. - */ - -#include -#include -#include - -#import -#import -#import - -#import - -#import -#import -#import -#import - -#import -#import - -#import "MAPIStoreTypes.h" - -#import "SOGoTaskObject+MAPIStore.h" - -@implementation SOGoTaskObject (MAPIStoreMessage) - -/* TODO: merge with events */ -- (void) setMAPIProperties: (NSDictionary *) properties -{ - // MAPIStoreDumpMessageProperties (properties); - - iCalCalendar *vCalendar; - iCalToDo *vToDo; - id value; - SOGoUserDefaults *ud; - iCalTimeZone *tz; - iCalDateTime *date; - NSString *status, *priority; - NSCalendarDate *now; - - vToDo = [self component: YES secure: NO]; - vCalendar = [vToDo parent]; - [vCalendar setProdID: @"-//Inverse inc.//OpenChange+SOGo//EN"]; - - // summary - value = [properties - objectForKey: MAPIPropertyKey (PR_NORMALIZED_SUBJECT_UNICODE)]; - if (value) - [vToDo setSummary: value]; - - // comment - value = [properties - objectForKey: MAPIPropertyKey (PR_BODY_UNICODE)]; - if (value) - [vToDo setComment: value]; - - // Location - value = [properties objectForKey: MAPIPropertyKey (PidLidLocation)]; - if (value) - [vToDo setLocation: value]; - - /* created */ - value = [properties objectForKey: MAPIPropertyKey (PR_CREATION_TIME)]; - if (value) - [vToDo setCreated: value]; - - /* last-modified + dtstamp */ - value = [properties objectForKey: MAPIPropertyKey (PR_LAST_MODIFICATION_TIME)]; - if (value) - { - [vToDo setLastModified: value]; - [vToDo setTimeStampAsDate: value]; - } - - ud = [[SOGoUser userWithLogin: owner] userDefaults]; - tz = [iCalTimeZone timeZoneForName: [ud timeZoneName]]; - [vCalendar addTimeZone: tz]; - - // start - value = [properties objectForKey: MAPIPropertyKey (PidLidTaskStartDate)]; - if (value) - { - date = (iCalDateTime *) [vToDo uniqueChildWithTag: @"dtstart"]; - [date setTimeZone: tz]; - [date setDateTime: value]; - } - - // due - value = [properties objectForKey: MAPIPropertyKey (PidLidTaskDueDate)]; - if (value) - { - date = (iCalDateTime *) [vToDo uniqueChildWithTag: @"due"]; - [date setTimeZone: tz]; - [date setDateTime: value]; - } - - // status - value = [properties objectForKey: MAPIPropertyKey (PidLidTaskStatus)]; - if (value) - { - switch ([value intValue]) - { - case 1: status = @"IN-PROCESS"; break; - case 2: status = @"COMPLETED"; break; - default: status = @"NEEDS-ACTION"; - } - [vToDo setStatus: status]; - } - - // priority - value = [properties objectForKey: MAPIPropertyKey (PR_IMPORTANCE)]; - if (value) - { - switch ([value intValue]) - { - case 0: // IMPORTANCE_LOW - priority = @"9"; - break; - case 2: // IMPORTANCE_HIGH - priority = @"1"; - break; - default: // IMPORTANCE_NORMAL - priority = @"5"; - } - } - else - priority = @"0"; // None - [vToDo setPriority: priority]; - - now = [NSCalendarDate date]; - if ([self isNew]) - { - [vToDo setCreated: now]; - } - [vToDo setTimeStampAsDate: now]; - - // due - // value = [properties objectForKey: MAPIPropertyKey (PR_DUE_DATE)]; - // if (value) - // { - // due = (iCalDateTime *) [vToDo uniqueChildWithTag: @"due"]; - // [due setTimeZone: tz]; - // [due setDateTime: value]; - // } - - // MAPIStoreDumpMessageProperties (properties); - ASSIGN (content, [vCalendar versitString]); - - [fullCalendar release]; - fullCalendar = nil; - [safeCalendar release]; - safeCalendar = nil; -} - -@end