diff --git a/ChangeLog b/ChangeLog index ed251855e..d705117c8 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,22 @@ +2010-11-24 Wolfgang Sourdeau + + * OpenChange/SOGoDraftObject+MAPIStore.m: new category module. + (-setMAPIProperties,-MAPISubmit,-MAPISave): implemented the MAPI + methods for sending messages. + + * OpenChange/MAPIStoreSOGo.m: (sogo_op_modifyrecipients): new + backend operation. + + * OpenChange/MAPIStoreContext.m: + (-modifyRecipientsWithMID:inRows:withCount:): new method + implementing the new "modifyrecipients" operation. + + * OpenChange/MAPIStoreOutboxContext.m: new class module + implementing the "outbox" folder type, as a subclass of + MAPIStoreMailContext. + (-createMessageInFolder:): new method that returns an instance of + SOGoDraftObject. + 2010-11-23 Francis Lachapelle * UI/WebServerResources/SchedulerUI.js (deletePersonalCalendar): diff --git a/GNUmakefile b/GNUmakefile index ba362d698..af8be3570 100644 --- a/GNUmakefile +++ b/GNUmakefile @@ -6,8 +6,8 @@ include $(GNUSTEP_MAKEFILES)/common.make SUBPROJECTS = \ SOPE/NGCards \ SOPE/GDLContentStore \ - SoObjects \ OGoContentStore \ + SoObjects \ Main \ UI \ Tools diff --git a/OGoContentStore/GNUmakefile.preamble b/OGoContentStore/GNUmakefile.preamble index 01bd0b897..7a9042c3d 100644 --- a/OGoContentStore/GNUmakefile.preamble +++ b/OGoContentStore/GNUmakefile.preamble @@ -6,8 +6,7 @@ libOGoContentStore_LIBRARIES_DEPEND_UPON += \ -lNGCards \ -lNGExtensions \ -lEOControl \ - -lSaxObjC \ - -lSOGo + -lSaxObjC ADDITIONAL_INCLUDE_DIRS += -I. -I.. -I../SOPE -I../SoObjects diff --git a/OpenChange/GNUmakefile b/OpenChange/GNUmakefile index af5db8ec5..5056a83a7 100644 --- a/OpenChange/GNUmakefile +++ b/OpenChange/GNUmakefile @@ -35,12 +35,14 @@ $(SOGOBACKEND)_OBJC_FILES += \ MAPIStoreContactsContext.m \ MAPIStoreFreebusyContext.m \ MAPIStoreMailContext.m \ + MAPIStoreOutboxContext.m \ MAPIStoreTasksContext.m \ \ SOGoAppointmentObject+MAPIStore.m \ - SOGoGCSFolder+MAPIStore.m \ SOGoContentObject+MAPIStore.m \ SOGoContactGCSEntry+MAPIStore.m \ + SOGoDraftObject+MAPIStore.m \ + SOGoGCSFolder+MAPIStore.m \ SOGoTaskObject+MAPIStore.m \ \ NSArray+MAPIStore.m \ diff --git a/OpenChange/MAPIStoreContext.h b/OpenChange/MAPIStoreContext.h index 0171c4a69..db4272e0a 100644 --- a/OpenChange/MAPIStoreContext.h +++ b/OpenChange/MAPIStoreContext.h @@ -62,7 +62,7 @@ WOContext *woContext; NSMutableDictionary *messageCache; NSMutableDictionary *subfolderCache; - SOGoFolder *moduleFolder; + id moduleFolder; } + (id) contextFromURI: (const char *) newUri @@ -123,6 +123,9 @@ - (int) setPropertiesWithFMID: (uint64_t) fmid ofTableType: (uint8_t) tableType inRow: (struct SRow *) aRow; +- (int) modifyRecipientsWithMID: (uint64_t) mid + inRows: (struct ModifyRecipientRow *) rows + withCount: (NSUInteger) max; - (int) deleteMessageWithMID: (uint64_t) mid withFlags: (uint8_t) flags; diff --git a/OpenChange/MAPIStoreContext.m b/OpenChange/MAPIStoreContext.m index 65881f7f5..430f9013b 100644 --- a/OpenChange/MAPIStoreContext.m +++ b/OpenChange/MAPIStoreContext.m @@ -1198,28 +1198,14 @@ _prepareContextClass (struct mapistore_context *newMemCtx, return MAPISTORE_ERROR; } -- (int) setPropertiesOfMessage: (NSMutableDictionary *) message - fromRow: (struct SRow *) aRow -{ - int counter; - struct SPropValue *cValue; - - for (counter = 0; counter < aRow->cValues; counter++) - { - cValue = &(aRow->lpProps[counter]); - [message setObject: NSObjectFromSPropValue (cValue) - forKey: MAPIPropertyNumber (cValue->ulPropTag)]; - } - - return MAPISTORE_SUCCESS; -} - - (int) setPropertiesWithFMID: (uint64_t) fmid ofTableType: (uint8_t) tableType inRow: (struct SRow *) aRow { NSMutableDictionary *message; NSNumber *midNbr; + struct SPropValue *cValue; + NSUInteger counter; int rc; switch (tableType) @@ -1228,8 +1214,15 @@ _prepareContextClass (struct mapistore_context *newMemCtx, midNbr = [NSNumber numberWithUnsignedLongLong: fmid]; message = [messages objectForKey: midNbr]; if (message) - rc = [self setPropertiesOfMessage: message - fromRow: aRow]; + { + for (counter = 0; counter < aRow->cValues; counter++) + { + cValue = &(aRow->lpProps[counter]); + [message setObject: NSObjectFromSPropValue (cValue) + forKey: MAPIPropertyNumber (cValue->ulPropTag)]; + } + rc = MAPISTORE_SUCCESS; + } else rc = MAPISTORE_ERR_NOT_FOUND; break; @@ -1243,6 +1236,97 @@ _prepareContextClass (struct mapistore_context *newMemCtx, return rc; } +- (NSDictionary *) _convertRecipientFromRow: (struct RecipientRow *) row +{ + NSMutableDictionary *recipient; + NSString *value; + + recipient = [NSMutableDictionary dictionaryWithCapacity: 5]; + + if ((row->RecipientFlags & 0x07) == 1) + { + value = [NSString stringWithUTF8String: row->X500DN.recipient_x500name]; + [recipient setObject: value forKey: @"x500dn"]; + } + + switch ((row->RecipientFlags & 0x208)) + { + case 0x08: + // TODO: we cheat + value = [NSString stringWithUTF8String: row->EmailAddress.lpszA]; + break; + case 0x208: + value = [NSString stringWithUTF8String: row->EmailAddress.lpszW]; + break; + default: + value = nil; + } + if (value) + [recipient setObject: value forKey: @"email"]; + + switch ((row->RecipientFlags & 0x210)) + { + case 0x10: + // TODO: we cheat + value = [NSString stringWithUTF8String: row->DisplayName.lpszA]; + break; + case 0x210: + value = [NSString stringWithUTF8String: row->DisplayName.lpszW]; + break; + default: + value = nil; + } + if (value) + [recipient setObject: value forKey: @"fullName"]; + + return recipient; +} + +- (int) modifyRecipientsWithMID: (uint64_t) mid + inRows: (struct ModifyRecipientRow *) rows + withCount: (NSUInteger) max +{ + static NSString *recTypes[] = { @"orig", @"to", @"cc", @"bcc" }; + NSMutableDictionary *message, *recipients; + NSMutableArray *list; + NSString *recType; + struct ModifyRecipientRow *currentRow; + NSUInteger count; + int rc; + + message = [messages + objectForKey: [NSNumber numberWithUnsignedLongLong: mid]]; + if (message) + { + recipients = [NSMutableDictionary new]; + [message setObject: recipients forKey: @"recipients"]; + [recipients release]; + for (count = 0; count < max; count++) + { + currentRow = rows + count; + if (currentRow->RecipClass >= 0 + && currentRow->RecipClass < 3) + { + recType = recTypes[currentRow->RecipClass]; + list = [recipients objectForKey: recType]; + if (!list) + { + list = [NSMutableArray new]; + [recipients setObject: list forKey: recType]; + [list release]; + } + [list addObject: [self _convertRecipientFromRow: + &(currentRow->RecipientRow)]]; + } + } + rc = MAPISTORE_SUCCESS; + } + else + rc = MAPISTORE_ERR_NOT_FOUND; + + return rc; +} + - (int) deleteMessageWithMID: (uint64_t) mid withFlags: (uint8_t) flags { diff --git a/OpenChange/MAPIStoreOutboxContext.h b/OpenChange/MAPIStoreOutboxContext.h new file mode 100644 index 000000000..d103ba40b --- /dev/null +++ b/OpenChange/MAPIStoreOutboxContext.h @@ -0,0 +1,32 @@ +/* MAPIStoreOutboxContext.h - this file is part of SOGo + * + * Copyright (C) 2010 Wolfgang Sourdeau + * + * 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 MAPISTOREOUTBOXCONTEXT_H +#define MAPISTOREOUTBOXCONTEXT_H + +#import "MAPIStoreMailContext.h" + +@interface MAPIStoreOutboxContext : MAPIStoreMailContext + +@end + +#endif /* MAPISTOREOUTBOXCONTEXT_H */ diff --git a/OpenChange/MAPIStoreOutboxContext.m b/OpenChange/MAPIStoreOutboxContext.m new file mode 100644 index 000000000..0f7367a33 --- /dev/null +++ b/OpenChange/MAPIStoreOutboxContext.m @@ -0,0 +1,84 @@ +/* MAPIStoreOutboxContext.m - this file is part of SOGo + * + * Copyright (C) 2010 Wolfgang Sourdeau + * + * 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 "MAPIApplication.h" +#import "MAPIStoreAuthenticator.h" +#import "MAPIStoreMapping.h" +#import "MAPIStoreTypes.h" + +#import "MAPIStoreOutboxContext.h" + +@implementation MAPIStoreOutboxContext + ++ (NSString *) MAPIModuleName +{ + return @"outbox"; +} + ++ (void) registerFixedMappings: (MAPIStoreMapping *) mapping +{ + [mapping registerURL: @"sogo://openchange:openchange@outbox/" + withID: 0x150001]; +} + +- (void) setupModuleFolder +{ + SOGoUserFolder *userFolder; + SOGoMailAccounts *accountsFolder; + SOGoMailAccount *accountFolder; + + userFolder = [SOGoUserFolder objectWithName: [authenticator username] + inContainer: MAPIApp]; + [woContext setClientObject: userFolder]; + [userFolder retain]; // LEAK + + accountsFolder = [userFolder lookupName: @"Mail" + inContext: woContext + acquire: NO]; + [woContext setClientObject: accountsFolder]; + [accountsFolder retain]; // LEAK + + accountFolder = [accountsFolder lookupName: @"0" + inContext: woContext + acquire: NO]; + [accountFolder retain]; // LEAK + + moduleFolder = [accountFolder draftsFolderInContext: nil]; + [moduleFolder retain]; +} + +- (id) createMessageInFolder: (id) parentFolder +{ + return [moduleFolder newDraft]; +} + +@end diff --git a/OpenChange/MAPIStoreSOGo.m b/OpenChange/MAPIStoreSOGo.m index 47cc4733c..afa6b0038 100644 --- a/OpenChange/MAPIStoreSOGo.m +++ b/OpenChange/MAPIStoreSOGo.m @@ -551,7 +551,9 @@ static int sogo_op_getprops(void *private_data, context = cContext->objcContext; [context setupRequest]; - rc = [context getProperties: SPropTagArray ofTableType: type inRow: aRow withMID: fmid]; + rc = [context getProperties: SPropTagArray + ofTableType: type + inRow: aRow withMID: fmid]; [context tearDownRequest]; [pool release]; @@ -586,6 +588,34 @@ static int sogo_op_setprops(void *private_data, return rc; } +static int sogo_op_modifyrecipients(void *private_data, + uint64_t mid, + struct ModifyRecipientRow *rows, + uint16_t count) +{ + NSAutoreleasePool *pool; + sogo_context *cContext; + MAPIStoreContext *context; + int rc; + + DEBUG (5, ("[SOGo: %s:%d]\n", __FUNCTION__, __LINE__)); + + pool = [NSAutoreleasePool new]; + + cContext = private_data; + context = cContext->objcContext; + [context setupRequest]; + + rc = [context modifyRecipientsWithMID: mid + inRows: rows + withCount: count]; + + [context tearDownRequest]; + [pool release]; + + return rc; +} + static int sogo_op_deletemessage(void *private_data, uint64_t mid, uint8_t flags) @@ -680,6 +710,7 @@ int mapistore_init_backend(void) backend.op_getprops = sogo_op_getprops; backend.op_get_fid_by_name = sogo_op_get_fid_by_name; backend.op_setprops = sogo_op_setprops; + backend.op_modifyrecipients = sogo_op_modifyrecipients; backend.op_deletemessage = sogo_op_deletemessage; backend.op_get_folders_list = sogo_op_get_folders_list; diff --git a/OpenChange/MAPIStoreTypes.m b/OpenChange/MAPIStoreTypes.m index 1e5161746..af786b878 100644 --- a/OpenChange/MAPIStoreTypes.m +++ b/OpenChange/MAPIStoreTypes.m @@ -122,7 +122,6 @@ NSObjectFromSPropValue (const struct SPropValue *value) // #define PT_SVREID 0xFB // #define PT_SRESTRICT 0xFD // #define PT_ACTIONS 0xFE -// #define PT_BINARY 0x102 result = [NSNull null]; NSLog (@"object type not handled: %d (0x.4x)", valueType, valueType); } @@ -143,6 +142,11 @@ MAPIStoreDumpMessageProperties (NSDictionary *properties) max = [allKeys count]; NSLog (@"message properties (%d):", max); + + value = [properties objectForKey: @"recipients"]; + if (value) + NSLog (@" recipients: %@", value); + for (count = 0; count < max; count++) { key = [allKeys objectAtIndex: count]; diff --git a/OpenChange/SOGoContentObject+MAPIStore.h b/OpenChange/SOGoContentObject+MAPIStore.h index d6a1d34c4..e8d4d6c14 100644 --- a/OpenChange/SOGoContentObject+MAPIStore.h +++ b/OpenChange/SOGoContentObject+MAPIStore.h @@ -31,5 +31,4 @@ @end - #endif /* SOGOCONTENTOBJECT_MAPISTORE_H */ diff --git a/OpenChange/SOGoDraftObject+MAPIStore.h b/OpenChange/SOGoDraftObject+MAPIStore.h new file mode 100644 index 000000000..49285b25a --- /dev/null +++ b/OpenChange/SOGoDraftObject+MAPIStore.h @@ -0,0 +1,36 @@ +/* SOGoDraftObject+MAPIStore.h - this file is part of SOGo + * + * Copyright (C) 2010 Wolfgang Sourdeau + * + * 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 SOGODRAFTOBJECT_MAPISTORE_H +#define SOGODRAFTOBJECT_MAPISTORE_H + +#import + +@interface SOGoDraftObject (MAPIStoreMessage) + +- (void) setMAPIProperties: (NSDictionary *) properties; + +- (void) MAPISubmit; + +@end + +#endif /* SOGODRAFTOBJECT_MAPISTORE_H */ diff --git a/OpenChange/SOGoDraftObject+MAPIStore.m b/OpenChange/SOGoDraftObject+MAPIStore.m new file mode 100644 index 000000000..451e43071 --- /dev/null +++ b/OpenChange/SOGoDraftObject+MAPIStore.m @@ -0,0 +1,113 @@ +/* SOGoDraftObject+MAPIStore.m - this file is part of SOGo + * + * Copyright (C) 2010 Wolfgang Sourdeau + * + * 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 "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; + + // MAPIStoreDumpMessageProperties (properties); + + newHeaders = [NSMutableDictionary dictionaryWithCapacity: 6]; + recipients = [properties objectForKey: @"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]; + } + + /* + 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: MAPIPropertyNumber (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: MAPIPropertyNumber (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: MAPIPropertyNumber (PR_BODY_UNICODE)]; + if (value) + { + [self setIsHTML: NO]; + [self setText: value]; + } + } +} + +- (void) MAPISubmit +{ + [self sendMail]; +} + +- (void) MAPISave +{ + [self save]; +} + +@end