+2007-08-16 Wolfgang Sourdeau <wsourdeau@inverse.ca>
+
+ * UI/MailerUI/UIxMailEditor.m ([-patchFlagsInStore]): removed
+ useless stub method, of which the intention was implemented in
+ SOGoDraftObject.
+ ([-lookupSentFolderUsingAccount]): removed obsolete method.
+ ([-selectedMailIdentity]): removed obsolete method.
+ ([-lookupSentFolderUsingFrom]): removed obsolete method.
+ ([-storeMailInSentFolder:_path]): removed obsolete method, of
+ which the mechanism has been put in -[SOGoDraftObject sendMail]
+ method.
+ ([UIxMailEditor -_saveFormInfo], [UIxMailEditor -defaultAction])
+ ([UIxMailEditor -saveAction], [UIxMailEditor -sendAction]):
+ adapted algorithms to the new SOGoDraftObject methods.
+ ([-deleteAction]): removed method since local draft objects cannot
+ be removed by the user.
+
+ * UI/MailerUI/UIxMailFolderActions.m ([UIxMailFolderActions
+ -expungeAction]): new method replacing the one previously found in
+ UIxMailListView.
+ ([UIxMailFolderActions -createFolderAction])
+ ([UIxMailFolderActions -renameFolderAction])
+ ([UIxMailFolderActions -deleteFolderAction])
+ ([UIxMailFolderActions -emptyTrashAction])
+ ([UIxMailFolderActions -subscribeAction])
+ ([UIxMailFolderActions -unsubscribeAction]): error situations
+ should have http return code 500 instead of 403.
+
+ * UI/MailerUI/UIxMailAccountActions.m ([UIxMailAccountActions
+ -composeAction]): new method replacing the one previously in
+ UIxMailMainFrame.
+
+ * SoObjects/Mailer/SOGoMailObject.m ([SOGoMailObject
+ -imap4URLString]): removed overriden method (see below).
+
+ * SoObjects/Mailer/SOGoMailFolder.m ([SOGoMailFolder -aclUsers]):
+ cache the mailbox acl.
+ ([SOGoMailFolder -aclsForUser:uid]): cache the mailbox acl.
+ ([SOGoMailFolder -setRoles:rolesforUser:uid]): reset the mailbox
+ acl cache.
+ ([SOGoMailFolder -httpURLForAdvisoryToUser:uid]): modified to use
+ the new method of determining the users mail accounts.
+
+ * SoObjects/Mailer/SOGoMailBaseObject.m ([-imap4URLString]): no
+ longer adds a "/" at the end of the string (the default for
+ folders), therefore this will be overriden in SOGoMailFolder
+ rather than in SOGoMailObject.
+
+ * UI/MailerUI/UIxMailListView.m ([-expungeAction]): removed
+ method, moved into the new UIxMailActions module class.
+
+ * SoObjects/Mailer/SOGoDraftsFolder.m ([SOGoDraftsFolder
+ -newDraft]): new method that returns a new SOGoDraftObject
+ instance with a unique filename based on the current timestamp and
+ the "newDraft" prefix.
+ ([SOGoDraftsFolder
+ -lookupName:nameinContext:localContextacquire:acquire]): overriden
+ method by detecting local drafts with their "newDraft" prefix.
+ ([SOGoDraftsFolder -isInDraftsFolder]): returns YES.
+
+ * SoObjects/Mailer/SOGoDraftsFolder.[hm]: rewrote class module
+ from scratch by making it a subclass of SOGoMailFolder.
+
+ * UI/MailerUI/UIxMailReplyAction.m: removed obsolete class
+ module.
+
+ * UI/MailerUI/UIxMailForwardAction.m: removed obsolete class
+ module.
+
+ * UI/MailerUI/UIxMailEditorAction.[hm]: removed obsolete class
+ module.
+
+ * SoObjects/Mailer/SOGoDraftObject.m ([SOGoDraftObject -init]):
+ new method, initializing the new ivars: IMAP4ID, headers, text,
+ sourceURL and sourceFlag.
+ ([-spoolFileManager], [SOGoDraftObject -userSpoolFolderPath])
+ ([-_ensureUserSpoolFolderPath])
+ ([-saveMimeMessageToTemporaryFileWithHeaders:])
+ ([SOGoDraftObject -mimeMessageWithHeaders:_headers])
+ ([-deleteTemporaryMessageFile:], [-delete], [-content])
+ ([-GETAction:_ctx], [-DELETEAction:_ctx], [-fetchParts:])
+ ([-uid], [-flags], [-size], [-envelope]): removed methods.
+ ([SOGoDraftObject -storeInfo]): rewrote method to take the new
+ ivars into account.
+ ([SOGoDraftObject -setSourceURL:newSourceURL])
+ ([SOGoDraftObject -setSourceFlag:newSourceFlag]): new accessor
+ methods to store the url of the original message between accesses
+ so that, depending on the action taken (forward or reply), the
+ correct flag can be given.
+ ([SOGoDraftObject -setIMAP4ID:]): new accessor method that sets
+ the imap4 id that is returned for the message whenever it is saved
+ into the drafts folder.
+ ([SOGoDraftObject -save]): new method that takes in charge the
+ saving of the message in the IMAP drafts folder.
+ ([SOGoDraftObject -fetchMailForReplying:sourceMailtoAll:toAll]):
+ new method that fills the original data of the new message with
+ the reply content of the original message.
+ ([SOGoDraftObject -fetchMailForForwarding:sourceMail]): same as
+ above for message forwarding.
+ ([-spoolFileManager]): removed useless method (only returned the
+ default filemanager...)
+ ([SOGoDraftObject -mimeMessageAsData]): new method that returns
+ the resulting message as an NSData chunk.
+
+ * SoObjects/Mailer/SOGoMailObject+Draft.m: new extension module
+ containing a rewrite of the mail action methods found in
+ UI/Mailer/, that needed to be put in the SOGoMailObject class.
+ ([SOGoMailObject -subjectForReply]): new method that returns a
+ subject suitable for replies.
+ ([SOGoMailObject -contentForReply]): new method that returns the
+ textual content of an email, quoted for replying.
+ ([SOGoMailObject -filenameForForward]): new method that returns
+ the name of the filename that should contain the forwarded
+ message, based on its subject.
+ ([SOGoMailObject -subjectForForward]): explicit.
+
+2007-08-15 Wolfgang Sourdeau <wsourdeau@inverse.ca>
+
+ * UI/MailerUI/UIxMailMainFrame.m ([UIxMailMainFrame
+ -mailAccounts]): rewrote method to return the name of the mail
+ accounts now available with the -[SOGoUser mailAccounts] method.
+ ([UIxMailMainFrame -showLinkBanner]): removed method.
+ ([UIxMailMainFrame -bannerToolbarStyle]): removed method.
+ ([UIxMailMainFrame -bannerConsumeStyle]): removed method.
+ ([UIxMailMainFrame -rootURL]): removed method.
+ ([UIxMailMainFrame -userRootURL]): removed method.
+ ([UIxMailMainFrame -calendarRootURL]): removed method.
+ ([UIxMailMainFrame -contactsRootURL]): removed method.
+ ([UIxMailMainFrame -hasErrorText])
+ ([UIxMailMainFrame -errorText])
+ ([UIxMailMainFrame -errorAlertJavaScript]): removed methods.
+ ([-composeAction]): removed method. Now provided by
+ UIxMailAccountActions.
+ ([UIxMailMainFrame -setHideFolderTree:_flag]): removed method.
+ ([UIxMailMainFrame -hideFolderTree]): removed method.
+ ([UIxMailMainFrame -treeRootClassName]): removed method.
+ ([UIxMailMainFrame +initialize]): removed method.
+ SOGoMailTreeRootClass userdefaults will no longer have any effect.
+
+ * UI/Common/WODirectAction+SOGo.m ([WODirectAction
+ -redirectToLocation:newLocation]): new method that implements the
+ same functionality as WOComponent.
+
+ * UI/Common/WODirectAction+SOGo.[hm]: new class extension module.
+
+ * UI/MailerUI/UIxMailView.m ([UIxMailView -mailIsDraft]): new
+ method that returns whether the current mail is store in the
+ drafts folder hierarchy.
+
+ * SoObjects/SOGo/SOGoUser.m ([-fullEmail]): removed method.
+ ([-primaryEmail]): removed method.
+ ([SOGoUser -primaryIMAP4AccountString]): removed method.
+ ([SOGoUser -mailAccounts]): new method that returns an array
+ containing description dictionaries for all the user mail
+ accounts. Each account also contain the user's identities for that
+ account.
+ ([SOGoUser -allIdentities]): new utility method that returns all
+ the user identities on all accounts.
+ ([SOGoUser -primaryIdentity]): new method return the first
+ identity of the first account.
+
+ * SoObjects/Mailer/SOGoMailFolder.m ([SOGoMailFolder -httpURLForAdvisoryToUser:uid]):
+
+ * SoObjects/Mailer/SOGoMailAccount.m ([SOGoMailAccount
+ -isInDraftsFolder]): returns NO.
+ ([-preferredIdentity]): removed method, replaced with -[SOGoUser
+ primaryIdentity].
+ ([SOGoMailAccount -draftsFolderInContext:_ctx]): new method.
+
+ * SoObjects/Mailer/SOGoMailBaseObject.m ([SOGoMailBaseObject
+ -isInDraftsFolder]): new method that detects if self is contained
+ by the drafts folder by calling itself on the container object.
+
+ * SoObjects/Mailer/SOGoMailAccounts.m ([-fetchAllIdentities]):
+ removed method.
+ ([-fetchIdentitiesWithEmitterPermissions]): removed method.
+ ([SOGoMailAccounts -toManyRelationshipKeys]): rewrote method to
+ return the name of the mail accounts now available with the
+ -[SOGoUser mailAccounts] method.
+
+ * SoObjects/Mailer/SOGoUser+Mail.[hm]: removed useless class
+ extension module.
+
+ * SoObjects/Mailer/SOGoMailIdentity.[hm]: removed useless class
+ module.
+
+ * SoObjects/SOGo/NSArray+Utilities.m ([NSArray
+ -keysWithFormat:format]): method that forward the method of the
+ same name to each member of the array, considering they all are
+ instances of NSDictionary.
+ ([NSArray -objectsForKey:key]): same principle as above.
+ ([NSArray -flattenedArray]): new method that transforms an array
+ of arrays into a single array containing all the elements of the
+ subarrays.
+
+ * SoObjects/SOGo/NSDictionary+Utilities.m ([NSDictionary
+ -keysWithFormat:keyFormat]): new method inspired by the python
+ string formatting system and which replaces occurences of "%{key}"
+ by the corresponding keys.
+
+2007-08-13 Wolfgang Sourdeau <wsourdeau@inverse.ca>
+
+ * Main/SOGo.m ([SOGo -run]): check for channel-type specific
+ sql script before the generic one when initializing mandatory
+ system tables.
+
2007-08-09 Wolfgang Sourdeau <wsourdeau@inverse.ca>
* SoObjects/SOGo/NSString+Utilities.m ([NSString
tableURL: (NSString *) url
andType: (NSString *) tableType
{
- NSString *tableName, *descFile;
+ NSString *tableName, *descFile, *tableFile, *fileSuffix;
EOAdaptorChannel *tc;
NGBundleManager *bm;
NSBundle *bundle;
unsigned int length;
+ NSURL *channelURL;
bm = [NGBundleManager defaultBundleManager];
-
- tc = [cm acquireOpenChannelForURL: [NSURL URLWithString: url]];
+
+ channelURL = [NSURL URLWithString: url];
+ fileSuffix = [channelURL scheme];
+ tc = [cm acquireOpenChannelForURL: channelURL];
tableName = [url lastPathComponent];
if ([tc evaluateExpressionX:
{
bundle = [bm bundleWithName: @"MainUI" type: @"SOGo"];
length = [tableType length] - 3;
- descFile = [bundle pathForResource: [tableType substringToIndex: length]
+ tableFile = [tableType substringToIndex: length];
+ descFile
+ = [bundle pathForResource: [NSString stringWithFormat: @"%@-%@",
+ tableFile, fileSuffix]
ofType: @"sql"];
+ if (!descFile)
+ descFile = [bundle pathForResource: tableFile ofType: @"sql"];
if (![tc evaluateExpressionX:
[NSString stringWithContentsOfFile: descFile]])
[self logWithFormat: @"table '%@' successfully created!", tableName];
- added support for limiting LDAP queries with the SOGoLDAPQueryLimit and
the SOGoLDAPSizeLimit settings;
- fixed a bug where folders starting with digits would not be displayed;
-- improved IE7 and Safari support: priority menus, attendees selector;
+- improved IE7 and Safari support: priority menus, attendees selector,
+ search fields, textarea sizes;
- added the ability to print messages from the mailer toolbar;
- added the ability to use and configure SMTP as the email transport instead
of sendmail;
privacySqlString = @"and (c_isopaque = 1)";
else
{
- email = [activeUser primaryEmail];
+#warning we do not manage all the user's possible emails
+ email = [[activeUser primaryIdentity] objectForKey: @"email"];
privacySqlString
= [NSString stringWithFormat:
iCalPerson *person;
NSString *newContent;
NSException *ex;
- NSString *myEMail;
ex = nil;
component = [self component: NO];
if (component)
{
- myEMail = [[context activeUser] primaryEmail];
person = [self findParticipantWithUID: owner];
if (person)
{
#import <Foundation/NSArray.h>
#import <Foundation/NSEnumerator.h>
+#import <SoObjects/SOGo/NSArray+Utilities.h>
#import <SoObjects/SOGo/SOGoUser.h>
#import "iCalEntityObject+SOGo.h"
- (BOOL) userIsParticipant: (SOGoUser *) user
{
NSEnumerator *emails;
+ NSArray *identities;
NSString *currentEmail;
BOOL response;
response = NO;
- emails = [[user allEmails] objectEnumerator];
+ identities = [user allIdentities];
+ emails = [[identities objectsForKey: @"email"] objectEnumerator];
currentEmail = [emails nextObject];
while (!response && currentEmail)
if ([self isParticipant: currentEmail])
- (BOOL) userIsOrganizer: (SOGoUser *) user
{
NSEnumerator *emails;
+ NSArray *identities;
NSString *currentEmail;
BOOL response;
response = NO;
- emails = [[user allEmails] objectEnumerator];
+ identities = [user allIdentities];
+ emails = [[identities objectsForKey: @"email"] objectEnumerator];
currentEmail = [emails nextObject];
while (!response && currentEmail)
if ([self isOrganizer: currentEmail])
SOGoMailFolder.m \
SOGoSharedInboxFolder.m \
SOGoMailObject.m \
+ SOGoMailObject+Draft.m \
SOGoTrashFolder.m \
\
SOGoMailBodyPart.m \
\
SOGoDraftsFolder.m \
SOGoDraftObject.m \
- \
- SOGoMailIdentity.m \
- SOGoUser+Mail.m \
Mailer_RESOURCE_FILES += \
Version \
#ifndef __Mailer_SOGoDraftObject_H__
#define __Mailer_SOGoDraftObject_H__
-#include <Mailer/SOGoMailBaseObject.h>
+#import "SOGoMailBaseObject.h"
/*
SOGoDraftsFolder
TODO: store-info should be an own object, not NSDictionary.
*/
-@class NSString, NSArray, NSDictionary, NSData, NSException;
-@class NGMimeMessage, NGImap4Envelope;
+@class NSArray;
+@class NSData;
+@class NSDictionary;
+@class NSException;
+@class NGImap4Envelope;
+@class NGMimeMessage;
+@class NSMutableDictionary;
+@class NSString;
+
+@class SOGoMailObject;
@interface SOGoDraftObject : SOGoMailBaseObject
{
- NSString *path;
- NSDictionary *info; /* stores the envelope information */
+ NSString *path;
NGImap4Envelope *envelope;
+ int IMAP4ID;
+ NSMutableDictionary *headers;
+ NSString *text;
+ NSString *sourceURL;
+ NSString *sourceFlag;
}
/* contents */
+- (void) fetchInfo;
+- (NSException *) storeInfo;
+
+- (void) fetchMailForEditing: (SOGoMailObject *) sourceMail;
+- (void) fetchMailForReplying: (SOGoMailObject *) sourceMail
+ toAll: (BOOL) toAll;
+- (void) fetchMailForForwarding: (SOGoMailObject *) sourceMail;
+
+- (void) setHeaders: (NSDictionary *) newHeaders;
+- (NSDictionary *) headers;
+- (void) setText: (NSString *) newText;
+- (NSString *) text;
+
+/* for replies and forwards */
+- (void) setSourceURL: (NSString *) newSurceURL;
+- (void) setSourceFlag: (NSString *) newSourceFlag;
-- (NSDictionary *) fetchInfo;
-- (NSException *) storeInfo: (NSDictionary *) _info;
+- (void) setIMAP4ID: (int) newIMAPID;
+- (int) IMAP4ID;
/* attachments */
/* NGMime representations */
- (NGMimeMessage *) mimeMessage;
-
-- (NSString *) saveMimeMessageToTemporaryFile;
-- (NSString *) saveMimeMessageToTemporaryFileWithHeaders:(NSDictionary *)_addh;
-
-- (NSException *) sendMail;
+- (NSData *) mimeMessageAsData;
/* operations */
-- (NSException *) delete;
+- (NSException *) sendMail;
+- (NSException *) save;
-/* fake being a SOGoMailObject */
+// /* fake being a SOGoMailObject */
-- (id) fetchParts: (NSArray *) _parts;
+// - (id) fetchParts: (NSArray *) _parts;
@end
#import <Foundation/NSAutoreleasePool.h>
#import <Foundation/NSDictionary.h>
#import <Foundation/NSKeyValueCoding.h>
+#import <Foundation/NSURL.h>
#import <Foundation/NSUserDefaults.h>
#import <Foundation/NSValue.h>
#import <NGExtensions/NSNull+misc.h>
#import <NGExtensions/NSObject+Logs.h>
#import <NGExtensions/NGQuotedPrintableCoding.h>
+#import <NGExtensions/NSString+misc.h>
+#import <NGImap4/NGImap4Connection.h>
+#import <NGImap4/NGImap4Client.h>
#import <NGImap4/NGImap4Envelope.h>
#import <NGImap4/NGImap4EnvelopeAddress.h>
#import <NGMail/NGMimeMessage.h>
#import <SoObjects/SOGo/NSCalendarDate+SOGo.h>
#import <SoObjects/SOGo/SOGoMailer.h>
+#import "SOGoMailAccount.h"
+#import "SOGoMailFolder.h"
+#import "SOGoMailObject.h"
+#import "SOGoMailObject+Draft.h"
#import "SOGoDraftObject.h"
static NSString *contentTypeValue = @"text/plain; charset=utf-8";
+static NSString *headerKeys[] = {@"subject", @"to", @"cc", @"bcc",
+ @"from", @"replyTo", nil};
@interface NSString (NGMimeHelpers)
static BOOL debugOn = NO;
static BOOL showTextAttachmentsInline = NO;
-+ (void)initialize {
++ (void) initialize
+{
NSUserDefaults *ud = [NSUserDefaults standardUserDefaults];
/* Note: be aware of the charset issues before enabling this! */
- showTextAttachmentsInline = [ud boolForKey:@"SOGoShowTextAttachmentsInline"];
+ showTextAttachmentsInline = [ud boolForKey: @"SOGoShowTextAttachmentsInline"];
- if ((draftDeleteDisabled = [ud boolForKey:@"SOGoNoDraftDeleteAfterSend"]))
+ if ((draftDeleteDisabled = [ud boolForKey: @"SOGoNoDraftDeleteAfterSend"]))
NSLog(@"WARNING: draft delete is disabled! (SOGoNoDraftDeleteAfterSend)");
- TextPlainType = [[NGMimeType mimeType:@"text" subType:@"plain"] copy];
- MultiMixedType = [[NGMimeType mimeType:@"multipart" subType:@"mixed"] copy];
+ TextPlainType = [[NGMimeType mimeType: @"text" subType: @"plain"] copy];
+ MultiMixedType = [[NGMimeType mimeType: @"multipart" subType: @"mixed"] copy];
}
-- (void)dealloc {
+- (id) init
+{
+ if ((self = [super init]))
+ {
+ IMAP4ID = -1;
+ headers = [NSMutableDictionary new];
+ text = @"";
+ sourceURL = nil;
+ sourceFlag = nil;
+ }
+
+ return self;
+}
+
+- (void) dealloc
+{
+ [headers release];
+ [text release];
[envelope release];
- [info release];
[path release];
+ [sourceURL release];
+ [sourceFlag release];
[super dealloc];
}
/* draft folder functionality */
-- (NSFileManager *)spoolFileManager {
- return [[self container] spoolFileManager];
-}
-- (NSString *)userSpoolFolderPath {
+- (NSString *) userSpoolFolderPath
+{
return [[self container] userSpoolFolderPath];
}
-- (BOOL)_ensureUserSpoolFolderPath {
- return [[self container] _ensureUserSpoolFolderPath];
-}
/* draft object functionality */
-- (NSString *)draftFolderPath {
- if (path != nil)
- return path;
-
- path = [[[self userSpoolFolderPath] stringByAppendingPathComponent:
- [self nameInContainer]] copy];
+- (NSString *) draftFolderPath
+{
+ if (!path)
+ {
+ path = [[self userSpoolFolderPath] stringByAppendingPathComponent:
+ nameInContainer];
+ [path retain];
+ }
+
return path;
}
-- (BOOL)_ensureDraftFolderPath {
+
+- (BOOL) _ensureDraftFolderPath
+{
NSFileManager *fm;
+
+ fm = [NSFileManager defaultManager];
- if (![self _ensureUserSpoolFolderPath])
- return NO;
-
- if ((fm = [self spoolFileManager]) == nil) {
- [self errorWithFormat:@"missing spool file manager!"];
- return NO;
- }
- return [fm createDirectoriesAtPath:[self draftFolderPath] attributes:nil];
+ return ([fm createDirectoriesAtPath: [container userSpoolFolderPath]
+ attributes: nil]
+ && [fm createDirectoriesAtPath: [self draftFolderPath]
+ attributes:nil]);
}
-- (NSString *)infoPath {
- return [[self draftFolderPath]
- stringByAppendingPathComponent:@".info.plist"];
+- (NSString *) infoPath
+{
+ return [[self draftFolderPath]
+ stringByAppendingPathComponent: @".info.plist"];
}
/* contents */
-- (NSException *)storeInfo:(NSDictionary *)_info {
- if (_info == nil) {
- return [NSException exceptionWithHTTPStatus:500 /* server error */
- reason:@"got no info to write for draft!"];
- }
- if (![self _ensureDraftFolderPath]) {
- [self errorWithFormat:@"could not create folder for draft: '%@'",
+- (void) setHeaders: (NSDictionary *) newHeaders
+{
+ id headerValue;
+ unsigned int count;
+
+ for (count = 0; count < 6; count++)
+ {
+ headerValue = [newHeaders objectForKey: headerKeys[count]];
+ if (headerValue)
+ [headers setObject: headerValue
+ forKey: headerKeys[count]];
+ else
+ [headers removeObjectForKey: headerKeys[count]];
+ }
+}
+
+- (NSDictionary *) headers
+{
+ return headers;
+}
+
+- (void) setText: (NSString *) newText
+{
+ ASSIGN (text, newText);
+}
+
+- (NSString *) text
+{
+ return text;
+}
+
+- (void) setSourceURL: (NSString *) newSourceURL
+{
+ ASSIGN (sourceURL, newSourceURL);
+}
+
+- (void) setSourceFlag: (NSString *) newSourceFlag
+{
+ ASSIGN (sourceFlag, newSourceFlag);
+}
+
+- (NSException *) storeInfo
+{
+ NSMutableDictionary *infos;
+ NSException *error;
+
+ if ([self _ensureDraftFolderPath])
+ {
+ infos = [NSMutableDictionary new];
+ [infos setObject: headers forKey: @"headers"];
+ if (text)
+ [infos setObject: text forKey: @"text"];
+ if (IMAP4ID > -1)
+ [infos setObject: [NSNumber numberWithInt: IMAP4ID]
+ forKey: @"IMAP4ID"];
+ if (sourceURL && sourceFlag)
+ {
+ [infos setObject: sourceURL forKey: @"sourceURL"];
+ [infos setObject: sourceFlag forKey: @"sourceFlag"];
+ }
+
+ if ([infos writeToFile: [self infoPath] atomically:YES])
+ error = nil;
+ else
+ {
+ [self errorWithFormat: @"could not write info: '%@'",
+ [self infoPath]];
+ error = [NSException exceptionWithHTTPStatus:500 /* server error */
+ reason: @"could not write draft info!"];
+ }
+
+ [infos release];
+ }
+ else
+ {
+ [self errorWithFormat: @"could not create folder for draft: '%@'",
[self draftFolderPath]];
- return [NSException exceptionWithHTTPStatus:500 /* server error */
- reason:@"could not create folder for draft!"];
- }
- if (![_info writeToFile:[self infoPath] atomically:YES]) {
- [self errorWithFormat:@"could not write info: '%@'", [self infoPath]];
- return [NSException exceptionWithHTTPStatus:500 /* server error */
- reason:@"could not write draft info!"];
- }
-
- /* reset info cache */
- [info release]; info = nil;
-
- return nil /* everything is excellent */;
+ error = [NSException exceptionWithHTTPStatus:500 /* server error */
+ reason: @"could not create folder for draft!"];
+ }
+
+ return error;
}
-- (NSDictionary *)fetchInfo {
+
+- (void) _loadInfosFromDictionary: (NSDictionary *) infoDict
+{
+ id value;
+
+ value = [infoDict objectForKey: @"headers"];
+ if (value)
+ [self setHeaders: value];
+
+ value = [infoDict objectForKey: @"text"];
+ if ([value length] > 0)
+ [self setText: value];
+
+ value = [infoDict objectForKey: @"IMAP4ID"];
+ if (value)
+ [self setIMAP4ID: [value intValue]];
+
+ value = [infoDict objectForKey: @"sourceURL"];
+ if (value)
+ [self setSourceURL: value];
+ value = [infoDict objectForKey: @"sourceFlag"];
+ if (value)
+ [self setSourceFlag: value];
+}
+
+- (NSString *) relativeImap4Name
+{
+ return [NSString stringWithFormat: @"%d", IMAP4ID];
+}
+
+- (void) fetchInfo
+{
NSString *p;
+ NSDictionary *infos;
+ NSFileManager *fm;
- if (info != nil)
- return info;
-
p = [self infoPath];
- if (![[self spoolFileManager] fileExistsAtPath:p]) {
- [self debugWithFormat:@"Note: info object does not yet exist: %@", p];
- return nil;
- }
+
+ fm = [NSFileManager defaultManager];
+ if ([fm fileExistsAtPath: p])
+ {
+ infos = [NSDictionary dictionaryWithContentsOfFile: p];
+ if (infos)
+ [self _loadInfosFromDictionary: infos];
+// else
+// [self errorWithFormat: @"draft info dictionary broken at path: %@", p];
+ }
+ else
+ [self debugWithFormat: @"Note: info object does not yet exist: %@", p];
+}
+
+- (void) setIMAP4ID: (int) newIMAP4ID
+{
+ IMAP4ID = newIMAP4ID;
+}
+
+- (int) IMAP4ID
+{
+ return IMAP4ID;
+}
+
+- (int) _IMAP4IDFromAppendResult: (NSDictionary *) result
+{
+ NSDictionary *results;
+ NSString *flag, *newIdString;
+
+ results = [[result objectForKey: @"RawResponse"]
+ objectForKey: @"ResponseResult"];
+ flag = [results objectForKey: @"flag"];
+ newIdString = [[flag componentsSeparatedByString: @" "] objectAtIndex: 2];
+
+ return [newIdString intValue];
+}
+
+- (NSException *) save
+{
+ NGImap4Client *client;
+ NSException *error;
+ NSData *message;
+ NSString *folder;
+ id result;
+
+ error = nil;
+ message = [self mimeMessageAsData];
+
+ client = [[self imap4Connection] client];
+ folder = [imap4 imap4FolderNameForURL: [container imap4URL]];
+ result
+ = [client append: message toFolder: folder
+ withFlags: [NSArray arrayWithObjects: @"seen", @"draft", nil]];
+ if ([[result objectForKey: @"result"] boolValue])
+ {
+ if (IMAP4ID > -1)
+ error = [imap4 markURLDeleted: [self imap4URL]];
+ IMAP4ID = [self _IMAP4IDFromAppendResult: result];
+ [self storeInfo];
+ }
+ else
+ error = [NSException exceptionWithHTTPStatus:500 /* Server Error */
+ reason: @"Failed to store message"];
+
+ return error;
+}
+
+- (void) fetchMailForEditing: (SOGoMailObject *) sourceMail
+{
+#warning unimplemented
+}
+
+- (void) _addEMailsOfAddresses: (NSArray *) _addrs
+ toArray: (NSMutableArray *) _ma
+{
+ unsigned i, count;
+
+ for (i = 0, count = [_addrs count]; i < count; i++)
+ [_ma addObject:
+ [(NGImap4EnvelopeAddress *) [_addrs objectAtIndex: i] email]];
+}
+
+- (void) _fillInReplyAddresses: (NSMutableDictionary *) _info
+ replyToAll: (BOOL) _replyToAll
+ envelope: (NGImap4Envelope *) _envelope
+{
+ /*
+ The rules as implemented by Thunderbird:
+ - if there is a 'reply-to' header, only include that (as TO)
+ - if we reply to all, all non-from addresses are added as CC
+ - the from is always the lone TO (except for reply-to)
+
+ Note: we cannot check reply-to, because Cyrus even sets a reply-to in the
+ envelope if none is contained in the message itself! (bug or
+ feature?)
+
+ TODO: what about sender (RFC 822 3.6.2)
+ */
+ NSMutableArray *to;
+ NSArray *addrs;
+
+ to = [NSMutableArray arrayWithCapacity:2];
+
+ /* first check for "reply-to" */
- info = [[NSDictionary alloc] initWithContentsOfFile:p];
- if (info == nil)
- [self errorWithFormat:@"draft info dictionary broken at path: %@", p];
+ addrs = [_envelope replyTo];
+ if ([addrs count] == 0)
+ /* no "reply-to", try "from" */
+ addrs = [_envelope from];
+
+ [self _addEMailsOfAddresses: addrs toArray: to];
+ [_info setObject: to forKey: @"to"];
+
+ /* CC processing if we reply-to-all: add all 'to' and 'cc' */
- return info;
+ if (_replyToAll)
+ {
+ to = [NSMutableArray arrayWithCapacity:8];
+
+ [self _addEMailsOfAddresses: [_envelope to] toArray: to];
+ [self _addEMailsOfAddresses: [_envelope cc] toArray: to];
+
+ [_info setObject: to forKey: @"cc"];
+ }
+}
+
+- (void) fetchMailForReplying: (SOGoMailObject *) sourceMail
+ toAll: (BOOL) toAll
+{
+ NSString *contentForReply;
+ NSMutableDictionary *info;
+
+ [sourceMail fetchCoreInfos];
+
+ info = [NSMutableDictionary dictionaryWithCapacity: 16];
+ [info setObject: [sourceMail subjectForReply] forKey: @"subject"];
+ [self _fillInReplyAddresses: info replyToAll: toAll
+ envelope: [sourceMail envelope]];
+ contentForReply = [sourceMail contentForReply];
+ [self setText: contentForReply];
+ [self setHeaders: info];
+ [self setSourceURL: [sourceMail imap4URLString]];
+ [self setSourceFlag: @"Answered"];
+ [self storeInfo];
+}
+
+- (void) fetchMailForForwarding: (SOGoMailObject *) sourceMail
+{
+ NSDictionary *info, *attachment;
+
+ [sourceMail fetchCoreInfos];
+
+ info = [NSDictionary dictionaryWithObject: [sourceMail subjectForForward]
+ forKey: @"subject"];
+ [self setHeaders: info];
+ [self setSourceURL: [sourceMail imap4URLString]];
+ [self setSourceFlag: @"$Forwarded"];
+
+ /* attach message */
+
+ // TODO: use subject for filename?
+// error = [newDraft saveAttachment:content withName:@"forward.mail"];
+ attachment = [NSDictionary dictionaryWithObjectsAndKeys:
+ [sourceMail filenameForForward], @"filename",
+ @"message/rfc822", @"mime-type",
+ nil];
+ [self saveAttachment: [sourceMail content]
+ withMetadata: attachment];
+ [self storeInfo];
}
/* accessors */
-- (NSString *)sender {
+- (NSString *) sender
+{
id tmp;
- if ((tmp = [[self fetchInfo] objectForKey:@"from"]) == nil)
+ if ((tmp = [headers objectForKey: @"from"]) == nil)
return nil;
if ([tmp isKindOfClass:[NSArray class]])
- return [tmp count] > 0 ? [tmp objectAtIndex:0] : nil;
+ return [tmp count] > 0 ? [tmp objectAtIndex: 0] : nil;
+
return tmp;
}
/* attachments */
-- (NSArray *)fetchAttachmentNames {
+- (NSArray *) fetchAttachmentNames
+{
NSMutableArray *ma;
- NSFileManager *fm;
- NSArray *files;
- unsigned i, count;
-
- fm = [self spoolFileManager];
- if ((files = [fm directoryContentsAtPath:[self draftFolderPath]]) == nil)
- return nil;
-
- count = [files count];
- ma = [NSMutableArray arrayWithCapacity:count];
- for (i = 0; i < count; i++) {
- NSString *filename;
-
- filename = [files objectAtIndex:i];
- if ([filename hasPrefix:@"."])
- continue;
-
- [ma addObject:filename];
- }
+ NSFileManager *fm;
+ NSArray *files;
+ unsigned count, max;
+ NSString *filename;
+
+ fm = [NSFileManager defaultManager];
+ files = [fm directoryContentsAtPath: [self draftFolderPath]];
+
+ max = [files count];
+ ma = [NSMutableArray arrayWithCapacity: max];
+ for (count = 0; count < max; count++)
+ {
+ filename = [files objectAtIndex: count];
+ if (![filename hasPrefix: @"."])
+ [ma addObject: filename];
+ }
+
return ma;
}
-- (BOOL)isValidAttachmentName:(NSString *)_name {
+- (BOOL) isValidAttachmentName: (NSString *) _name
+{
static NSString *sescape[] = { @"/", @"..", @"~", @"\"", @"'", @" ", nil };
unsigned i;
NSRange r;
if (![_name isNotNull]) return NO;
if ([_name length] == 0) return NO;
- if ([_name hasPrefix:@"."]) return NO;
+ if ([_name hasPrefix: @"."]) return NO;
for (i = 0; sescape[i] != nil; i++) {
r = [_name rangeOfString:sescape[i]];
return YES;
}
-- (NSString *)pathToAttachmentWithName:(NSString *)_name {
+- (NSString *) pathToAttachmentWithName: (NSString *) _name
+{
if ([_name length] == 0)
return nil;
return [[self draftFolderPath] stringByAppendingPathComponent:_name];
}
-- (NSException *)invalidAttachmentNameError:(NSString *)_name {
+- (NSException *) invalidAttachmentNameError: (NSString *) _name
+{
return [NSException exceptionWithHTTPStatus:400 /* Bad Request */
- reason:@"Invalid attachment name!"];
+ reason: @"Invalid attachment name!"];
}
- (NSException *) saveAttachment: (NSData *) _attach
if (![_attach isNotNull]) {
return [NSException exceptionWithHTTPStatus:400 /* Bad Request */
- reason:@"Missing attachment content!"];
+ reason: @"Missing attachment content!"];
}
if (![self _ensureDraftFolderPath]) {
return [NSException exceptionWithHTTPStatus:500 /* Server Error */
- reason:@"Could not create folder for draft!"];
+ reason: @"Could not create folder for draft!"];
}
name = [metadata objectForKey: @"filename"];
if (![self isValidAttachmentName: name])
if (![_attach writeToFile: p atomically: YES])
{
return [NSException exceptionWithHTTPStatus:500 /* Server Error */
- reason:@"Could not write attachment to draft!"];
+ reason: @"Could not write attachment to draft!"];
}
mimeType = [metadata objectForKey: @"mime-type"];
writeToFile: p atomically: YES])
{
return [NSException exceptionWithHTTPStatus:500 /* Server Error */
- reason:@"Could not write attachment to draft!"];
+ reason: @"Could not write attachment to draft!"];
}
}
return nil; /* everything OK */
}
-- (NSException *)deleteAttachmentWithName:(NSString *)_name {
+- (NSException *) deleteAttachmentWithName: (NSString *) _name
+{
NSFileManager *fm;
NSString *p;
-
- if (![self isValidAttachmentName:_name])
- return [self invalidAttachmentNameError:_name];
-
- fm = [self spoolFileManager];
- p = [self pathToAttachmentWithName:_name];
- if (![fm fileExistsAtPath:p])
- return nil; /* well, doesn't exist, so its deleted ;-) */
-
- if (![fm removeFileAtPath:p handler:nil]) {
- [self logWithFormat:@"ERROR: failed to delete file: %@", p];
- return [NSException exceptionWithHTTPStatus:500 /* Server Error */
- reason:@"Could not delete attachment from draft!"];
- }
- return nil; /* everything OK */
+ NSException *error;
+
+ error = nil;
+
+ if ([self isValidAttachmentName:_name])
+ {
+ fm = [NSFileManager defaultManager];
+ p = [self pathToAttachmentWithName:_name];
+ if ([fm fileExistsAtPath: p])
+ if (![fm removeFileAtPath: p handler: nil])
+ error
+ = [NSException exceptionWithHTTPStatus: 500 /* Server Error */
+ reason: @"Could not delete attachment from draft!"];
+ }
+ else
+ error = [self invalidAttachmentNameError:_name];
+
+ return error;
}
/* NGMime representations */
-- (NGMimeBodyPart *)bodyPartForText
+- (NGMimeBodyPart *) bodyPartForText
{
/*
This add the text typed by the user (the primary plain/text part).
*/
NGMutableHashMap *map;
NGMimeBodyPart *bodyPart;
- NSDictionary *lInfo;
- id body;
-
- if ((lInfo = [self fetchInfo]) == nil)
- return nil;
/* prepare header of body part */
- map = [[[NGMutableHashMap alloc] initWithCapacity:2] autorelease];
+ map = [[[NGMutableHashMap alloc] initWithCapacity: 1] autorelease];
// TODO: set charset in header!
- [map setObject:@"text/plain" forKey:@"content-type"];
- if ((body = [lInfo objectForKey:@"text"]) != nil) {
- if ([body isKindOfClass: [NSString class]]) {
- [map setObject: contentTypeValue
- forKey: @"content-type"];
-// body = [body dataUsingEncoding:NSUTF8StringEncoding];
- }
- }
+ [map setObject: @"text/plain" forKey: @"content-type"];
+ if (text)
+ [map setObject: contentTypeValue forKey: @"content-type"];
+
+// if ((body = text) != nil) {
+// if ([body isKindOfClass: [NSString class]]) {
+// [map setObject: contentTypeValue
+// forKey: @"content-type"];
+// // body = [body dataUsingEncoding:NSUTF8StringEncoding];
+// }
+// }
/* prepare body content */
bodyPart = [[[NGMimeBodyPart alloc] initWithHeader:map] autorelease];
- [bodyPart setBody:body];
+ [bodyPart setBody: text];
+
return bodyPart;
}
-- (NGMimeMessage *)mimeMessageForContentWithHeaderMap:(NGMutableHashMap *)map
+- (NGMimeMessage *) mimeMessageForContentWithHeaderMap: (NGMutableHashMap *) map
{
- NSDictionary *lInfo;
NGMimeMessage *message;
- BOOL addSuffix;
+// BOOL addSuffix;
id body;
- if ((lInfo = [self fetchInfo]) == nil)
- return nil;
-
[map setObject: @"text/plain" forKey: @"content-type"];
- if ((body = [lInfo objectForKey:@"text"]) != nil) {
- if ([body isKindOfClass:[NSString class]])
- /* Note: just 'utf8' is displayed wrong in Mail.app */
- [map setObject: contentTypeValue
- forKey: @"content-type"];
+ body = text;
+ if (body)
+ {
+// if ([body isKindOfClass:[NSString class]])
+ /* Note: just 'utf8' is displayed wrong in Mail.app */
+ [map setObject: contentTypeValue
+ forKey: @"content-type"];
// body = [body dataUsingEncoding:NSUTF8StringEncoding];
- else if ([body isKindOfClass:[NSData class]] && addSuffix) {
- body = [[body mutableCopy] autorelease];
+// else if ([body isKindOfClass:[NSData class]] && addSuffix) {
+// body = [[body mutableCopy] autorelease];
+// }
+// else if (addSuffix) {
+// [self warnWithFormat: @"Note: cannot add Internet marker to body: %@",
+// NSStringFromClass([body class])];
+// }
+
+ message = [[[NGMimeMessage alloc] initWithHeader:map] autorelease];
+ [message setBody: body];
}
- else if (addSuffix) {
- [self warnWithFormat:@"Note: cannot add Internet marker to body: %@",
- NSStringFromClass([body class])];
- }
- }
-
- message = [[[NGMimeMessage alloc] initWithHeader:map] autorelease];
- [message setBody:body];
+ else
+ message = nil;
+
return message;
}
-- (NSString *)mimeTypeForExtension:(NSString *)_ext {
+- (NSString *) mimeTypeForExtension: (NSString *) _ext
+{
// TODO: make configurable
// TODO: use /etc/mime-types
- if ([_ext isEqualToString:@"txt"]) return @"text/plain";
- if ([_ext isEqualToString:@"html"]) return @"text/html";
- if ([_ext isEqualToString:@"htm"]) return @"text/html";
- if ([_ext isEqualToString:@"gif"]) return @"image/gif";
- if ([_ext isEqualToString:@"jpg"]) return @"image/jpeg";
- if ([_ext isEqualToString:@"jpeg"]) return @"image/jpeg";
- if ([_ext isEqualToString:@"mail"]) return @"message/rfc822";
+ if ([_ext isEqualToString: @"txt"]) return @"text/plain";
+ if ([_ext isEqualToString: @"html"]) return @"text/html";
+ if ([_ext isEqualToString: @"htm"]) return @"text/html";
+ if ([_ext isEqualToString: @"gif"]) return @"image/gif";
+ if ([_ext isEqualToString: @"jpg"]) return @"image/jpeg";
+ if ([_ext isEqualToString: @"jpeg"]) return @"image/jpeg";
+ if ([_ext isEqualToString: @"mail"]) return @"message/rfc822";
return @"application/octet-stream";
}
-- (NSString *)contentTypeForAttachmentWithName:(NSString *)_name {
+- (NSString *) contentTypeForAttachmentWithName: (NSString *) _name
+{
NSString *s, *p;
NSData *mimeData;
{
s = [self mimeTypeForExtension:[_name pathExtension]];
if ([_name length] > 0)
- s = [s stringByAppendingFormat:@"; name=\"%@\"", _name];
+ s = [s stringByAppendingFormat: @"; name=\"%@\"", _name];
}
return s;
}
-- (NSString *)contentDispositionForAttachmentWithName:(NSString *)_name {
+- (NSString *) contentDispositionForAttachmentWithName: (NSString *) _name
+{
NSString *type;
NSString *cdtype;
NSString *cd;
type = [self contentTypeForAttachmentWithName:_name];
- if ([type hasPrefix:@"text/"])
+ if ([type hasPrefix: @"text/"])
cdtype = showTextAttachmentsInline ? @"inline" : @"attachment";
- else if ([type hasPrefix:@"image/"] || [type hasPrefix:@"message"])
+ else if ([type hasPrefix: @"image/"] || [type hasPrefix: @"message"])
cdtype = @"inline";
else
cdtype = @"attachment";
- cd = [cdtype stringByAppendingString:@"; filename=\""];
+ cd = [cdtype stringByAppendingString: @"; filename=\""];
cd = [cd stringByAppendingString:_name];
- cd = [cd stringByAppendingString:@"\""];
+ cd = [cd stringByAppendingString: @"\""];
// TODO: add size parameter (useful addition, RFC 2183)
return cd;
}
-- (NGMimeBodyPart *)bodyPartForAttachmentWithName:(NSString *)_name {
+- (NGMimeBodyPart *) bodyPartForAttachmentWithName: (NSString *) _name
+{
NSFileManager *fm;
NGMutableHashMap *map;
NGMimeBodyPart *bodyPart;
/* check attachment */
- fm = [self spoolFileManager];
+ fm = [NSFileManager defaultManager];
p = [self pathToAttachmentWithName:_name];
if (![fm isReadableFileAtPath:p]) {
- [self errorWithFormat:@"did not find attachment: '%@'", _name];
+ [self errorWithFormat: @"did not find attachment: '%@'", _name];
return nil;
}
attachAsString = NO;
map = [[[NGMutableHashMap alloc] initWithCapacity:4] autorelease];
if ((s = [self contentTypeForAttachmentWithName:_name]) != nil) {
- [map setObject:s forKey:@"content-type"];
- if ([s hasPrefix:@"text/"])
+ [map setObject:s forKey: @"content-type"];
+ if ([s hasPrefix: @"text/"])
attachAsString = YES;
- else if ([s hasPrefix:@"message/rfc822"])
+ else if ([s hasPrefix: @"message/rfc822"])
is7bit = YES;
}
if ((s = [self contentDispositionForAttachmentWithName:_name]))
- [map setObject:s forKey:@"content-disposition"];
+ [map setObject:s forKey: @"content-disposition"];
/* prepare body content */
generator!
*/
body = [[NGMimeFileData alloc] initWithPath:p removeFile:NO];
- [map setObject:@"7bit" forKey:@"content-transfer-encoding"];
+ [map setObject: @"7bit" forKey: @"content-transfer-encoding"];
[map setObject:[NSNumber numberWithInt:[body length]]
- forKey:@"content-length"];
+ forKey: @"content-length"];
}
else {
/*
encoded = [content dataByEncodingBase64];
[content release]; content = nil;
- [map setObject:@"base64" forKey:@"content-transfer-encoding"];
+ [map setObject: @"base64" forKey: @"content-transfer-encoding"];
[map setObject:[NSNumber numberWithInt:[encoded length]]
- forKey:@"content-length"];
+ forKey: @"content-length"];
/* Note: the -init method will create a temporary file! */
body = [[NGMimeFileData alloc] initWithBytes:[encoded bytes]
return bodyPart;
}
-- (NSArray *)bodyPartsForAllAttachments {
+- (NSArray *) bodyPartsForAllAttachments
+{
/* returns nil on error */
- NSMutableArray *bodyParts;
NSArray *names;
unsigned i, count;
-
+ NGMimeBodyPart *bodyPart;
+ NSMutableArray *bodyParts;
+
names = [self fetchAttachmentNames];
- if ((count = [names count]) == 0)
- return [NSArray array];
-
- bodyParts = [NSMutableArray arrayWithCapacity:count];
- for (i = 0; i < count; i++) {
- NGMimeBodyPart *bodyPart;
-
- bodyPart = [self bodyPartForAttachmentWithName:[names objectAtIndex:i]];
- if (bodyPart == nil)
- return nil;
-
- [bodyParts addObject:bodyPart];
- }
+ count = [names count];
+ bodyParts = [NSMutableArray arrayWithCapacity: count];
+
+ for (i = 0; i < count; i++)
+ {
+ bodyPart = [self bodyPartForAttachmentWithName: [names objectAtIndex: i]];
+ [bodyParts addObject: bodyPart];
+ }
+
return bodyParts;
}
-- (NGMimeMessage *)mimeMultiPartMessageWithHeaderMap:(NGMutableHashMap *)map
- andBodyParts:(NSArray *)_bodyParts
+- (NGMimeMessage *) mimeMultiPartMessageWithHeaderMap: (NGMutableHashMap *) map
+ andBodyParts: (NSArray *) _bodyParts
{
NGMimeMessage *message;
NGMimeMultipartBody *mBody;
NGMimeBodyPart *part;
NSEnumerator *e;
- [map addObject:MultiMixedType forKey:@"content-type"];
-
- message = [[[NGMimeMessage alloc] initWithHeader:map] autorelease];
- mBody = [[NGMimeMultipartBody alloc] initWithPart:message];
-
+ [map addObject: MultiMixedType forKey: @"content-type"];
+
+ message = [[NGMimeMessage alloc] initWithHeader: map];
+ [message autorelease];
+ mBody = [[NGMimeMultipartBody alloc] initWithPart: message];
+
part = [self bodyPartForText];
- [mBody addBodyPart:part];
-
+ [mBody addBodyPart: part];
+
e = [_bodyParts objectEnumerator];
- while ((part = [e nextObject]) != nil)
- [mBody addBodyPart:part];
-
- [message setBody:mBody];
- [mBody release]; mBody = nil;
+ part = [e nextObject];
+ while (part)
+ {
+ [mBody addBodyPart: part];
+ part = [e nextObject];
+ }
+
+ [message setBody: mBody];
+ [mBody release];
+
return message;
}
-- (void)_addHeaders:(NSDictionary *)_h toHeaderMap:(NGMutableHashMap *)_map {
+- (void) _addHeaders: (NSDictionary *) _h
+ toHeaderMap: (NGMutableHashMap *) _map
+{
NSEnumerator *names;
NSString *name;
}
}
-- (BOOL)isEmptyValue:(id)_value {
+- (BOOL) isEmptyValue: (id) _value
+{
if (![_value isNotNull])
return YES;
- if ([_value isKindOfClass:[NSArray class]])
+ if ([_value isKindOfClass: [NSArray class]])
return [_value count] == 0 ? YES : NO;
- if ([_value isKindOfClass:[NSString class]])
+ if ([_value isKindOfClass: [NSString class]])
return [_value length] == 0 ? YES : NO;
-
+
return NO;
}
-- (NGMutableHashMap *)mimeHeaderMapWithHeaders:(NSDictionary *)_headers {
+- (NGMutableHashMap *) mimeHeaderMapWithHeaders: (NSDictionary *) _headers
+{
NGMutableHashMap *map;
- NSDictionary *lInfo; // TODO: this should be some kind of object?
NSArray *emails;
NSString *s, *dateString;
id from, replyTo;
- if ((lInfo = [self fetchInfo]) == nil)
- return nil;
-
map = [[[NGMutableHashMap alloc] initWithCapacity:16] autorelease];
/* add recipients */
- if ((emails = [lInfo objectForKey:@"to"]) != nil) {
+ if ((emails = [headers objectForKey: @"to"]) != nil) {
if ([emails count] == 0) {
- [self errorWithFormat:@"missing 'to' recipient in email!"];
+ [self errorWithFormat: @"missing 'to' recipient in email!"];
return nil;
}
- [map setObjects:emails forKey:@"to"];
+ [map setObjects: emails forKey: @"to"];
}
- if ((emails = [lInfo objectForKey:@"cc"]) != nil)
- [map setObjects:emails forKey:@"cc"];
- if ((emails = [lInfo objectForKey:@"bcc"]) != nil)
- [map setObjects:emails forKey:@"bcc"];
+ if ((emails = [headers objectForKey: @"cc"]) != nil)
+ [map setObjects:emails forKey: @"cc"];
+ if ((emails = [headers objectForKey: @"bcc"]) != nil)
+ [map setObjects:emails forKey: @"bcc"];
/* add senders */
- from = [lInfo objectForKey:@"from"];
- replyTo = [lInfo objectForKey:@"replyTo"];
+ from = [headers objectForKey: @"from"];
+ replyTo = [headers objectForKey: @"replyTo"];
if (![self isEmptyValue:from]) {
if ([from isKindOfClass:[NSArray class]])
- [map setObjects:from forKey:@"from"];
+ [map setObjects: from forKey: @"from"];
else
- [map setObject:from forKey:@"from"];
+ [map setObject: from forKey: @"from"];
}
- if (![self isEmptyValue:replyTo]) {
+ if (![self isEmptyValue: replyTo]) {
if ([from isKindOfClass:[NSArray class]])
- [map setObjects:from forKey:@"reply-to"];
+ [map setObjects:from forKey: @"reply-to"];
else
- [map setObject:from forKey:@"reply-to"];
+ [map setObject:from forKey: @"reply-to"];
}
else if (![self isEmptyValue:from])
- [map setObjects:[map objectsForKey:@"from"] forKey:@"reply-to"];
+ [map setObjects:[map objectsForKey: @"from"] forKey: @"reply-to"];
/* add subject */
- if ([(s = [lInfo objectForKey:@"subject"]) length] > 0)
+ if ([(s = [headers objectForKey: @"subject"]) length] > 0)
[map setObject: [s asQPSubjectString: @"utf-8"]
- forKey:@"subject"];
-// [map setObject: [s asQPSubjectString: @"utf-8"] forKey:@"subject"];
+ forKey: @"subject"];
+// [map setObject: [s asQPSubjectString: @"utf-8"] forKey: @"subject"];
/* add standard headers */
dateString = [[NSCalendarDate date] rfc822DateString];
- [map addObject: dateString forKey:@"date"];
- [map addObject: @"1.0" forKey:@"MIME-Version"];
- [map addObject: userAgent forKey:@"X-Mailer"];
+ [map addObject: dateString forKey: @"date"];
+ [map addObject: @"1.0" forKey: @"MIME-Version"];
+ [map addObject: userAgent forKey: @"X-Mailer"];
/* add custom headers */
- [self _addHeaders:[lInfo objectForKey:@"headers"] toHeaderMap:map];
- [self _addHeaders:_headers toHeaderMap:map];
+// [self _addHeaders: [lInfo objectForKey: @"headers"] toHeaderMap:map];
+ [self _addHeaders: _headers toHeaderMap: map];
return map;
}
-- (NGMimeMessage *)mimeMessageWithHeaders:(NSDictionary *)_headers {
- NSAutoreleasePool *pool;
+- (NGMimeMessage *) mimeMessageWithHeaders: (NSDictionary *) _headers
+{
NGMutableHashMap *map;
NSArray *bodyParts;
NGMimeMessage *message;
-
- pool = [[NSAutoreleasePool alloc] init];
-
- if ([self fetchInfo] == nil) {
- [self errorWithFormat:@"could not locate draft fetch info!"];
- return nil;
- }
-
- if ((map = [self mimeHeaderMapWithHeaders:_headers]) == nil)
- return nil;
- [self debugWithFormat:@"MIME Envelope: %@", map];
-
- if ((bodyParts = [self bodyPartsForAllAttachments]) == nil) {
- [self errorWithFormat:
- @"could not create body parts for attachments!"];
- return nil; // TODO: improve error handling, return exception
- }
- [self debugWithFormat:@"attachments: %@", bodyParts];
-
- if ([bodyParts count] == 0) {
- /* no attachments */
- message = [self mimeMessageForContentWithHeaderMap:map];
- }
- else {
- /* attachments, create multipart/mixed */
- message = [self mimeMultiPartMessageWithHeaderMap:map
- andBodyParts:bodyParts];
- }
- [self debugWithFormat:@"message: %@", message];
- message = [message retain];
- [pool release];
- return [message autorelease];
-}
-- (NGMimeMessage *)mimeMessage {
- return [self mimeMessageWithHeaders:nil];
-}
-
-- (NSString *)saveMimeMessageToTemporaryFileWithHeaders:(NSDictionary *)_h {
- NGMimeMessageGenerator *gen;
- NSAutoreleasePool *pool;
- NGMimeMessage *message;
- NSString *tmpPath;
+ message = nil;
- pool = [[NSAutoreleasePool alloc] init];
-
- message = [self mimeMessageWithHeaders:_h];
- if (![message isNotNull])
- return nil;
- if ([message isKindOfClass:[NSException class]]) {
- [self errorWithFormat:@"error: %@", message];
- return nil;
- }
-
- gen = [[NGMimeMessageGenerator alloc] init];
- tmpPath = [[gen generateMimeFromPartToFile:message] copy];
- [gen release]; gen = nil;
-
- [pool release];
- return [tmpPath autorelease];
-}
-- (NSString *)saveMimeMessageToTemporaryFile {
- return [self saveMimeMessageToTemporaryFileWithHeaders:nil];
-}
-
-- (void)deleteTemporaryMessageFile:(NSString *)_path {
- NSFileManager *fm;
+ map = [self mimeHeaderMapWithHeaders: _headers];
+ if (map)
+ {
+ [self debugWithFormat: @"MIME Envelope: %@", map];
- if (![_path isNotNull])
- return;
+ bodyParts = [self bodyPartsForAllAttachments];
+ if (bodyParts)
+ {
+ [self debugWithFormat: @"attachments: %@", bodyParts];
+
+ if ([bodyParts count] == 0)
+ /* no attachments */
+ message = [self mimeMessageForContentWithHeaderMap: map];
+ else
+ /* attachments, create multipart/mixed */
+ message = [self mimeMultiPartMessageWithHeaderMap: map
+ andBodyParts: bodyParts];
+ [self debugWithFormat: @"message: %@", message];
+ }
+ else
+ [self errorWithFormat:
+ @"could not create body parts for attachments!"];
+ }
- fm = [NSFileManager defaultManager];
- if (![fm fileExistsAtPath:_path])
- return;
-
- [fm removeFileAtPath:_path handler:nil];
+ return message;
}
-- (NSArray *)allRecipients {
- NSDictionary *lInfo;
- NSMutableArray *ma;
- NSArray *tmp;
-
- if ((lInfo = [self fetchInfo]) == nil)
- return nil;
-
- ma = [NSMutableArray arrayWithCapacity:16];
- if ((tmp = [lInfo objectForKey:@"to"]) != nil)
- [ma addObjectsFromArray:tmp];
- if ((tmp = [lInfo objectForKey:@"cc"]) != nil)
- [ma addObjectsFromArray:tmp];
- if ((tmp = [lInfo objectForKey:@"bcc"]) != nil)
- [ma addObjectsFromArray:tmp];
- return ma;
+- (NGMimeMessage *) mimeMessage
+{
+ return [self mimeMessageWithHeaders: nil];
}
-- (NSException *) sendMail
+- (NSData *) mimeMessageAsData
{
- NSException *error;
- NSString *tmpPath;
-
- /* save MIME mail to file */
-
- tmpPath = [self saveMimeMessageToTemporaryFile];
- if (![tmpPath isNotNull]) {
- return [NSException exceptionWithHTTPStatus:500 /* server error */
- reason:@"could not save MIME message for draft!"];
- }
-
- /* send mail */
- error = [[SOGoMailer sharedMailer] sendMailAtPath: tmpPath
- toRecipients: [self allRecipients]
- sender: [self sender]];
+ NGMimeMessageGenerator *generator;
+ NSData *message;
- /* delete temporary file */
- [self deleteTemporaryMessageFile:tmpPath];
+ generator = [NGMimeMessageGenerator new];
+ message = [generator generateMimeFromPart: [self mimeMessage]];
+ [generator release];
- return error;
+ return message;
}
-/* operations */
+- (NSArray *) allRecipients
+{
+ NSMutableArray *allRecipients;
+ NSArray *recipients;
+ NSString *fieldNames[] = {@"to", @"cc", @"bcc"};
+ unsigned int count;
-- (NSException *)delete {
- NSFileManager *fm;
- NSString *p, *sp;
- NSEnumerator *e;
-
- if ((fm = [self spoolFileManager]) == nil) {
- [self errorWithFormat:@"missing spool file manager!"];
- return [NSException exceptionWithHTTPStatus:500 /* server error */
- reason:@"missing spool file manager!"];
- }
-
- p = [self draftFolderPath];
- if (![fm fileExistsAtPath:p]) {
- return [NSException exceptionWithHTTPStatus:404 /* not found */
- reason:@"did not find draft!"];
- }
-
- e = [[fm directoryContentsAtPath:p] objectEnumerator];
- while ((sp = [e nextObject])) {
- sp = [p stringByAppendingPathComponent:sp];
- if (draftDeleteDisabled) {
- [self logWithFormat:@"should delete draft file %@ ...", sp];
- continue;
- }
-
- if (![fm removeFileAtPath:sp handler:nil]) {
- return [NSException exceptionWithHTTPStatus:500 /* server error */
- reason:@"failed to delete draft!"];
- }
- }
+ allRecipients = [NSMutableArray arrayWithCapacity: 16];
- if (draftDeleteDisabled) {
- [self logWithFormat:@"should delete draft directory: %@", p];
- }
- else {
- if (![fm removeFileAtPath:p handler:nil]) {
- return [NSException exceptionWithHTTPStatus:500 /* server error */
- reason:@"failed to delete draft directory!"];
+ for (count = 0; count < 3; count++)
+ {
+ recipients = [headers objectForKey: fieldNames[count]];
+ if ([recipients count] > 0)
+ [allRecipients addObjectsFromArray: recipients];
}
- }
- return nil;
-}
-- (NSData *)content {
- /* Note: does not cache, expensive operation */
- NSData *data;
- NSString *p;
-
- if ((p = [self saveMimeMessageToTemporaryFile]) == nil)
- return nil;
-
- data = [NSData dataWithContentsOfMappedFile:p];
-
- /* delete temporary file */
- [self deleteTemporaryMessageFile:p];
-
- return data;
-}
-- (NSString *)contentAsString {
- NSString *str;
- NSData *data;
-
- if ((data = [self content]) == nil)
- return nil;
-
- str = [[NSString alloc] initWithData:data encoding:NSASCIIStringEncoding];
- if (str == nil) {
- [self errorWithFormat:@"could not load draft as ASCII (data size=%d)",
- [data length]];
- return nil;
- }
-
- return [str autorelease];
+ return allRecipients;
}
-/* actions */
-
-- (id)DELETEAction:(id)_ctx {
+- (NSException *) sendMail
+{
NSException *error;
-
- if ((error = [self delete]) != nil)
- return error;
+ SOGoMailFolder *sentFolder;
+ NSData *message;
+ NSURL *sourceIMAP4URL;
- return [NSNumber numberWithBool:YES]; /* delete worked out ... */
-}
-
-- (id) GETAction: (id) _ctx
-{
- /*
- Override, because SOGoObject's GETAction uses the less efficient
- -contentAsString method.
- */
- WORequest *rq;
-
- rq = [_ctx request];
- if ([rq isSoWebDAVRequest]) {
- WOResponse *r;
- NSData *content;
-
- if ((content = [self content]) == nil) {
- return [NSException exceptionWithHTTPStatus:500
- reason:@"Could not generate MIME content!"];
+ /* send mail */
+ sentFolder = [[self mailAccountFolder] sentFolderInContext: context];
+ if ([sentFolder isKindOfClass: [NSException class]])
+ error = (NSException *) sentFolder;
+ else
+ {
+ message = [self mimeMessageAsData];
+ error = [[SOGoMailer sharedMailer] sendMailData: message
+ toRecipients: [self allRecipients]
+ sender: [self sender]];
+ if (!error)
+ {
+ error = [sentFolder postData: message flags: @"seen"];
+ if (!error)
+ {
+ [self imap4Connection];
+ if (IMAP4ID > -1)
+ [imap4 markURLDeleted: [self imap4URL]];
+ if (sourceURL && sourceFlag)
+ {
+ sourceIMAP4URL = [NSURL URLWithString: sourceURL];
+ [imap4 addFlags: sourceFlag toURL: sourceIMAP4URL];
+ }
+ }
+ }
}
- r = [_ctx response];
- [r setHeader:@"message/rfc822" forKey:@"content-type"];
- [r setContent:content];
- return r;
- }
-
- return [super GETAction:_ctx];
-}
-/* fake being a SOGoMailObject */
-
-- (id)fetchParts:(NSArray *)_parts {
- return [NSDictionary dictionaryWithObject:self forKey:@"fetch"];
+ return error;
}
-- (NSString *)uid {
- return [self nameInContainer];
-}
-- (NSArray *)flags {
- static NSArray *seenFlags = nil;
- seenFlags = [[NSArray alloc] initWithObjects:@"seen", nil];
- return seenFlags;
-}
-- (unsigned)size {
- // TODO: size, hard to support, we would need to generate MIME?
- return 0;
-}
+/* operations */
-- (NSArray *)imap4EnvelopeAddressesForStrings:(NSArray *)_emails {
- NSMutableArray *ma;
- unsigned i, count;
-
- if (_emails == nil)
- return nil;
- if ((count = [_emails count]) == 0)
- return [NSArray array];
-
- ma = [NSMutableArray arrayWithCapacity:count];
- for (i = 0; i < count; i++) {
- NGImap4EnvelopeAddress *envaddr;
-
- envaddr = [[NGImap4EnvelopeAddress alloc]
- initWithString:[_emails objectAtIndex:i]];
- if ([envaddr isNotNull])
- [ma addObject:envaddr];
- [envaddr release];
- }
- return ma;
-}
+- (NSString *) contentAsString
+{
+ NSString *str;
+ NSData *message;
-- (NGImap4Envelope *)envelope {
- NSDictionary *lInfo;
- id from, replyTo;
-
- if (envelope != nil)
- return envelope;
- if ((lInfo = [self fetchInfo]) == nil)
- return nil;
-
- if ((from = [self sender]) != nil)
- from = [NSArray arrayWithObjects:&from count:1];
+ message = [self mimeMessageAsData];
+ if (message)
+ {
+ str = [[NSString alloc] initWithData: message
+ encoding: NSUTF8StringEncoding];
+ if (!str)
+ [self errorWithFormat: @"could not load draft as UTF-8 (data size=%d)",
+ [message length]];
+ else
+ [str autorelease];
+ }
+ else
+ {
+ [self errorWithFormat: @"message data is empty"];
+ str = nil;
+ }
- if ((replyTo = [lInfo objectForKey:@"replyTo"]) != nil) {
- if (![replyTo isKindOfClass:[NSArray class]])
- replyTo = [NSArray arrayWithObjects:&replyTo count:1];
- }
-
- envelope =
- [[NGImap4Envelope alloc] initWithMessageID:[self nameInContainer]
- subject:[lInfo objectForKey:@"subject"]
- from:from replyTo:replyTo
- to:[lInfo objectForKey:@"to"]
- cc:[lInfo objectForKey:@"cc"]
- bcc:[lInfo objectForKey:@"bcc"]];
- return envelope;
+ return str;
}
/* debugging */
-- (BOOL)isDebuggingEnabled {
+- (BOOL) isDebuggingEnabled
+{
return debugOn;
}
-/*
- Copyright (C) 2004-2005 SKYRIX Software AG
-
- This file is part of OpenGroupware.org.
-
- OGo is free software; you can redistribute it and/or modify it under
- the terms of the GNU Lesser General Public License as published by the
- Free Software Foundation; either version 2, or (at your option) any
- later version.
-
- OGo 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 Lesser General Public
- License for more details.
-
- You should have received a copy of the GNU Lesser General Public
- License along with OGo; see the file COPYING. If not, write to the
- Free Software Foundation, 59 Temple Place - Suite 330, Boston, MA
- 02111-1307, USA.
-*/
-
-#ifndef __Mailer_SOGoDraftsFolder_H__
-#define __Mailer_SOGoDraftsFolder_H__
-
-#include <SoObjects/Mailer/SOGoMailBaseObject.h>
-
-/*
- SOGoDraftsFolder
- Parent object: SOGoMailAccount
- Child objects: SOGoDraftObject's
-
- The SOGoDraftsFolder is used for composing new messages. It is necessary
- because we can't cache objects in a session. So the contents of the drafts
- folder are some kind of "mail creation transaction".
-*/
-
-@interface SOGoDraftsFolder : SOGoMailBaseObject
-{
-}
-
-/* new objects */
-
-- (NSString *)makeNewObjectNameInContext:(id)_ctx;
-- (NSString *)newObjectBaseURLInContext:(id)_ctx;
-- (id)newObjectInContext:(id)_ctx;
+/* SOGoDraftsFolder.h - this file is part of SOGo
+ *
+ * Copyright (C) 2007 Inverse groupe conseil
+ *
+ * Author: Wolfgang Sourdeau <wsourdeau@inverse.ca>
+ *
+ * This file is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This file is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; see the file COPYING. If not, write to
+ * the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#import "SOGoMailFolder.h"
+
+@class SOGoDraftObject;
+
+@interface SOGoDraftsFolder : SOGoMailFolder
+
+- (SOGoDraftObject *) newDraft;
+- (BOOL) isInDraftsFolder;
@end
-
-#endif /* __Mailer_SOGoDraftsFolder_H__ */
-/*
- Copyright (C) 2004-2005 SKYRIX Software AG
-
- This file is part of OpenGroupware.org.
-
- OGo is free software; you can redistribute it and/or modify it under
- the terms of the GNU Lesser General Public License as published by the
- Free Software Foundation; either version 2, or (at your option) any
- later version.
-
- OGo 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 Lesser General Public
- License for more details.
-
- You should have received a copy of the GNU Lesser General Public
- License along with OGo; see the file COPYING. If not, write to the
- Free Software Foundation, 59 Temple Place - Suite 330, Boston, MA
- 02111-1307, USA.
-*/
-
-#import <unistd.h>
-
-#import <Foundation/NSArray.h>
+/* SOGoDraftsFolder.m - this file is part of SOGo
+ *
+ * Copyright (C) 2007 Inverse groupe conseil
+ *
+ * Author: Wolfgang Sourdeau <wsourdeau@inverse.ca>
+ *
+ * This file is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This file is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; see the file COPYING. If not, write to
+ * the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#import <Foundation/NSDate.h>
+#import <Foundation/NSString.h>
#import <Foundation/NSUserDefaults.h>
-#import <NGObjWeb/NSException+HTTP.h>
-#import <NGExtensions/NSFileManager+Extensions.h>
-#import <NGExtensions/NSNull+misc.h>
-#import <NGExtensions/NSObject+Logs.h>
+#import <NGObjWeb/WOContext+SoObjects.h>
+#import <SoObjects/SOGo/SOGoUser.h>
-#import <SoObjects/SOGo/SOGoUserFolder.h>
#import "SOGoDraftObject.h"
#import "SOGoDraftsFolder.h"
-@implementation SOGoDraftsFolder
-
static NSString *spoolFolder = nil;
+static NSTimeInterval lastNew = 0;
+static unsigned int newCount;
+
+@implementation SOGoDraftsFolder
+
+ (void) initialize
{
- NSUserDefaults *ud;;
+ NSUserDefaults *ud;
if (!spoolFolder)
{
ud = [NSUserDefaults standardUserDefaults];
- spoolFolder = [[ud stringForKey:@"SOGoMailSpoolPath"] copy];
- if ([spoolFolder length] < 3)
+ spoolFolder = [ud stringForKey:@"SOGoMailSpoolPath"];
+ if (![spoolFolder length])
spoolFolder = @"/tmp/";
+ [spoolFolder retain];
NSLog(@"Note: using SOGo mail spool folder: %@", spoolFolder);
}
}
-/* new objects */
-
-- (NSString *) makeNewObjectNameInContext: (id) _ctx
-{
- static int counter = 1; // THREAD
-
- return [NSString stringWithFormat:@"draft_%08d_%08x", getpid(), counter++];
-}
-
-- (NSString *) newObjectBaseURLInContext: (id) _ctx
-{
- NSString *s, *n;
-
- n = [self makeNewObjectNameInContext:_ctx];
- if (![n isNotNull]) return nil;
-
- s = [self baseURLInContext:_ctx];
- if (![s isNotNull]) return nil;
- if (![s hasSuffix:@"/"]) s = [s stringByAppendingString:@"/"];
- return [s stringByAppendingString:n];
-}
-
-- (id) newObjectInContext: (id) _ctx
-{
- return [self lookupName:[self makeNewObjectNameInContext:_ctx]
- inContext:_ctx acquire:NO];
-}
-
-/* draft folder functionality */
-
-- (NSFileManager *) spoolFileManager
-{
- return [NSFileManager defaultManager];
-}
-
-- (NSString *) spoolFolderPath
+- (NSString *) generateNameForNewDraft
{
- return spoolFolder;
-}
+ NSString *newName, *login;
+ unsigned int currentTime;
-- (NSString *) userSpoolFolderPath
-{
- NSString *p, *n;
-
- p = [self spoolFolderPath];
- n = [[self lookupUserFolder] nameInContainer];
+ currentTime = [[NSDate date] timeIntervalSince1970];
+ if (currentTime == lastNew)
+ newCount++;
+ else
+ {
+ lastNew = currentTime;
+ newCount = 1;
+ }
- return [p stringByAppendingPathComponent:n];
-}
+ login = [[context activeUser] login];
+ newName = [NSString stringWithFormat: @"newDraft%u-%u",
+ currentTime, newCount];
-- (BOOL) _ensureUserSpoolFolderPath
-{
- NSFileManager *fm;
-
- if ((fm = [self spoolFileManager]) == nil) {
- [self errorWithFormat:@"missing spool file manager!"];
- return NO;
- }
- return [fm createDirectoriesAtPath:[self userSpoolFolderPath]
- attributes:nil];
+ return newName;
}
-- (NSArray *) fetchMailNames
+- (SOGoDraftObject *) newDraft
{
- NSString *p;
-
- if ((p = [self userSpoolFolderPath]) == nil)
- return nil;
-
- return [[self spoolFileManager] directoryContentsAtPath:p];
+ return [SOGoDraftObject objectWithName: [self generateNameForNewDraft]
+ inContainer: self];
}
-/* folder methods (used by template) */
-
-- (NSArray *) fetchUIDsMatchingQualifier: (id) _q
- sortOrdering: (id) _so
+- (id) lookupName: (NSString *) name
+ inContext: (WOContext *) localContext
+ acquire: (BOOL) acquire
{
- // TODO: retrieve contained objects
- NSArray *allUids;
-
- allUids = [self fetchMailNames];
- if (![allUids isNotNull]) {
- [self logWithFormat:@"Note: no uids in drafts folder: %@",
- [self userSpoolFolderPath]];
- return [NSArray array];
- }
-
- // TODO: should sort uids (q=%@,so=%@): %@", _q, _so, allUids];
- return allUids;
-}
-- (NSArray *)fetchUIDs: (NSArray *)_uids parts: (NSArray *)_parts {
- /* FLAGS, ENVELOPE, RFC822.SIZE */
- NSMutableArray *drafts;
- unsigned i, count;
-
- if (_uids == nil)
- return nil;
- if ((count = [_uids count]) == 0)
- return [NSArray array];
-
- drafts = [NSMutableArray arrayWithCapacity:count];
- for (i = 0; i < count; i++) {
- SOGoDraftObject *draft;
- id parts;
-
- draft = [self lookupName:[_uids objectAtIndex:i] inContext:nil acquire:NO];
- if (![draft isNotNull] || [draft isKindOfClass:[NSException class]])
- continue;
-
- parts = [draft fetchParts:_parts];
- if ([parts isNotNull])
- [drafts addObject:parts];
- }
-
- return drafts;
-}
+ id object;
-/* name lookup */
+ if ([name hasPrefix: @"newDraft"])
+ object = [SOGoDraftObject objectWithName: name inContainer: self];
+ else
+ object = [super lookupName: name
+ inContext: localContext
+ acquire: acquire];
-- (id) lookupDraftMessage: (NSString *) _key
- inContext: (id) _ctx
-{
- // TODO: we might want to check for existence prior controller creation
- return [[[SOGoDraftObject alloc] initWithName:_key
- inContainer:self] autorelease];
+ return object;
}
-- (id) lookupName: (NSString *) _key
- inContext: (id) _ctx
- acquire: (BOOL) _flag
-{
- id obj;
-
- /* first check attributes directly bound to the application */
- if ((obj = [super lookupName:_key inContext:_ctx acquire:NO]) != nil)
- return obj;
-
- if ((obj = [self lookupDraftMessage:_key inContext:_ctx]) != nil)
- return obj;
-
- /* return 404 to stop acquisition */
- return [NSException exceptionWithHTTPStatus:404 /* Not Found */];
-}
-
-/* WebDAV */
-
-- (BOOL) davIsCollection
+- (BOOL) isInDraftsFolder
{
return YES;
}
-- (NSArray *) toOneRelationshipKeys
+- (NSString *) userSpoolFolderPath
{
- return [self fetchMailNames];
-}
+ NSString *login;
-/* folder type */
+ login = [[context activeUser] login];
-- (NSString *) outlookFolderClass
-{
- return @"IPF.Drafts";
+ return [NSString stringWithFormat: @"%@/%@",
+ spoolFolder, login];
}
-@end /* SOGoDraftsFolder */
+@end
#ifndef __Mailer_SOGoMailAccount_H__
#define __Mailer_SOGoMailAccount_H__
-#include <SoObjects/Mailer/SOGoMailBaseObject.h>
+#import <SoObjects/Mailer/SOGoMailBaseObject.h>
/*
SOGoMailAccount
password, etc)
*/
-@class NSString, NSArray;
-@class SOGoMailFolder, SOGoMailIdentity;
+@class NSArray;
+@class NSString;
+@class SOGoDraftsFolder;
+@class SOGoMailFolder;
@interface SOGoMailAccount : SOGoMailBaseObject
{
SOGoMailFolder *inboxFolder;
+ SOGoDraftsFolder *draftsFolder;
SOGoMailFolder *sentFolder;
SOGoMailFolder *trashFolder;
}
/* folder pathes */
-- (NSArray *)allFolderPaths;
-- (NSArray *)additionalRootFolderNames; /* stuff like filters and drafts */
+- (NSArray *) allFolderPaths;
+- (NSArray *) additionalRootFolderNames; /* stuff like filters and drafts */
+- (BOOL) isInDraftsFolder;
/* shared accounts */
-- (BOOL)isSharedAccount;
-- (NSString *)sharedAccountName;
-
-/* identity */
-
-- (SOGoMailIdentity *)preferredIdentity;
+- (BOOL) isSharedAccount;
+- (NSString *) sharedAccountName;
/* special folders */
-- (NSString *)inboxFolderNameInContext:(id)_ctx;
-- (NSString *)draftsFolderNameInContext:(id)_ctx;
-- (NSString *)sieveFolderNameInContext:(id)_ctx;
-- (NSString *)sentFolderNameInContext:(id)_ctx;
-- (NSString *)trashFolderNameInContext:(id)_ctx;
+- (NSString *) inboxFolderNameInContext: (id)_ctx;
+- (NSString *) draftsFolderNameInContext: (id)_ctx;
+- (NSString *) sieveFolderNameInContext: (id)_ctx;
+- (NSString *) sentFolderNameInContext: (id)_ctx;
+- (NSString *) trashFolderNameInContext: (id)_ctx;
-- (SOGoMailFolder *)inboxFolderInContext:(id)_ctx;
-- (SOGoMailFolder *)sentFolderInContext:(id)_ctx;
-- (SOGoMailFolder *)trashFolderInContext:(id)_ctx;
+- (SOGoMailFolder *) inboxFolderInContext: (id)_ctx;
+- (SOGoDraftsFolder *) draftsFolderInContext: (id)_ctx;
+- (SOGoMailFolder *) sentFolderInContext: (id)_ctx;
+- (SOGoMailFolder *) trashFolderInContext: (id)_ctx;
/* user defaults */
- (NSString *) sharedFolderName;
#import "SOGoMailFolder.h"
#import "SOGoMailManager.h"
#import "SOGoDraftsFolder.h"
-#import "SOGoUser+Mail.h"
#import "SOGoMailAccount.h"
@implementation SOGoMailAccount
-static NSArray *rootFolderNames = nil;
-static NSString *inboxFolderName = @"INBOX";
-static NSString *draftsFolderName = @"Drafts";
-static NSString *sieveFolderName = @"Filters";
+static NSArray *rootFolderNames = nil;
+static NSString *inboxFolderName = @"INBOX";
+static NSString *draftsFolderName = @"Drafts";
+static NSString *sieveFolderName = @"Filters";
static NSString *sentFolderName = nil;
static NSString *trashFolderName = nil;
-static NSString *sharedFolderName = @""; // TODO: add English default
+static NSString *sharedFolderName = @""; // TODO: add English default
static NSString *otherUsersFolderName = @""; // TODO: add English default
-static BOOL useAltNamespace = NO;
+static BOOL useAltNamespace = NO;
-+ (void)initialize {
++ (void) initialize
+{
NSUserDefaults *ud = [NSUserDefaults standardUserDefaults];
NSString *cfgDraftsFolderName;
NSLog(@"Note: using shared-folders name: '%@'", sharedFolderName);
NSLog(@"Note: using other-users-folders name: '%@'", otherUsersFolderName);
- if ([ud boolForKey:@"SOGoEnableSieveFolder"]) {
+ if ([ud boolForKey: @"SOGoEnableSieveFolder"])
rootFolderNames = [[NSArray alloc] initWithObjects:
draftsFolderName,
sieveFolderName,
nil];
- }
- else {
+ else
rootFolderNames = [[NSArray alloc] initWithObjects:
draftsFolderName,
nil];
- }
+}
+
+- (id) init
+{
+ if ((self = [super init]))
+ {
+ inboxFolder = nil;
+ draftsFolder = nil;
+ sentFolder = nil;
+ trashFolder = nil;
+ }
+
+ return self;
+}
+
+- (void) dealloc
+{
+ [inboxFolder release];
+ [draftsFolder release];
+ [sentFolder release];
+ [trashFolder release];
+ [super dealloc];
}
/* shared accounts */
-- (BOOL)isSharedAccount {
+- (BOOL) isSharedAccount
+{
NSString *s;
NSRange r;
return [s rangeOfString:@".-."].length > 0 ? YES : NO;
}
-- (NSString *)sharedAccountName {
+- (NSString *) sharedAccountName
+{
return nil;
}
/* listing the available folders */
-- (NSArray *)additionalRootFolderNames {
+- (NSArray *) additionalRootFolderNames
+{
return rootFolderNames;
}
+- (BOOL) isInDraftsFolder
+{
+ return NO;
+}
+
- (NSArray *) toManyRelationshipKeys
{
NSMutableArray *folders;
NSArray *imapFolders, *additionalFolders;
- folders = [NSMutableArray new];
- [folders autorelease];
+ folders = [NSMutableArray array];
imapFolders = [[self imap4Connection] subfoldersForURL: [self imap4URL]];
additionalFolders = [self additionalRootFolderNames];
return folders;
}
-/* identity */
-
-- (SOGoMailIdentity *)preferredIdentity {
- return [[context activeUser] primaryMailIdentityForAccount:
- [self nameInContainer]];
-}
-
/* hierarchy */
-- (SOGoMailAccount *)mailAccountFolder {
+- (SOGoMailAccount *) mailAccountFolder
+{
return self;
}
/* IMAP4 */
-- (BOOL)useSSL {
+- (BOOL) useSSL
+{
return NO;
}
-- (NSString *)imap4LoginFromHTTP {
+- (NSString *) imap4LoginFromHTTP
+{
WORequest *rq;
NSString *s;
NSArray *creds;
/* name lookup */
-- (id)lookupFolder:(NSString *)_key ofClassNamed:(NSString *)_cn
- inContext:(id)_cx
+- (id) lookupFolder: (NSString *) _key
+ ofClassNamed: (NSString *) _cn
+ inContext: (id) _cx
{
Class clazz;
+ SOGoMailFolder *folder;
- if ((clazz = NSClassFromString(_cn)) == Nil) {
- [self logWithFormat:@"ERROR: did not find class '%@' for key: '%@'",
+ if ((clazz = NSClassFromString(_cn)) == Nil)
+ {
+ [self logWithFormat:@"ERROR: did not find class '%@' for key: '%@'",
_cn, _key];
- return [NSException exceptionWithHTTPStatus:500 /* server error */
- reason:@"did not find mail folder class!"];
- }
- return [[[clazz alloc] initWithName:_key inContainer:self] autorelease];
+ return [NSException exceptionWithHTTPStatus:500 /* server error */
+ reason:@"did not find mail folder class!"];
+ }
+
+ folder = [clazz objectWithName: _key inContainer: self];
+
+ return folder;
}
-- (id)lookupImap4Folder:(NSString *)_key inContext:(id)_cx {
+- (id) lookupImap4Folder: (NSString *) _key
+ inContext: (id) _cx
+{
NSString *s;
s = [_key isEqualToString: [self trashFolderNameInContext:_cx]]
return [self lookupFolder:_key ofClassNamed:s inContext:_cx];
}
-- (id)lookupDraftsFolder:(NSString *)_key inContext:(id)_ctx {
- return [self lookupFolder:_key ofClassNamed:@"SOGoDraftsFolder"
- inContext:_ctx];
+- (id) lookupDraftsFolder: (NSString *) _key
+ inContext: (id) _ctx
+{
+ return [self lookupFolder: _key ofClassNamed: @"SOGoDraftsFolder"
+ inContext: _ctx];
}
-- (id)lookupFiltersFolder:(NSString *)_key inContext:(id)_ctx {
+
+- (id) lookupFiltersFolder: (NSString *) _key inContext: (id) _ctx
+{
return [self lookupFolder:_key ofClassNamed:@"SOGoSieveScriptsFolder"
inContext:_ctx];
}
return inboxFolder;
}
+- (SOGoDraftsFolder *) draftsFolderInContext: (id) _ctx
+{
+ SOGoMailFolder *lookupFolder;
+ // TODO: use some profile to determine real location, use a -traverse lookup
+
+ if (!draftsFolder)
+ {
+ lookupFolder = (useAltNamespace
+ ? (id) self
+ : [self inboxFolderInContext:_ctx]);
+ if (![lookupFolder isKindOfClass: [NSException class]])
+ draftsFolder
+ = [lookupFolder lookupName: [self draftsFolderNameInContext:_ctx]
+ inContext: _ctx acquire: NO];
+ if (![draftsFolder isNotNull])
+ draftsFolder = [NSException exceptionWithHTTPStatus: 404 /* not found */
+ reason: @"did not find Drafts folder!"];
+ [draftsFolder retain];
+ }
+
+ return draftsFolder;
+}
+
- (SOGoMailFolder *) sentFolderInContext: (id) _ctx
{
SOGoMailFolder *lookupFolder;
#ifndef __Mailer_SOGoMailAccounts_H__
#define __Mailer_SOGoMailAccounts_H__
-#include <SOGo/SOGoObject.h>
+#import <SOGo/SOGoObject.h>
/*
SOGoMailAccounts
@class NSArray;
@interface SOGoMailAccounts : SOGoObject
-{
-}
-
-- (NSArray *)fetchAllIdentities;
-- (NSArray *)fetchIdentitiesWithEmitterPermissions;
@end
#import <NGExtensions/NSNull+misc.h>
#import <NGExtensions/NSObject+Logs.h>
-#import "SOGoUser+Mail.h"
+#import "../SOGo/NSArray+Utilities.h"
+#import "../SOGo/SOGoUser.h"
#import "SOGoMailAccounts.h"
- (BOOL) isInHomeFolderBranchOfLoggedInAccount: (NSString *) userLogin
{
- return [[[self container] nameInContainer] isEqualToString: userLogin];
+ return [[container nameInContainer] isEqualToString: userLogin];
}
-- (NSArray *)toManyRelationshipKeys {
- SOGoUser *user;
- id account;
-// NSArray *shares;
- NSString *userLogin;
-
- /*
- Note: this is not strictly correct. The accounts being retrieved should be
- the accounts based on the container object of this folder. Given
- sufficient rights (eg delegation rights!), this would allow you to
- browse the hierarchies of other users.
-
- But then, the home-folder would need to know about mail
- functionality which isn't perfect either.
- => TODO
- */
- user = [context activeUser];
- userLogin = [user login];
-
- /* for now: return nothing if the home-folder does not belong to the login */
- if (![self isInHomeFolderBranchOfLoggedInAccount: userLogin]) {
- [self warnWithFormat:@"User %@ tried to access mail hierarchy of %@",
- [user login], [[self container] nameInContainer]];
- return nil;
- }
-
- account = [user primaryIMAP4AccountString];
- if ([account isNotNull]) account = [NSArray arrayWithObject:account];
-
- return account;
-// shares = [user valueForKey:@"additionalIMAP4AccountStrings"]
-// return ([shares count] == 0)
-// ? account
-// : [account arrayByAddingObjectsFromArray:shares];
-}
-
-- (NSArray *) fetchIdentitiesWithOnlyEmitterAccess: (BOOL) _flag
+- (NSArray *) toManyRelationshipKeys
{
- NSString *accountString;
+ NSArray *accounts;
- accountString = [[context activeUser] primaryIMAP4AccountString];
-
- return [NSArray arrayWithObject: accountString];
-}
+ accounts = [[context activeUser] mailAccounts];
-- (NSArray *)fetchAllIdentities {
- return [self fetchIdentitiesWithOnlyEmitterAccess:NO];
-}
-
-- (NSArray *)fetchIdentitiesWithEmitterPermissions {
- return [self fetchIdentitiesWithOnlyEmitterAccess:YES];
+ return [accounts objectsForKey: @"name"];
}
/* name lookup */
-- (BOOL)isValidMailAccountName:(NSString *)_key {
- if ([_key length] == 0)
- return NO;
-
- return YES;
+- (BOOL) isValidMailAccountName: (NSString *) _key
+{
+ return ([_key length] > 0);
}
-- (id)mailAccountWithName:(NSString *)_key inContext:(id)_ctx {
+- (id) mailAccountWithName: (NSString *) _key
+ inContext: (id) _ctx
+{
static Class ctClass = Nil;
id ct;
return [ct autorelease];
}
-- (id)sharedMailAccountWithName:(NSString *)_key inContext:(id)_ctx {
+- (id) sharedMailAccountWithName: (NSString *) _key
+ inContext: (id) _ctx
+{
static Class ctClass = Nil;
- id ct;
-
+
+ if (ctClass == Nil)
+ ctClass = NSClassFromString (@"SOGoSharedMailAccount");
if (ctClass == Nil)
- ctClass = NSClassFromString(@"SOGoSharedMailAccount");
- if (ctClass == Nil) {
[self errorWithFormat:@"missing SOGoSharedMailAccount class!"];
- return nil;
- }
- ct = [[ctClass alloc] initWithName:_key inContainer:self];
- return [ct autorelease];
+ return [ctClass objectWithName: _key inContainer: self];
}
-- (id)lookupName:(NSString *)_key inContext:(id)_ctx acquire:(BOOL)_flag
+- (id) lookupName: (NSString *) _key
+ inContext: (id) _ctx
+ acquire: (BOOL) _flag
{
id obj;
NSString *userLogin;
return obj;
if (![self isInHomeFolderBranchOfLoggedInAccount: userLogin]) {
- [self warnWithFormat:@"User %@ tried to access mail hierarchy of %@",
- userLogin, [[self container] nameInContainer]];
+ [self warnWithFormat:@ "User %@ tried to access mail hierarchy of %@",
+ userLogin, [container nameInContainer]];
return [NSException exceptionWithHTTPStatus:403 /* Forbidden */
reason:@"Tried to access the mail of another user"];
NGImap4Connection *imap4;
}
-- (id)initWithImap4URL:(NSURL *)_url inContainer:(id)_container;
+- (id) initWithImap4URL: (NSURL *) _url
+ inContainer: (id) _container;
/* hierarchy */
-- (SOGoMailAccount *)mailAccountFolder;
-- (SOGoMailAccounts *)mailAccountsFolder;
+- (SOGoMailAccount *) mailAccountFolder;
+- (SOGoMailAccounts *) mailAccountsFolder;
+- (BOOL) isInDraftsFolder;
/* IMAP4 */
-- (NGImap4Connection *)imap4Connection;
-- (NGImap4ConnectionManager *)mailManager;
+- (NGImap4Connection *) imap4Connection;
+- (NGImap4ConnectionManager *) mailManager;
- (NSString *) relativeImap4Name;
- (NSMutableString *) imap4URLString;
-- (NSURL *)imap4URL;
-- (NSString *)imap4Login;
-- (NSString *)imap4Password;
+- (NSURL *) imap4URL;
+- (NSString *) imap4Login;
+- (NSString *) imap4Password;
-- (void)flushMailCaches;
+- (void) flushMailCaches;
/* IMAP4 names */
-- (BOOL)isBodyPartKey:(NSString *)_key inContext:(id)_ctx;
+- (BOOL) isBodyPartKey: (NSString *) _key inContext: (id) _ctx;
@end
return o;
}
+- (BOOL) isInDraftsFolder
+{
+ return [container isInDraftsFolder];
+}
+
/* IMAP4 */
- (NGImap4ConnectionManager *) mailManager
urlString = [container imap4URLString];
imap4Name = [[self relativeImap4Name] stringByEscapingURL];
- [urlString appendFormat: @"%@/", imap4Name];
+ [urlString appendFormat: @"%@", imap4Name];
return urlString;
}
{
NSMutableArray *filenames;
NSString *folderType;
+ NSDictionary *mailboxACL;
}
/* messages */
inContainer: newContainer]))
{
[self _adjustOwner];
+ mailboxACL = nil;
}
return self;
{
[filenames release];
[folderType release];
+ [mailboxACL release];
[super dealloc];
}
return [nameInContainer substringFromIndex: 6];
}
+
+- (NSMutableString *) imap4URLString
+{
+ NSMutableString *urlString;
+
+ urlString = [super imap4URLString];
+ [urlString appendString: @"/"];
+
+ return urlString;
+}
+
/* listing the available folders */
- (NSArray *) toManyRelationshipKeys
sortOrdering: (id) _so
{
/* seems to return an NSArray of NSNumber's */
- return [[self imap4Connection] fetchUIDsInURL:[self imap4URL]
- qualifier:_q sortOrdering:_so];
+ return [[self imap4Connection] fetchUIDsInURL: [self imap4URL]
+ qualifier: _q sortOrdering: _so];
}
- (NSArray *) fetchUIDs: (NSArray *) _uids
parts: (NSArray *) _parts
{
- return [[self imap4Connection] fetchUIDs:_uids inURL:[self imap4URL]
- parts:_parts];
+ return [[self imap4Connection] fetchUIDs: _uids inURL: [self imap4URL]
+ parts: _parts];
}
- (NSException *) postData: (NSData *) _data
flags: (id) _flags
{
- return [[self imap4Connection] postData:_data flags:_flags
- toFolderURL:[self imap4URL]];
+ return [[self imap4Connection] postData: _data flags: _flags
+ toFolderURL: [self imap4URL]];
}
- (NSException *) expunge
return imapAcls;
}
+- (void) _readMailboxACL
+{
+ mailboxACL
+ = [[self imap4Connection] aclForMailboxAtURL: [self imap4URL]];
+ [mailboxACL retain];
+}
+
- (NSArray *) aclUsers
{
NSArray *users;
- NSDictionary *imapAcls;
- imapAcls = [[self imap4Connection] aclForMailboxAtURL: [self imap4URL]];
- if ([imapAcls isKindOfClass: [NSDictionary class]])
- users = [imapAcls allKeys];
+ if (!mailboxACL)
+ [self _readMailboxACL];
+
+ if ([mailboxACL isKindOfClass: [NSDictionary class]])
+ users = [mailboxACL allKeys];
else
users = nil;
- (NSArray *) aclsForUser: (NSString *) uid
{
- NSDictionary *imapAcls;
NSMutableArray *acls;
NSString *userAcls;
acls = [self _sharesACLs];
- imapAcls = [[self imap4Connection] aclForMailboxAtURL: [self imap4URL]];
- if ([imapAcls isKindOfClass: [NSDictionary class]])
+
+ if (!mailboxACL)
+ [self _readMailboxACL];
+
+ if ([mailboxACL isKindOfClass: [NSDictionary class]])
{
- userAcls = [imapAcls objectForKey: uid];
+ userAcls = [mailboxACL objectForKey: uid];
if (!([userAcls length] || [uid isEqualToString: defaultUserID]))
- userAcls = [imapAcls objectForKey: defaultUserID];
+ userAcls = [mailboxACL objectForKey: defaultUserID];
if ([userAcls length])
[acls addObjectsFromArray: [self _imapAclsToSOGoAcls: userAcls]];
}
[client deleteACL: folderName uid: currentUID];
currentUID = [uids nextObject];
}
+ [mailboxACL release];
+ mailboxACL = nil;
}
- (void) setRoles: (NSArray *) roles
acls = [self _sogoAclsToImapAcls: roles];
folderName = [[self imap4Connection] imap4FolderNameForURL: [self imap4URL]];
[[imap4 client] setACL: folderName rights: acls uid: uid];
+
+ [mailboxACL release];
+ mailboxACL = nil;
}
- (NSString *) defaultUserID
{
SOGoUser *user;
NSString *otherUsersPath, *url;
+ SOGoMailAccount *thisAccount;
+ NSDictionary *mailAccount;
user = [SOGoUser userWithLogin: uid roles: nil];
otherUsersPath = [self otherUsersPathToFolder];
if (otherUsersPath)
- url = [NSString stringWithFormat: @"%@/%@%@",
- [self soURLToBaseContainerForUser: uid],
- [user primaryIMAP4AccountString],
- otherUsersPath];
+ {
+ thisAccount = [self mailAccountFolder];
+ mailAccount = [[user mailAccounts] objectAtIndex: 0];
+ url = [NSString stringWithFormat: @"%@/%@%@",
+ [self soURLToBaseContainerForUser: uid],
+ [mailAccount objectForKey: @"name"],
+ otherUsersPath];
+ }
else
url = nil;
+++ /dev/null
-/*
- Copyright (C) 2005 SKYRIX Software AG
-
- This file is part of OpenGroupware.org.
-
- OGo is free software; you can redistribute it and/or modify it under
- the terms of the GNU Lesser General Public License as published by the
- Free Software Foundation; either version 2, or (at your option) any
- later version.
-
- OGo 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 Lesser General Public
- License for more details.
-
- You should have received a copy of the GNU Lesser General Public
- License along with OGo; see the file COPYING. If not, write to the
- Free Software Foundation, 59 Temple Place - Suite 330, Boston, MA
- 02111-1307, USA.
-*/
-
-#ifndef __Mailer_SOGoMailIdentity_H__
-#define __Mailer_SOGoMailIdentity_H__
-
-#import <Foundation/NSObject.h>
-
-/*
- SOGoMailIdentity
-
- A user identity bound to an account.
-
- Note: currently this is not a SoObject. This might change later on.
-
-
- In Thunderbird you have a set of accounts which in turn have a set of
- identities. There is one default identity.
-
- The identities then have:
- - settings
- - a name
- - a from-email
- - a reply-to
- - an organization
- - a signature
- - a vcard (to be attached)
- - folder settings
- - Sent-Folder and bcc
- - Drafts + Templates
- - composition
- - whether to use HTML
- - whether to quote the source message (reply below, above the quote or
- select the quote)
-*/
-
-@class NSString;
-
-@interface SOGoMailIdentity : NSObject
-{
- NSString *name;
- NSString *email;
- NSString *replyTo;
- NSString *organization;
- NSString *signature;
- NSString *vCard;
- NSString *sentFolderName;
- NSString *sentBCC;
- NSString *draftsFolderName;
- NSString *templatesFolderName;
- struct {
- int composeHTML:1;
- int reserved:31;
- } idFlags;
-}
-
-/* accessors */
-
-- (void)setName:(NSString *)_value;
-- (NSString *)name;
-
-- (void)setEmail:(NSString *)_value;
-- (NSString *)email;
-
-- (void)setReplyTo:(NSString *)_value;
-- (NSString *)replyTo;
-
-- (void)setOrganization:(NSString *)_value;
-- (NSString *)organization;
-
-- (void)setSignature:(NSString *)_value;
-- (NSString *)signature;
-- (BOOL)hasSignature;
-
-- (void)setVCard:(NSString *)_value;
-- (NSString *)vCard;
-- (BOOL)hasVCard;
-
-- (void)setSentFolderName:(NSString *)_value;
-- (NSString *)sentFolderName;
-
-- (void)setSentBCC:(NSString *)_value;
-- (NSString *)sentBCC;
-
-- (void)setDraftsFolderName:(NSString *)_value;
-- (NSString *)draftsFolderName;
-
-- (void)setTemplatesFolderName:(NSString *)_value;
-- (NSString *)templatesFolderName;
-
-@end
-
-#endif /* __Mailer_SOGoMailIdentity_H__ */
+++ /dev/null
-/*
- Copyright (C) 2005 SKYRIX Software AG
-
- This file is part of OpenGroupware.org.
-
- OGo is free software; you can redistribute it and/or modify it under
- the terms of the GNU Lesser General Public License as published by the
- Free Software Foundation; either version 2, or (at your option) any
- later version.
-
- OGo 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 Lesser General Public
- License for more details.
-
- You should have received a copy of the GNU Lesser General Public
- License along with OGo; see the file COPYING. If not, write to the
- Free Software Foundation, 59 Temple Place - Suite 330, Boston, MA
- 02111-1307, USA.
-*/
-
-#import <Foundation/NSString.h>
-
-#import <NGExtensions/NSNull+misc.h>
-
-#import "SOGoMailIdentity.h"
-
-@implementation SOGoMailIdentity
-
-- (void)dealloc {
- [name release];
- [email release];
- [replyTo release];
- [organization release];
- [signature release];
- [vCard release];
- [sentFolderName release];
- [sentBCC release];
- [draftsFolderName release];
- [templatesFolderName release];
- [super dealloc];
-}
-
-/* accessors */
-
-- (void)setName:(NSString *)_value {
- ASSIGNCOPY(name, _value);
-}
-- (NSString *)name {
- return name;
-}
-
-- (void)setEmail:(NSString *)_value {
- ASSIGNCOPY(email, _value);
-}
-- (NSString *)email {
- return email;
-}
-
-- (void)setReplyTo:(NSString *)_value {
- ASSIGNCOPY(replyTo, _value);
-}
-- (NSString *)replyTo {
- return replyTo;
-}
-
-- (void)setOrganization:(NSString *)_value {
- ASSIGNCOPY(organization, _value);
-}
-- (NSString *)organization {
- return organization;
-}
-
-- (void)setSignature:(NSString *)_value {
- ASSIGNCOPY(signature, _value);
-}
-- (NSString *)signature {
- return signature;
-}
-- (BOOL)hasSignature {
- return [[self signature] isNotEmpty];
-}
-
-- (void)setVCard:(NSString *)_value {
- ASSIGNCOPY(vCard, _value);
-}
-- (NSString *)vCard {
- return vCard;
-}
-- (BOOL)hasVCard {
- return [[self vCard] isNotEmpty];
-}
-
-- (void)setSentFolderName:(NSString *)_value {
- ASSIGNCOPY(sentFolderName, _value);
-}
-- (NSString *)sentFolderName {
- return sentFolderName;
-}
-
-- (void)setSentBCC:(NSString *)_value {
- ASSIGNCOPY(sentBCC, _value);
-}
-- (NSString *)sentBCC {
- return sentBCC;
-}
-
-- (void)setDraftsFolderName:(NSString *)_value {
- ASSIGNCOPY(draftsFolderName, _value);
-}
-- (NSString *)draftsFolderName {
- return draftsFolderName;
-}
-
-- (void)setTemplatesFolderName:(NSString *)_value {
- ASSIGNCOPY(templatesFolderName, _value);
-}
-- (NSString *)templatesFolderName {
- return templatesFolderName;
-}
-
-/* description */
-
-- (NSString *)description {
- NSMutableString *ms;
-
- ms = [NSMutableString stringWithCapacity:128];
- [ms appendFormat:@"<0x%08X[%@]:", self, NSStringFromClass([self class])];
-
- if (name != nil) [ms appendFormat:@" name='%@'", name];
- if (email != nil) [ms appendFormat:@" email='%@'", email];
-
- if (sentFolderName != nil)
- [ms appendFormat:@" sent='%@'", sentFolderName];
-
- if ([sentBCC length] > 0) [ms appendString:@" sent-bcc"];
- if ([vCard length] > 0) [ms appendString:@" vcard"];
-
- [ms appendString:@">"];
- return ms;
-}
-
-@end /* SOGoMailIdentity */
--- /dev/null
+/* SOGoMailObject+Draft.h - this file is part of SOGo
+ *
+ * Copyright (C) 2007 Inverse groupe conseil
+ *
+ * Author: Wolfgang Sourdeau <wsourdeau@inverse.ca>
+ *
+ * This file is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This file is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; see the file COPYING. If not, write to
+ * the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#ifndef SOGOMAILOBJECT_DRAFT_H
+#define SOGOMAILOBJECT_DRAFT_H
+
+#import "SOGoMailObject.h"
+
+@interface SOGoMailObject (SOGoDraftObjectExtensions)
+
+- (NSString *) subjectForReply;
+- (NSString *) contentForReply;
+
+- (NSString *) subjectForForward;
+- (NSString *) filenameForForward;
+
+@end
+
+#endif /* SOGOMAILOBJECT_DRAFT_H */
--- /dev/null
+/* SOGoMailObject+Draft.m - this file is part of SOGo
+ *
+ * Copyright (C) 2007 Inverse groupe conseil
+ *
+ * Author: Wolfgang Sourdeau <wsourdeau@inverse.ca>
+ *
+ * This file is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This file is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; see the file COPYING. If not, write to
+ * the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#import <Foundation/NSArray.h>
+
+#import <NGExtensions/NSString+misc.h>
+#import <NGExtensions/NSObject+Logs.h>
+
+#import "SOGoMailObject+Draft.h"
+
+#define maxFilenameLength 64
+
+@implementation SOGoMailObject (SOGoDraftObjectExtensions)
+
+- (NSString *) subjectForReply
+{
+ static NSString *replyPrefixes[] = {
+ @"Re:", // regular
+ @"RE:", // Outlook v11 (English?)
+ @"AW:", // German Outlook v11
+ @"Re[", // numbered Re, eg "Re[2]:"
+ nil
+ };
+ BOOL hasPrefix;
+ unsigned int i;
+ NSString *subject, *newSubject;
+
+ hasPrefix = NO;
+
+ subject = [[self envelope] subject];
+ i = 0;
+ while (!hasPrefix && replyPrefixes[i])
+ if ([subject hasPrefix: replyPrefixes[i]])
+ hasPrefix = YES;
+ else
+ i++;
+
+ if (hasPrefix)
+ newSubject = subject;
+ else
+ newSubject = [NSString stringWithFormat: @"Re: %@", subject];
+
+ return newSubject;
+}
+
+- (NSString *) contentForReplyOnParts: (NSDictionary *) _prts
+ keys: (NSArray *) _k
+{
+ static NSString *textPartSeparator = @"\n---\n";
+ NSMutableString *ms;
+ unsigned int count, max;
+ NSString *k, *v;
+
+ ms = [NSMutableString stringWithCapacity: 16000];
+
+ max = [_k count];
+ for (count = 0; count < max; count++)
+ {
+ k = [_k objectAtIndex: count];
+
+ // TODO: this is DUP code to SOGoMailObject
+ if ([k isEqualToString: @"body[text]"])
+ k = @"";
+ else if ([k hasPrefix: @"body["]) {
+ k = [k substringFromIndex: 5];
+ if ([k length] > 0)
+ k = [k substringToIndex: ([k length] - 1)];
+ }
+
+ v = [_prts objectForKey: k];
+ if ([v isKindOfClass: [NSString class]]
+ && [v length] > 0)
+ {
+ if (count > 0)
+ [ms appendString: textPartSeparator];
+ [ms appendString: [v stringByApplyingMailQuoting]];
+ }
+ else
+ [self logWithFormat:@"Note: cannot show part %@", k];
+ }
+
+ return ms;
+}
+
+#warning this method should be fixed to return the first available text/plain \
+ part, and otherwise the first text/html part converted to text
+- (NSString *) contentForReply
+{
+ NSArray *keys;
+ NSDictionary *parts;
+ NSMutableArray *topLevelKeys = nil;
+ unsigned int count, max;
+ NSRange r;
+ NSString *contentForReply;
+
+// SOGoMailObject *co;
+
+// co = self;
+// keys = [co plainTextContentFetchKeys];
+// infos = [co fetchCoreInfos];
+// partInfos = [infos objectForKey: keys];
+// NSLog (@"infos: '%@'", infos);
+
+ keys = [self plainTextContentFetchKeys];
+ max = [keys count];
+ if (max > 0)
+ {
+ if (max > 1)
+ {
+ /* filter keys, only include top-level, or if none, the first */
+ for (count = 0; count < max; count++)
+ {
+ r = [[keys objectAtIndex: count] rangeOfString: @"."];
+ if (!r.length)
+ {
+ if (!topLevelKeys)
+ topLevelKeys = [NSMutableArray arrayWithCapacity: 4];
+ [topLevelKeys addObject: [keys objectAtIndex: count]];
+ }
+ }
+
+ if ([topLevelKeys count] > 0)
+ /* use top-level keys if we have some */
+ keys = topLevelKeys;
+ else
+ /* just take the first part */
+ keys = [NSArray arrayWithObject: [keys objectAtIndex: 0]];
+ }
+
+ parts = [self fetchPlainTextStrings: keys];
+ contentForReply = [self contentForReplyOnParts: parts
+ keys: keys];
+ }
+ else
+ contentForReply = nil;
+
+ return contentForReply;
+}
+
+- (NSString *) filenameForForward
+{
+ NSString *subject;
+ NSMutableString *newSubject;
+ static NSString *sescape[] = {
+ @"/", @"..", @"~", @"\"", @"'", @" ", @".", nil
+ };
+ unsigned int count, length;
+
+ subject = [[self envelope] subject];
+ length = [subject length];
+ if (!length)
+ {
+ subject = @"forward";
+ length = [subject length];
+ }
+
+ if (length > maxFilenameLength)
+ length = maxFilenameLength;
+ newSubject = [NSMutableString
+ stringWithString: [subject substringToIndex: length]];
+ count = 0;
+ while (sescape[count])
+ {
+ [newSubject replaceString: sescape[count]
+ withString: @"_"];
+ count++;
+ }
+ [newSubject appendString: @".eml"];
+
+ return newSubject;
+}
+
+- (NSString *) subjectForForward
+{
+ NSString *subject, *newSubject;
+
+ subject = [[self envelope] subject];
+ if ([subject length] > 0)
+ newSubject = [NSString stringWithFormat: @"[Fwd: %@]", subject];
+ else
+ newSubject = subject;
+
+ return newSubject;
+}
+
+@end
/* deletion */
- (BOOL)isDeletionAllowed;
-- (NSException *)trashInContext:(id)_ctx;
+- (NSException *) trashInContext:(id)_ctx;
- (NSException *) moveToFolderNamed: (NSString *) folderName
inContext: (id)_ctx;
NSLog(@"Note(SOGoMailObject): etag caching disabled!");
}
-- (void)dealloc {
+- (void) dealloc
+{
[headers release];
[headerPart release];
[coreInfos release];
return [nameInContainer stringByDeletingPathExtension];
}
-- (NSMutableString *) imap4URLString
-{
- NSMutableString *urlString;
- NSString *imap4Name;
-
- urlString = [container imap4URLString];
- imap4Name = [[self relativeImap4Name] stringByEscapingURL];
- [urlString appendFormat: @"%@", imap4Name];
-
- return urlString;
-}
-
/* hierarchy */
- (SOGoMailObject *)mailObject {
/* part hierarchy */
-- (NSString *)keyExtensionForPart:(id)_partInfo {
+- (NSString *) keyExtensionForPart: (id) _partInfo
+{
NSString *mt, *st;
if (_partInfo == nil)
/* message */
-- (id)fetchParts:(NSArray *)_parts {
+- (id) fetchParts: (NSArray *) _parts
+{
// TODO: explain what it does
/*
Called by -fetchPlainTextParts:
return [msgs count] > 0 ? YES : NO;
}
-- (id)fetchCoreInfos {
+- (id) fetchCoreInfos
+{
id msgs;
if (coreInfos != nil)
return nil;
coreInfos = [[msgs objectAtIndex:0] retain];
+
return coreInfos;
}
-- (id)bodyStructure {
+- (id) bodyStructure
+{
id body;
body = [[self fetchCoreInfos] valueForKey: @"body"];
if (debugBodyStructure)
[self logWithFormat: @"BODY: %@", body];
+
return body;
}
-- (NGImap4Envelope *)envelope {
+- (NGImap4Envelope *) envelope
+{
return [[self fetchCoreInfos] valueForKey: @"envelope"];
}
/* bulk fetching of plain/text content */
-- (BOOL)shouldFetchPartOfType:(NSString *)_type subtype:(NSString *)_subtype {
+- (BOOL) shouldFetchPartOfType: (NSString *) _type
+ subtype: (NSString *) _subtype
+{
/*
This method decides which parts are 'prefetched' for display. Those are
usually text parts (the set is currently hardcoded in this method ...).
|| [_subtype hasPrefix: @"x-vnd.kolab."])));
}
-- (void)addRequiredKeysOfStructure:(id)_info path:(NSString *)_p
- toArray:(NSMutableArray *)_keys
- recurse:(BOOL)_recurse
+- (void) addRequiredKeysOfStructure: (id) _info
+ path: (NSString *) _p
+ toArray: (NSMutableArray *) _keys
+ recurse: (BOOL) _recurse
{
/*
This is used to collect the set of IMAP4 fetch-keys required to fetch
The method calls itself recursively to walk the body structure.
*/
- NSArray *parts;
+ NSArray *parts;
unsigned i, count;
BOOL fetchPart;
+ NSString *k;
id body;
-
- /* Note: if the part itself doesn't qualify, we still check subparts */
- fetchPart = [self shouldFetchPartOfType:[_info valueForKey: @"type"]
- subtype:[_info valueForKey: @"subtype"]];
- if (fetchPart) {
- NSString *k;
+ NSString *sp;
+ id childInfo;
- if ([_p length] > 0) {
- k = [[@"body[" stringByAppendingString:_p] stringByAppendingString: @"]"];
- }
- else {
- /*
- for some reason we need to add ".TEXT" for plain text stuff on root
- entities?
- TODO: check with HTML
- */
- k = @"body[text]";
+
+ /* Note: if the part itself doesn't qualify, we still check subparts */
+ fetchPart = [self shouldFetchPartOfType: [_info valueForKey: @"type"]
+ subtype: [_info valueForKey: @"subtype"]];
+ if (fetchPart)
+ {
+ if ([_p length] > 0)
+ k = [NSString stringWithFormat: @"body[%@]", _p];
+ else
+ {
+ /*
+ for some reason we need to add ".TEXT" for plain text stuff on root
+ entities?
+ TODO: check with HTML
+ */
+ k = @"body[text]";
+ }
+ [_keys addObject: k];
}
- [_keys addObject:k];
- }
-
- if (!_recurse)
- return;
-
- /* recurse */
-
- parts = [(NSDictionary *)_info objectForKey: @"parts"];
- for (i = 0, count = [parts count]; i < count; i++) {
- NSString *sp;
- id childInfo;
-
- sp = ([_p length] > 0)
- ? [_p stringByAppendingFormat: @".%d", i + 1]
- : [NSString stringWithFormat: @"%d", i + 1];
-
- childInfo = [parts objectAtIndex:i];
-
- [self addRequiredKeysOfStructure:childInfo path:sp toArray:_keys
- recurse:YES];
- }
-
- /* check body */
-
- if ((body = [(NSDictionary *)_info objectForKey: @"body"]) != nil) {
- NSString *sp;
- sp = [[body valueForKey: @"type"] lowercaseString];
- if ([sp isEqualToString: @"multipart"])
- sp = _p;
- else
- sp = [_p length] > 0 ? [_p stringByAppendingString: @".1"] : @"1";
- [self addRequiredKeysOfStructure:body path:sp toArray:_keys
- recurse:YES];
- }
+ if (_recurse)
+ {
+ /* recurse */
+ parts = [(NSDictionary *)_info objectForKey: @"parts"];
+ count = [parts count];
+ for (i = 0; i < count; i++)
+ {
+ sp = (([_p length] > 0)
+ ? [_p stringByAppendingFormat: @".%d", i + 1]
+ : [NSString stringWithFormat: @"%d", i + 1]);
+
+ childInfo = [parts objectAtIndex: i];
+
+ [self addRequiredKeysOfStructure: childInfo
+ path: sp toArray: _keys
+ recurse: YES];
+ }
+
+ /* check body */
+ body = [(NSDictionary *)_info objectForKey: @"body"];
+ if (body)
+ {
+ sp = [[body valueForKey: @"type"] lowercaseString];
+ if ([sp isEqualToString: @"multipart"])
+ sp = _p;
+ else
+ sp = [_p length] > 0 ? [_p stringByAppendingString: @".1"] : @"1";
+ [self addRequiredKeysOfStructure: body
+ path: sp toArray: _keys
+ recurse: YES];
+ }
+ }
}
-- (NSArray *)plainTextContentFetchKeys {
+- (NSArray *) plainTextContentFetchKeys
+{
/*
The name is not 100% correct. The method returns all body structure fetch
keys which are marked by the -shouldFetchPartOfType:subtype: method.
NSMutableArray *ma;
ma = [NSMutableArray arrayWithCapacity:4];
- [self addRequiredKeysOfStructure:[[self clientObject] bodyStructure]
- path: @"" toArray:ma recurse:YES];
+ [self addRequiredKeysOfStructure: [[self clientObject] bodyStructure]
+ path: @"" toArray: ma recurse: YES];
return ma;
}
/* b) mark deleted */
- error = [[self imap4Connection] markURLDeleted:[self imap4URL]];
+ error = [[self imap4Connection] markURLDeleted: [self imap4URL]];
if (error != nil) return error;
/* c) expunge */
+++ /dev/null
-/*
- Copyright (C) 2005 SKYRIX Software AG
-
- This file is part of OpenGroupware.org.
-
- OGo is free software; you can redistribute it and/or modify it under
- the terms of the GNU Lesser General Public License as published by the
- Free Software Foundation; either version 2, or (at your option) any
- later version.
-
- OGo 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 Lesser General Public
- License for more details.
-
- You should have received a copy of the GNU Lesser General Public
- License along with OGo; see the file COPYING. If not, write to the
- Free Software Foundation, 59 Temple Place - Suite 330, Boston, MA
- 02111-1307, USA.
-*/
-
-#ifndef __Mailer_SOGoUser_Mail_H__
-#define __Mailer_SOGoUser_Mail_H__
-
-#include <SoObjects/Mailer/SOGoMailBaseObject.h>
-
-/*
- SOGoUser(Mail)
-
- TODO: document
-
- This category adds mail related stuff to the SOGo user class.
-*/
-
-#include <SOGo/SOGoUser.h>
-
-@class NSArray;
-@class SOGoMailIdentity;
-
-@interface SOGoUser(Mail)
-
-- (SOGoMailIdentity *)primaryMailIdentity;
-- (NSArray *)fetchAllMailIdentitiesWithOnlyEmitterAccess:(BOOL)_onlyGC;
-- (SOGoMailIdentity *)primaryMailIdentityForAccount:(NSString *)_account;
-
-@end
-
-#endif /* __Mailer_SOGoUser_Mail_H__ */
+++ /dev/null
-/*
- Copyright (C) 2004-2005 SKYRIX Software AG
-
- This file is part of OpenGroupware.org.
-
- OGo is free software; you can redistribute it and/or modify it under
- the terms of the GNU Lesser General Public License as published by the
- Free Software Foundation; either version 2, or (at your option) any
- later version.
-
- OGo 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 Lesser General Public
- License for more details.
-
- You should have received a copy of the GNU Lesser General Public
- License along with OGo; see the file COPYING. If not, write to the
- Free Software Foundation, 59 Temple Place - Suite 330, Boston, MA
- 02111-1307, USA.
-*/
-
-#import <Foundation/NSArray.h>
-#import <Foundation/NSUserDefaults.h>
-#import <Foundation/NSKeyValueCoding.h>
-
-#import <NGExtensions/NSNull+misc.h>
-#import <NGExtensions/NSObject+Logs.h>
-
-#import "SOGoMailIdentity.h"
-
-#import "SOGoUser+Mail.h"
-
-@implementation SOGoUser(Mail)
-
-- (NSString *)agenorSentFolderName {
- /* Note: specialty: the Sent folder is called the same in all accounts */
- static NSString *s = nil;
- if (s == nil) {
- NSUserDefaults *ud;
-
- ud = [NSUserDefaults standardUserDefaults];
- s = [[ud stringForKey:@"SOGoSentFolderName"] copy];
- if (![s isNotEmpty]) s = @"Sent";
- [self logWithFormat:@"Note: using SOGoSentFolderName: '%@'", s];
- }
- return s;
-}
-
-- (NSString *)agenorSentFolderForAccount:(NSString *)_account {
- // TODO: support different locations for shares!
- NSString *p;
-
- if (![_account isNotEmpty])
- return nil;
-
- // if ([_account rangeOfString:@".-."].length == 0)
- // TODO: check whether we need special handling for shares!
- p = [_account stringByAppendingString:@"/"];
- p = [p stringByAppendingString:[self agenorSentFolderName]];
- return p;
-}
-
-- (SOGoMailIdentity *)primaryMailIdentity {
- SOGoMailIdentity *identity;
- NSString *account;
-
- account = [self valueForKey:@"primaryIMAP4AccountString"];
-
- identity = [[[SOGoMailIdentity alloc] init] autorelease];
- [identity setName: [self cn]];
- [identity setEmail: [self primaryEmail]];
- [identity setSentFolderName:[self agenorSentFolderForAccount:account]];
- return identity;
-}
-
-- (SOGoMailIdentity *)mailIdentityForAccount:(NSString *)_account
- emitter:(NSString *)_em
-{
- SOGoMailIdentity *identity;
-
- identity = [[[SOGoMailIdentity alloc] init] autorelease];
- [identity setName:[self cn]]; // TODO: should we use something else?
- if ([_em isNotEmpty]) [identity setEmail:_em];
- [identity setSentFolderName:[self agenorSentFolderForAccount:_account]];
- return identity;
-}
-
-- (NSArray *)fetchAllMailIdentitiesWithOnlyEmitterAccess:(BOOL)_onlyGC {
- NSMutableArray *identities;
- NSEnumerator *accounts;
- NSDictionary *shares;
- NSString *account;
- id identity;
-
- identity = [self primaryMailIdentity];
- shares = [self valueForKey:@"additionalIMAP4AccountsAndEMails"];
- if ([shares count] == 0)
- return [NSArray arrayWithObject: identity];
-
- identities = [NSMutableArray arrayWithCapacity:[shares count] + 1];
- if (identity != nil) [identities addObject:identity];
-
- accounts = [shares keyEnumerator];
- while ((account = [accounts nextObject]) != nil) {
- NSString *emitter;
-
- emitter = [shares objectForKey:account];
- if (_onlyGC && ![emitter isNotNull]) continue;
-
- identity = [self mailIdentityForAccount:account emitter:emitter];
- if (identity != nil)
- [identities addObject:identity];
- }
-
- return identities;
-}
-
-- (SOGoMailIdentity *)primaryMailIdentityForAccount:(NSString *)_account {
- NSEnumerator *accounts;
- NSDictionary *shares;
- NSString *account;
- id identity;
-
- identity = [self primaryMailIdentity];
- shares = [self valueForKey:@"additionalIMAP4AccountsAndEMails"];
- if ([shares count] == 0)
- return identity;
-
- /* scan shares for ID */
- accounts = [shares keyEnumerator];
- while ((account = [accounts nextObject]) != nil) {
- NSString *emitter;
-
- if (![account isEqualToString:_account])
- continue;
-
- emitter = [shares objectForKey:account];
- identity = [self mailIdentityForAccount:_account emitter:emitter];
- if ([identity isNotNull])
- return identity;
- }
-
- return identity;
-}
-
-@end /* SOGoUser(Mail) */
- (NSString *) jsonRepresentation;
- (NSArray *) stringsWithFormat: (NSString *) format;
+- (NSArray *) keysWithFormat: (NSString *) format;
+- (NSArray *) objectsForKey: (NSString *) key;
+- (NSArray *) flattenedArray;
- (BOOL) containsCaseInsensitiveString: (NSString *) match;
id currentObject;
formattedStrings = [NSMutableArray arrayWithCapacity: [self count]];
+
objects = [self objectEnumerator];
currentObject = [objects nextObject];
while (currentObject)
return formattedStrings;
}
+- (NSArray *) keysWithFormat: (NSString *) format
+{
+ NSMutableArray *formattedStrings;
+ NSEnumerator *objects;
+ id currentObject;
+
+ formattedStrings = [NSMutableArray arrayWithCapacity: [self count]];
+
+ objects = [self objectEnumerator];
+ currentObject = [objects nextObject];
+ while (currentObject)
+ {
+ [formattedStrings addObject: [currentObject keysWithFormat: format]];
+ currentObject = [objects nextObject];
+ }
+
+ return formattedStrings;
+}
+
+- (NSArray *) objectsForKey: (NSString *) key
+{
+ NSMutableArray *objectsForKey;
+ unsigned int count, max;
+ id value;
+
+ max = [self count];
+ objectsForKey = [NSMutableArray arrayWithCapacity: max];
+
+ for (count = 0; count < max; count++)
+ {
+ value = [[self objectAtIndex: count] objectForKey: key];
+ [objectsForKey addObject: value];
+ }
+
+ return objectsForKey;
+}
+
+- (NSArray *) flattenedArray
+{
+ NSMutableArray *flattenedArray;
+ NSEnumerator *objects;
+ id currentObject;
+
+ flattenedArray = [NSMutableArray array];
+ objects = [self objectEnumerator];
+ currentObject = [objects nextObject];
+ while (currentObject)
+ {
+ [flattenedArray addObjectsFromArray: currentObject];
+ currentObject = [objects nextObject];
+ }
+
+ return flattenedArray;
+}
+
- (void) makeObjectsPerform: (SEL) selector
withObject: (id) object1
withObject: (id) object2
@interface NSDictionary (SOGoDictionaryUtilities)
- (NSString *) jsonRepresentation;
+- (NSString *) keysWithFormat: (NSString *) keyFormat;
@end
#import <Foundation/NSArray.h>
#import <Foundation/NSString.h>
+#import "NSArray+Utilities.h"
#import "NSObject+Utilities.h"
#import "NSDictionary+Utilities.h"
return representation;
}
+- (NSString *) keysWithFormat: (NSString *) keyFormat
+{
+ NSArray *keys, *allKeys;
+ unsigned int count, max;
+ NSMutableString *keysWithFormat;
+ id value;
+
+ keysWithFormat = [NSMutableString stringWithString: keyFormat];
+
+ allKeys = [self allKeys];
+ keys = [allKeys stringsWithFormat: @"%{%@}"];
+
+ max = [allKeys count];
+ for (count = 0; count < max; count++)
+ {
+ value = [self objectForKey: [allKeys objectAtIndex: count]];
+ [keysWithFormat replaceString: [keys objectAtIndex: count]
+ withString: [value description]];
+ }
+
+ return keysWithFormat;
+}
+
@end
#import <stdlib.h>
#import <Foundation/NSArray.h>
+#import <Foundation/NSDate.h>
#import <Foundation/NSDictionary.h>
#import <Foundation/NSException.h>
+#import <Foundation/NSKeyValueCoding.h>
#import <Foundation/NSURL.h>
#import <NGObjWeb/SoObject.h>
static GCSFolderManager *folderManager = nil;
if (!folderManager)
- {
- folderManager = [GCSFolderManager defaultFolderManager];
- [folderManager setFolderNamePrefix: @"SOGo"];
- }
+ folderManager = [GCSFolderManager defaultFolderManager];
return folderManager;
}
+ (id) sharedMailer;
+- (NSException *) sendMailData: (NSData *) data
+ toRecipients: (NSArray *) recipients
+ sender: (NSString *) sender;
- (NSException *) sendMailAtPath: (NSString *) filename
toRecipients: (NSArray *) recipients
sender: (NSString *) sender;
-
- (NSException *) sendMimePart: (id <NGMimePart>) part
toRecipients: (NSArray *) recipients
sender: (NSString *) sender;
NSUserDefaults *userSettings;
NSTimeZone *userTimeZone;
SOGoDateFormatter *dateFormatter;
+ NSMutableArray *mailAccounts;
}
+ (SOGoUser *) userWithLogin: (NSString *) login
/* properties */
-- (NSString *) fullEmail;
+// - (NSString *) fullEmail;
-- (NSString *) primaryEmail;
-- (NSString *) systemEmail;
-- (NSArray *) allEmails;
+// - (NSString *) primaryEmail;
+// - (NSString *) systemEmail;
+// - (NSArray *) allEmails;
- (BOOL) hasEmail: (NSString *) email;
/* shares and identities */
-- (NSString *) primaryIMAP4AccountString;
+// - (NSString *) primaryIMAP4AccountString;
// - (NSString *) primaryIMAP4AccountString;
// - (NSString *) primaryMailServer;
- (NSTimeZone *) timeZone;
- (NSTimeZone *) serverTimeZone;
+- (NSArray *) mailAccounts;
+- (NSArray *) allIdentities;
+- (NSDictionary *) primaryIdentity;
+
/* folders */
- (id) homeFolderInContext: (id) _ctx;
#import <Foundation/NSNull.h>
#import <Foundation/NSTimeZone.h>
#import <Foundation/NSUserDefaults.h>
+#import <Foundation/NSValue.h>
#import <NGObjWeb/WOApplication.h>
#import <NGObjWeb/SoObject.h>
#import <NGExtensions/NSNull+misc.h>
/* properties */
-- (NSString *) fullEmail
-{
- return [[LDAPUserManager sharedUserManager] getFullEmailForUID: login];
-}
+// - (NSString *) fullEmail
+// {
+// return [[LDAPUserManager sharedUserManager] getFullEmailForUID: login];
+// }
-- (NSString *) primaryEmail
-{
- if (!allEmails)
- [self _fetchAllEmails];
+// - (NSString *) primaryEmail
+// {
+// if (!allEmails)
+// [self _fetchAllEmails];
- return [allEmails objectAtIndex: 0];
-}
+// return [allEmails objectAtIndex: 0];
+// }
-- (NSString *) systemEmail
+- (NSArray *) allEmails
{
if (!allEmails)
[self _fetchAllEmails];
- return [allEmails lastObject];
+ return allEmails;
}
-- (NSArray *) allEmails
+- (NSString *) systemEmail
{
if (!allEmails)
[self _fetchAllEmails];
- return allEmails;
+ return [allEmails lastObject];
}
- (BOOL) hasEmail: (NSString *) email
return cn;
}
-- (NSString *) primaryIMAP4AccountString
-{
- return [NSString stringWithFormat: @"%@@%@", login, fallbackIMAP4Server];
-}
-
// - (NSString *) primaryMailServer
// {
// return [[self userManager] getServerForUID: [self login]];
return serverTimeZone;
}
+/* mail */
+- (NSArray *) mailAccounts
+{
+#warning should be implemented with the user defaults interfaces
+ NSMutableDictionary *mailAccount, *identity;
+ NSMutableArray *identities;
+ NSString *name, *fullName;
+
+ if (!mailAccounts)
+ {
+ mailAccount = [NSMutableDictionary dictionary];
+ name = [NSString stringWithFormat: @"%@@%@", login, fallbackIMAP4Server];
+ [mailAccount setObject: login forKey: @"userName"];
+ [mailAccount setObject: fallbackIMAP4Server forKey: @"serverName"];
+ [mailAccount setObject: name forKey: @"name"];
+
+ identity = [NSMutableDictionary dictionary];
+ fullName = [self cn];
+ if (![fullName length])
+ fullName = login;
+ [identity setObject: fullName forKey: @"fullName"];
+ [identity setObject: [self systemEmail] forKey: @"email"];
+ [identity setObject: [NSNumber numberWithBool: YES] forKey: @"isDefault"];
+
+ identities = [NSMutableArray array];
+ [identities addObject: identity];
+ [mailAccount setObject: identities forKey: @"identities"];
+
+ mailAccounts = [NSMutableArray new];
+ [mailAccounts addObject: mailAccount];
+ }
+
+ return mailAccounts;
+}
+
+/*
+@interface SOGoMailIdentity : NSObject
+{
+ NSString *name;
+ NSString *email;
+ NSString *replyTo;
+ NSString *organization;
+ NSString *signature;
+ NSString *vCard;
+ NSString *sentFolderName;
+ NSString *sentBCC;
+ NSString *draftsFolderName;
+ NSString *templatesFolderName;
+ struct
+ {
+ int composeHTML:1;
+ int reserved:31;
+ } idFlags;
+}
+
+- (void) setName: (NSString *) _value;
+- (NSString *) name;
+
+- (void) setEmail: (NSString *) _value;
+- (NSString *) email;
+
+- (void) setReplyTo: (NSString *) _value;
+- (NSString *) replyTo;
+
+- (void) setOrganization: (NSString *) _value;
+- (NSString *) organization;
+
+- (void) setSignature: (NSString *) _value;
+- (NSString *) signature;
+- (BOOL) hasSignature;
+
+- (void) setVCard: (NSString *) _value;
+- (NSString *) vCard;
+- (BOOL) hasVCard;
+
+- (void) setSentFolderName: (NSString *) _value;
+- (NSString *) sentFolderName;
+
+- (void) setSentBCC: (NSString *) _value;
+- (NSString *) sentBCC;
+
+- (void) setDraftsFolderName: (NSString *) _value;
+- (NSString *) draftsFolderName;
+
+- (void) setTemplatesFolderName: (NSString *) _value;
+- (NSString *) templatesFolderName;
+
+@end */
+
+- (NSArray *) allIdentities
+{
+ NSArray *identities;
+
+ [self mailAccounts];
+ identities = [mailAccounts objectsForKey: @"identities"];
+
+ return [identities flattenedArray];
+}
+
+- (NSDictionary *) primaryIdentity
+{
+ NSDictionary *defaultAccount;
+
+ [self mailAccounts];
+ defaultAccount = [mailAccounts objectAtIndex: 0];
+
+ return [[defaultAccount objectForKey: @"identities"] objectAtIndex: 0];
+}
+
/* folders */
// TODO: those methods should check whether the traversal stack in the context
UIxTabItem.m \
UIxUserRightsEditor.m \
\
- UIxToolbar.m
+ UIxToolbar.m \
+ \
+ WODirectAction+SOGo.m \
CommonUI_RESOURCE_FILES += \
Version \
--- /dev/null
+/* WODirectAction+SOGo.h - this file is part of SOGo
+ *
+ * Copyright (C) 2007 Inverse groupe conseil
+ *
+ * Author: Wolfgang Sourdeau <wsourdeau@inverse.ca>
+ *
+ * This file is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This file is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; see the file COPYING. If not, write to
+ * the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#ifndef WODIRECTACTION_SOGO_H
+#define WODIRECTACTION_SOGO_H
+
+#import <NGObjWeb/WODirectAction.h>
+
+@class NSString;
+@class WOResponse;
+
+@interface WODirectAction (SOGoExtension)
+
+- (WOResponse *) redirectToLocation: (NSString *) newLocation;
+
+@end
+
+#endif /* WODIRECTACTION_SOGO_H */
--- /dev/null
+/* WODirectAction+SOGo.m - this file is part of SOGo
+ *
+ * Copyright (C) 2007 Inverse groupe conseil
+ *
+ * Author: Wolfgang Sourdeau <wsourdeau@inverse.ca>
+ *
+ * This file is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This file is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; see the file COPYING. If not, write to
+ * the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+
+#import <NGObjWeb/WOContext.h>
+#import <NGObjWeb/WOResponse.h>
+
+#import "WODirectAction+SOGo.h"
+
+@implementation WODirectAction (SOGoExtension)
+
+- (WOResponse *) redirectToLocation: (NSString *) newLocation
+{
+ WOResponse *response;
+
+ response = [context response];
+ [response setStatus: 302 /* moved */];
+ [response setHeader: newLocation forKey: @"location"];
+
+ return response;
+}
+
+@end
/* organizer tracking */
-- (NSString *)loggedInUserEMail {
- return [[[self context] activeUser] primaryEmail];
+- (NSString *) loggedInUserEMail
+{
+ NSDictionary *identity;
+
+ identity = [[context activeUser] primaryIdentity];
+
+ return [identity objectForKey: @"email"];
}
- (iCalEvent *)authorativeEvent {
"Addressbook" = "Addressbook";
"Anais" = "Anais";
+"Edit Draft..." = "Edit Draft...";
+
"This mail is being sent from an unsecure network!" = "This mail is being sent from an unsecure network!";
/* Popup "show" */
"cc" = "Cc";
"bcc" = "Cci";
+"Edit Draft..." = "Modifier le brouillon...";
+
"This mail is being sent from an unsecure network!" = "Ce mail est envoyé depuis un réseau non sécurisé !";
/* Popup "show" */
\
UIxMailAccountActions.m \
UIxMailFolderActions.m \
+ UIxMailActions.m \
UIxMailEditor.m \
- UIxMailEditorAction.m \
- UIxMailReplyAction.m \
- UIxMailForwardAction.m \
UIxMailToSelection.m \
UIxMailWindowCloser.m \
\
+ UIxMailUserRightsEditor.m \
+# UIxMailEditorAction.m \
+# UIxMailReplyAction.m \
+# UIxMailForwardAction.m \
# UIxFilterList.m \
# \
- UIxMailUserRightsEditor.m
# UIxSieveEditor.m
MailerUI_RESOURCE_FILES += \
{ link = "#";
isSafe = NO;
image = "tb-mail-write-flat-24x24.png";
- onclick = "return openMessageWindow(null, 'compose');";
- cssClass = "tbicon_compose"; label = "Write"; },
+ onclick = "return onComposeMessage();";
+ cssClass = "tbicon_compose"; label = "Write"; },
{ link = "#"; target = "addressbook";
onclick = "openAddressbook(this);return false;";
image = "tb-mail-addressbook-flat-24x24.png";
#import <Foundation/NSEnumerator.h>
#import <NGObjWeb/WOContext.h>
+#import <NGObjWeb/WORequest.h>
#import <NGObjWeb/WOResponse.h>
#import <NGImap4/NGImap4Connection.h>
#import <SoObjects/Mailer/SOGoMailAccount.h>
+#import <SoObjects/Mailer/SOGoDraftObject.h>
+#import <SoObjects/Mailer/SOGoDraftsFolder.h>
#import <SoObjects/SOGo/NSObject+Utilities.h>
+#import <SoObjects/SOGo/NSString+Utilities.h>
+
+#import "../Common/WODirectAction+SOGo.h"
#import "UIxMailAccountActions.h"
return response;
}
+/* compose */
+
+- (WOResponse *) composeAction
+{
+ SOGoDraftsFolder *drafts;
+ SOGoDraftObject *newDraftMessage;
+ NSString *urlBase, *url;
+ NSString *mailTo;
+
+ drafts = [[self clientObject] draftsFolderInContext: context];
+ newDraftMessage = [drafts newDraft];
+
+ mailTo = [[self request] formValueForKey: @"mailto"];
+ if ([mailTo length] > 0)
+ {
+ [newDraftMessage setHeaders: [NSDictionary dictionaryWithObject: mailTo
+ forKey: @"to"]];
+ [newDraftMessage storeInfo];
+ }
+
+ urlBase = [newDraftMessage baseURLInContext: context];
+ url = [urlBase composeURLWithAction: @"edit"
+ parameters: nil
+ andHash: NO];
+
+ return [self redirectToLocation: url];
+}
+
@end
--- /dev/null
+/* UIxMailActions.h - this file is part of SOGo
+ *
+ * Copyright (C) 2007 Inverse groupe conseil
+ *
+ * Author: Wolfgang Sourdeau <wsourdeau@inverse.ca>
+ *
+ * This file is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This file is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; see the file COPYING. If not, write to
+ * the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#ifndef UIXMAILACTIONS_H
+#define UIXMAILACTIONS_H
+
+#import <NGObjWeb/WODirectAction.h>
+
+@interface UIxMailActions : WODirectAction
+@end
+
+#endif /* UIXMAILACTIONS_H */
--- /dev/null
+/* UIxMailActions.m - this file is part of SOGo
+ *
+ * Copyright (C) 2007 Inverse groupe conseil
+ *
+ * Author: Wolfgang Sourdeau <wsourdeau@inverse.ca>
+ *
+ * This file is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This file is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; see the file COPYING. If not, write to
+ * the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#import <Foundation/NSString.h>
+
+#import <SoObjects/Mailer/SOGoDraftObject.h>
+#import <SoObjects/Mailer/SOGoDraftsFolder.h>
+#import <SoObjects/Mailer/SOGoMailAccount.h>
+#import <SoObjects/Mailer/SOGoMailObject.h>
+
+#import "../Common/WODirectAction+SOGo.h"
+
+#import "UIxMailActions.h"
+
+@implementation UIxMailActions
+
+- (WOResponse *) editAction
+{
+ SOGoMailAccount *account;
+ SOGoMailObject *co;
+ SOGoDraftsFolder *folder;
+ SOGoDraftObject *newMail;
+ NSString *newLocation;
+
+ co = [self clientObject];
+ account = [co mailAccountFolder];
+ folder = [account draftsFolderInContext: context];
+ newMail = [folder newDraft];
+ [newMail fetchMailForEditing: co];
+ [newMail storeInfo];
+
+ newLocation = [NSString stringWithFormat: @"%@/edit",
+ [newMail baseURLInContext: context]];
+
+ return [self redirectToLocation: newLocation];
+}
+
+- (WOResponse *) replyToAll: (BOOL) toAll
+{
+ SOGoMailAccount *account;
+ SOGoMailObject *co;
+ SOGoDraftsFolder *folder;
+ SOGoDraftObject *newMail;
+ NSString *newLocation;
+
+ co = [self clientObject];
+ account = [co mailAccountFolder];
+ folder = [account draftsFolderInContext: context];
+ newMail = [folder newDraft];
+ [newMail fetchMailForReplying: co toAll: toAll];
+
+ newLocation = [NSString stringWithFormat: @"%@/edit",
+ [newMail baseURLInContext: context]];
+
+ return [self redirectToLocation: newLocation];
+}
+
+- (WOResponse *) replyAction
+{
+ return [self replyToAll: NO];
+}
+
+- (WOResponse *) replyToAllAction
+{
+ return [self replyToAll: NO];
+}
+
+- (WOResponse *) forwardAction
+{
+ SOGoMailAccount *account;
+ SOGoMailObject *co;
+ SOGoDraftsFolder *folder;
+ SOGoDraftObject *newMail;
+ NSString *newLocation;
+
+ co = [self clientObject];
+ account = [co mailAccountFolder];
+ folder = [account draftsFolderInContext: context];
+ newMail = [folder newDraft];
+ [newMail fetchMailForForwarding: co];
+
+ newLocation = [NSString stringWithFormat: @"%@/edit",
+ [newMail baseURLInContext: context]];
+
+ return [self redirectToLocation: newLocation];
+}
+
+@end
#import <Foundation/NSString.h>
#import <Foundation/NSUserDefaults.h>
-#import <NGObjWeb/WORequest.h>
-#import <NGObjWeb/SoSubContext.h>
#import <NGObjWeb/NSException+HTTP.h>
+#import <NGObjWeb/SoSubContext.h>
+#import <NGObjWeb/WORequest.h>
+#import <NGObjWeb/WOResponse.h>
#import <NGExtensions/NSNull+misc.h>
#import <NGExtensions/NSObject+Logs.h>
#import <NGExtensions/NSString+misc.h>
#import <SoObjects/Mailer/SOGoMailFolder.h>
#import <SoObjects/Mailer/SOGoMailAccount.h>
#import <SoObjects/Mailer/SOGoMailAccounts.h>
-#import <SoObjects/Mailer/SOGoMailIdentity.h>
#import <SoObjects/SOGo/SOGoUser.h>
+#import <SoObjects/SOGo/NSArray+Utilities.h>
+#import <SoObjects/SOGo/NSDictionary+Utilities.h>
#import <SOGoUI/UIxComponent.h>
/*
An mail editor component which works on SOGoDraftObject's.
*/
-@class NSArray, NSString;
-@class SOGoMailFolder;
-
@interface UIxMailEditor : UIxComponent
{
NSArray *to;
NSArray *bcc;
NSString *subject;
NSString *text;
- NSMutableArray *fromEMails;
+ NSArray *fromEMails;
NSString *from;
SOGoMailFolder *sentFolder;
@implementation UIxMailEditor
-static BOOL keepMailTmpFile = NO;
-static BOOL showInternetMarker = NO;
-static BOOL useLocationBasedSentFolder = NO;
+static BOOL keepMailTmpFile = NO;
+static BOOL showInternetMarker = NO;
+static BOOL useLocationBasedSentFolder = NO;
static NSDictionary *internetMailHeaders = nil;
-static NSArray *infoKeys = nil;
+static NSArray *infoKeys = nil;
+ (void) initialize
{
NSUserDefaults *ud = [NSUserDefaults standardUserDefaults];
infoKeys = [[NSArray alloc] initWithObjects:
- @"subject", @"text", @"to", @"cc", @"bcc",
+ @"subject", @"to", @"cc", @"bcc",
@"from", @"replyTo",
nil];
{
[sentFolder release];
[fromEMails release];
- [from release];
- [text release];
+ [from release];
+ [text release];
[subject release];
- [to release];
- [cc release];
- [bcc release];
-
- [attachmentName release];
+ [to release];
+ [cc release];
+ [bcc release];
+ [attachmentName release];
[attachmentNames release];
[super dealloc];
}
/* accessors */
-- (void) setFrom: (NSString *) _value
+- (void) setFrom: (NSString *) newFrom
{
- ASSIGNCOPY(from, _value);
+ ASSIGN (from, newFrom);
}
- (NSString *) from
{
- if (![from isNotEmpty])
- return [[[self context] activeUser] primaryEmail];
+ NSDictionary *identity;
+
+ if (!from)
+ {
+ identity = [[context activeUser] primaryIdentity];
+ from = [identity keysWithFormat: @"%{fullName} <%{email}>"];
+ }
return from;
}
-- (void) setReplyTo: (NSString *) _ignore
-{
-}
+// - (void) setReplyTo: (NSString *) ignore
+// {
+// }
-- (NSString *) replyTo
-{
- /* we are here for future extensibility */
- return @"";
-}
+// - (NSString *) replyTo
+// {
+// /* we are here for future extensibility */
+// return @"";
+// }
-- (void) setSubject: (NSString *) _value
+- (void) setSubject: (NSString *) newSubject
{
- ASSIGNCOPY(subject, _value);
+ ASSIGN (subject, newSubject);
}
- (NSString *) subject
{
- return subject ? subject : @"";
+ return subject;
}
-- (void) setText: (NSString *) _value
+- (void) setText: (NSString *) newText
{
- ASSIGNCOPY(text, _value);
+ ASSIGN (text, newText);
}
- (NSString *) text
{
- return [text isNotNull] ? text : @"";
+ return text;
}
-- (void) setTo: (NSArray *)_value
+- (void) setTo: (NSArray *) newTo
{
- ASSIGNCOPY(to, _value);
+ if ([newTo isKindOfClass: [NSNull class]])
+ newTo = nil;
+
+ ASSIGN (to, newTo);
}
- (NSArray *) to
{
- return [to isNotNull] ? to : [NSArray array];
+ return to;
}
-- (void) setCc: (NSArray *) _value
+- (void) setCc: (NSArray *) newCc
{
- ASSIGNCOPY(cc, _value);
+ if ([newCc isKindOfClass: [NSNull class]])
+ newCc = nil;
+
+ ASSIGN (cc, newCc);
}
- (NSArray *) cc
{
- return [cc isNotNull] ? cc : [NSArray array];
+ return cc;
}
-- (void) setBcc: (NSArray *) _value
+- (void) setBcc: (NSArray *) newBcc
{
- ASSIGNCOPY(bcc, _value);
+ if ([newBcc isKindOfClass: [NSNull class]])
+ newBcc = nil;
+
+ ASSIGN (bcc, newBcc);
}
- (NSArray *) bcc
{
- return [bcc isNotNull] ? bcc : [NSArray array];
+ return bcc;
}
- (BOOL) hasOneOrMoreRecipients
{
- if ([[self to] count] > 0) return YES;
- if ([[self cc] count] > 0) return YES;
- if ([[self bcc] count] > 0) return YES;
- return NO;
+ return (([to count] + [cc count] + [bcc count]) > 0);
}
-- (void) setAttachmentName: (NSString *) _attachmentName
+- (void) setAttachmentName: (NSString *) newAttachmentName
{
- ASSIGN(attachmentName, _attachmentName);
+ ASSIGN (attachmentName, newAttachmentName);
}
- (NSString *) attachmentName
- (NSArray *) fromEMails
{
- NSEnumerator *emails;
- SOGoUser *activeUser;
- NSString *cn, *fullMail, *email;
-
+ NSArray *allIdentities;
+
if (!fromEMails)
{
- fromEMails = [NSMutableArray new];
- activeUser = [context activeUser];
- cn = [activeUser cn];
- if ([cn length] == 0)
- cn = nil;
- emails = [[activeUser allEmails] objectEnumerator];
- email = [emails nextObject];
- while (email)
- {
- if (cn)
- fullMail = [NSString stringWithFormat: @"%@ <%@>", cn, email];
- else
- fullMail = email;
- [fromEMails addObject: fullMail];
- email = [emails nextObject];
- }
+ allIdentities = [[context activeUser] allIdentities];
+ fromEMails = [allIdentities keysWithFormat: @"%{fullName} <%{email}>"];
+ [fromEMails retain];
}
return fromEMails;
}
-/* title */
-
-- (NSString *)panelTitle {
- return [self labelForKey:@"Compose Mail"];
-}
-
/* info loading */
-- (void)loadInfo:(NSDictionary *)_info {
+- (void) loadInfo: (NSDictionary *) _info
+{
if (![_info isNotNull]) return;
[self debugWithFormat:@"loading info ..."];
[self takeValuesFromDictionary:_info];
}
-- (NSDictionary *)storeInfo {
+
+- (NSDictionary *) storeInfo
+{
[self debugWithFormat:@"storing info ..."];
return [self valuesForKeys:infoKeys];
}
/* requests */
-- (BOOL) shouldTakeValuesFromRequest: (WORequest *) _rq
- inContext: (WOContext*) _c
+- (BOOL) shouldTakeValuesFromRequest: (WORequest *) request
+ inContext: (WOContext*) localContext
{
return YES;
}
-/* IMAP4 store */
-
-- (NSException *) patchFlagsInStore
-{
- /*
- Flags we should set:
- if the draft is a reply => [message markAnswered]
- if the draft is a forward => [message addFlag:@"forwarded"]
-
- This is hard, we would need to find the original message in Cyrus.
- */
- return nil;
-}
-
-- (id) lookupSentFolderUsingAccount
-{
- SOGoMailAccount *account;
- SOGoMailFolder *folder;
-
- if (sentFolder != nil)
- return [sentFolder isNotNull] ? sentFolder : nil;;
-
- account = [[self clientObject] mailAccountFolder];
- if ([account isKindOfClass:[NSException class]]) return account;
-
- folder = [account sentFolderInContext:[self context]];
- if ([folder isKindOfClass:[NSException class]]) return folder;
- return ((sentFolder = [folder retain]));
-}
-
-- (void) _presetFromBasedOnAccountsQueryParameter
-{
- /* preset the from field to the primary identity of the given account */
- /* Note: The compose action sets the 'accounts' query parameter */
- NSString *accountID;
- SOGoMailAccounts *accounts;
- SOGoMailAccount *account;
- SOGoMailIdentity *identity;
-
- if (useLocationBasedSentFolder) /* from will be based on location */
- return;
-
- if ([from isNotEmpty]) /* a from is already set */
- return;
-
- accountID = [[[self context] request] formValueForKey:@"account"];
- if (![accountID isNotEmpty])
- return;
-
- accounts = [[self clientObject] mailAccountsFolder];
- if ([accounts isExceptionOrNull])
- return; /* we don't treat this as an error but are tolerant */
-
- account = [accounts lookupName:accountID inContext:[self context]
- acquire:NO];
- if ([account isExceptionOrNull])
- return; /* we don't treat this as an error but are tolerant */
-
- identity = [account valueForKey:@"preferredIdentity"];
- if (![identity isNotNull]) {
- [self warnWithFormat:@"Account has no preferred identity: %@", account];
- return;
- }
-
- [self setFrom: [identity email]];
-}
-
-- (SOGoMailIdentity *) selectedMailIdentity
-{
- SOGoMailAccounts *accounts;
- NSEnumerator *e;
- SOGoMailIdentity *identity;
-
- accounts = [[self clientObject] mailAccountsFolder];
- if ([accounts isExceptionOrNull]) return (id)accounts;
-
- // TODO: This is still a hack because we detect the identity based on the
- // from. In Agenor all of the identities have unique emails, but this
- // is not required for SOGo.
-
- if ([[self from] length] == 0)
- return nil;
-
- e = [[accounts fetchIdentitiesWithEmitterPermissions] objectEnumerator];
- while ((identity = [e nextObject]) != nil) {
- if ([[identity email] isEqualToString:[self from]])
- return identity;
- }
- return nil;
-}
-
-- (id) lookupSentFolderUsingFrom
-{
- // TODO: if we have the identity we could also support BCC
- SOGoMailAccounts *accounts;
- SOGoMailIdentity *identity;
- SoSubContext *ctx;
- NSString *sentFolderName;
- NSArray *sentFolderPath;
- NSException *error = nil;
-
- if (sentFolder != nil)
- return [sentFolder isNotNull] ? sentFolder : nil;;
-
- identity = [self selectedMailIdentity];
- if ([identity isKindOfClass:[NSException class]]) return identity;
-
- if (![(sentFolderName = [identity sentFolderName]) isNotEmpty]) {
- [self warnWithFormat:@"Identity has no sent folder name: %@", identity];
- return nil;
- }
-
- // TODO: fixme, we treat the foldername as a hardcoded path from SOGoAccounts
- // TODO: escaping of foldernames with slashes
- // TODO: maybe the SOGoMailIdentity should have an 'account-identifier'
- // which is used to lookup the account and _then_ perform an account
- // local folder lookup? => would not be possible to have identities
- // saving to different accounts.
- sentFolderPath = [sentFolderName componentsSeparatedByString:@"/"];
-
- accounts = [[self clientObject] mailAccountsFolder];
- if ([accounts isKindOfClass:[NSException class]]) return (id)accounts;
-
- ctx = [[SoSubContext alloc] initWithParentContext:[self context]];
-
- sentFolder = [[accounts traversePathArray:sentFolderPath
- inContext:ctx error:&error
- acquire:NO] retain];
- [ctx release]; ctx = nil;
- if (error != nil) {
- [self errorWithFormat:@"Sent-Folder lookup for identity %@ failed: %@",
- identity, sentFolderPath];
- return error;
- }
-
-#if 0
- [self logWithFormat:@"Sent-Folder: %@", sentFolderName];
- [self logWithFormat:@" object: %@", sentFolder];
-#endif
- return sentFolder;
-}
-
-- (NSException *) storeMailInSentFolder: (NSString *) _path
-{
- SOGoMailFolder *folder;
- NSData *data;
- id result;
-
- folder = useLocationBasedSentFolder
- ? [self lookupSentFolderUsingAccount]
- : [self lookupSentFolderUsingFrom];
- if ([folder isKindOfClass:[NSException class]]) return (id)folder;
- if (folder == nil) return nil;
-
- if ((data = [[NSData alloc] initWithContentsOfMappedFile:_path]) == nil) {
- return [NSException exceptionWithHTTPStatus:500 /* server error */
- reason:@"could not find temporary draft file!"];
- }
-
- result = [folder postData:data flags:@"seen"];
- [data release]; data = nil;
- return result;
-}
-
/* actions */
- (NSDictionary *) _scanAttachmentFilenamesInRequest: (id) httpBody
for (count = 0; count < max; count++)
{
part = [parts objectAtIndex: count];
- header = [part headerForKey: @"content-disposition"];
- mimeType = [[part headerForKey: @"content-type"] stringValue];
+ header = (NGMimeContentDispositionHeaderField *) [part headerForKey: @"content-disposition"];
+ mimeType = [(NGMimeType *) [part headerForKey: @"content-type"] stringValue];
attachment = [NSDictionary dictionaryWithObjectsAndKeys:
[header filename], @"filename",
mimeType, @"mime-type", nil];
NSDictionary *info;
NSException *error;
BOOL success;
+ SOGoDraftObject *co;
+
+ co = [self clientObject];
+ [co fetchInfo];
success = YES;
if ([self _saveAttachments])
{
info = [self storeInfo];
- if (info)
+ [co setHeaders: info];
+ [co setText: text];
+ error = [co storeInfo];
+ if (error)
{
- error = [[self clientObject] storeInfo:info];
- if (error)
- {
- [self errorWithFormat:@"failed to store draft: %@", error];
- // TODO: improve error handling
- success = NO;
- }
+ [self errorWithFormat: @"failed to store draft: %@", error];
+ // TODO: improve error handling
+ success = NO;
}
}
else
success = NO;
-
+
// TODO: wrap content
return success;
- (NSArray *) attachmentNames
{
NSArray *a;
-
+
if (attachmentNames != nil)
return attachmentNames;
-
+
a = [[self clientObject] fetchAttachmentNames];
- a = [a sortedArrayUsingSelector:@selector(compare:)];
+ a = [a sortedArrayUsingSelector: @selector (compare:)];
attachmentNames = [a copy];
+
return attachmentNames;
}
- (id) defaultAction
{
-#if 0
- [self logWithFormat:@"edit action, load content from: %@",
- [self clientObject]];
-#endif
-
- [self loadInfo:[[self clientObject] fetchInfo]];
- [self _presetFromBasedOnAccountsQueryParameter];
+ SOGoDraftObject *co;
+
+ co = [self clientObject];
+ [co fetchInfo];
+ [self loadInfo: [co headers]];
+ [self setText: [co text]];
+
return self;
}
-- (id) saveAction
+- (id <WOActionResults>) saveAction
{
- return [self _saveFormInfo] ? self : [self failedToSaveFormResponse];
+ id result;
+
+ if ([self _saveFormInfo])
+ {
+ result = [[self clientObject] save];
+ if (!result)
+ {
+ result = [context response];
+ [result setStatus: 204];
+ }
+ }
+ else
+ result = [self failedToSaveFormResponse];
+
+ return result;
}
- (NSException *) validateForSend
{
- // TODO: localize errors
-
- if (![self hasOneOrMoreRecipients]) {
- return [NSException exceptionWithHTTPStatus:400 /* Bad Request */
- reason:@"Please select a recipient!"];
- }
- if ([[self subject] length] == 0) {
- return [NSException exceptionWithHTTPStatus:400 /* Bad Request */
- reason:@"Please set a subject!"];
- }
+ NSException *error;
+
+ if (![self hasOneOrMoreRecipients])
+ error = [NSException exceptionWithHTTPStatus: 400 /* Bad Request */
+ reason: @"Please select a recipient!"];
+ else if ([[self subject] length] == 0)
+ error = [NSException exceptionWithHTTPStatus: 400 /* Bad Request */
+ reason: @"Please set a subject!"];
+ else
+ error = nil;
- return nil;
+ return error;
}
- (id <WOActionResults>) sendAction
{
- NSException *error;
- NSString *mailPath;
id <WOActionResults> result;
// TODO: need to validate whether we have a To etc
/* first, save form data */
-
- if (![self _saveFormInfo])
- return [self failedToSaveFormResponse];
-
- /* validate for send */
-
- if ((error = [self validateForSend]) != nil) {
- id url;
-
- url = [[error reason] stringByEscapingURL];
- url = [@"edit?error=" stringByAppendingString:url];
- return [self redirectToLocation:url];
- }
-
- /* setup some extra headers if required */
-
- /* save mail to file (so that we can upload the mail to Cyrus) */
- // TODO: all this could be handled by the SOGoDraftObject?
-
- mailPath = [[self clientObject] saveMimeMessageToTemporaryFileWithHeaders: internetMailHeaders];
-
- /* then, send mail */
-
- if ((error = [[self clientObject] sendMimeMessageAtPath:mailPath]) != nil) {
- // TODO: improve error handling
- [[NSFileManager defaultManager] removeFileAtPath:mailPath handler:nil];
- return error;
- }
-
- /* patch flags in store for replies etc */
-
- if ((error = [self patchFlagsInStore]) != nil)
- return error;
-
- /* finally store in Sent */
-
- if ((error = [self storeMailInSentFolder:mailPath]) != nil)
- return error;
-
- /* delete temporary mail file */
-
- if (keepMailTmpFile)
- [self warnWithFormat:@"keeping mail file: '%@'", mailPath];
- else
- [[NSFileManager defaultManager] removeFileAtPath:mailPath handler:nil];
- mailPath = nil;
-
- /* delete draft */
-
- if ((error = [[self clientObject] delete]) != nil)
- return error;
-
- if ([[[[self context] request] formValueForKey: @"nojs"] intValue])
- result = [self redirectToLocation: [self applicationPath]];
- else
- result = [self jsCloseWithRefreshMethod: nil];
+ result = [self validateForSend];
+ if (!result)
+ {
+ if ([self _saveFormInfo])
+ {
+ result = [[self clientObject] sendMail];
+ if (!result)
+ result = [self jsCloseWithRefreshMethod: nil];
+ }
+ else
+ result = [self failedToSaveFormResponse];
+ }
return result;
}
-- (id) deleteAction
-{
- NSException *error;
- id page;
-
- if ((error = [[self clientObject] delete]) != nil) {
- /* Note: we ignore 404: those are drafts which were not yet saved */
- if (![error httpStatus] == 404)
- return error;
- }
-
-#if 1
- page = [self pageWithName:@"UIxMailWindowCloser"];
- [page takeValue:@"YES" forKey:@"refreshOpener"];
- return page;
-#else
- // TODO: if we just return nil, we produce a 500
- return [NSException exceptionWithHTTPStatus:204 /* No Content */
- reason:@"object was deleted."];
-#endif
-}
-
@end /* UIxMailEditor */
+++ /dev/null
-/*
- Copyright (C) 2004-2005 SKYRIX Software AG
-
- This file is part of OpenGroupware.org.
-
- OGo is free software; you can redistribute it and/or modify it under
- the terms of the GNU Lesser General Public License as published by the
- Free Software Foundation; either version 2, or (at your option) any
- later version.
-
- OGo 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 Lesser General Public
- License for more details.
-
- You should have received a copy of the GNU Lesser General Public
- License along with OGo; see the file COPYING. If not, write to the
- Free Software Foundation, 59 Temple Place - Suite 330, Boston, MA
- 02111-1307, USA.
-*/
-
-#ifndef __UIxMailEditorAction_H__
-#define __UIxMailEditorAction_H__
-
-#include <NGObjWeb/WODirectAction.h>
-
-/*
- UIxMailEditorAction
-
- This action implements the backend for the various buttons which invoke the
- mail editor. The mail editor itself only works on a SOGoDraftObject which
- needs to be created in advance.
-*/
-
-@class NSException;
-@class WOResponse;
-@class SOGoDraftObject, SOGoDraftsFolder;
-
-@interface UIxMailEditorAction : WODirectAction
-{
- SOGoDraftObject *newDraft;
-}
-
-/* errors */
-
-- (id)didNotFindDraftsError;
-- (id)couldNotCreateDraftError:(SOGoDraftsFolder *)_draftsFolder;
-- (id)didNotFindMailError;
-
-/* creating new draft object */
-
-- (NSException *)_setupNewDraft;
-- (WOResponse *)redirectToEditNewDraft;
-
-/* state */
-
-- (void)reset;
-
-@end
-
-#endif /* __UIxMailEditorAction_H__ */
+++ /dev/null
-/*
- Copyright (C) 2004-2005 SKYRIX Software AG
-
- This file is part of OpenGroupware.org.
-
- OGo is free software; you can redistribute it and/or modify it under
- the terms of the GNU Lesser General Public License as published by the
- Free Software Foundation; either version 2, or (at your option) any
- later version.
-
- OGo 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 Lesser General Public
- License for more details.
-
- You should have received a copy of the GNU Lesser General Public
- License along with OGo; see the file COPYING. If not, write to the
- Free Software Foundation, 59 Temple Place - Suite 330, Boston, MA
- 02111-1307, USA.
-*/
-
-#import <Foundation/NSDictionary.h>
-
-#import <NGObjWeb/WOContext.h>
-#import <NGObjWeb/WORequest.h>
-#import <NGObjWeb/WOResponse.h>
-#import <NGObjWeb/NSException+HTTP.h>
-#import <NGExtensions/NSNull+misc.h>
-#import <NGExtensions/NSException+misc.h>
-
-#import <SoObjects/SOGo/NSString+Utilities.h>
-#import <SoObjects/Mailer/SOGoDraftsFolder.h>
-#import <SoObjects/Mailer/SOGoDraftObject.h>
-#import <SoObjects/Mailer/SOGoMailAccount.h>
-#import <SoObjects/Mailer/SOGoMailObject.h>
-
-#import "UIxMailEditorAction.h"
-
-@implementation UIxMailEditorAction
-
-- (void)dealloc
-{
- [self->newDraft release];
- [super dealloc];
-}
-
-/* caches */
-
-- (void)reset {
- [self->newDraft release]; self->newDraft = nil;
-}
-
-/* lookups */
-
-- (SOGoDraftsFolder *)draftsFolder {
- /*
- Note: we cannot use acquisition to find the nearest drafts folder, because
- the IMAP4 server might contains an own Drafts folder.
- */
-// SOGoDraftsFolder *drafts;
- SOGoMailAccount *accountFolder;
-
- accountFolder = [[self clientObject] mailAccountFolder];
-
- return [accountFolder
- lookupName: [accountFolder draftsFolderNameInContext: context]
- inContext: context acquire: NO];
-}
-
-/* errors */
-
-- (id)didNotFindDraftsError {
- // TODO: make a nice error page
- return [@"did not find drafts folder in object: "
- stringByAppendingString:[[self clientObject] description]];
-}
-- (id)couldNotCreateDraftError:(SOGoDraftsFolder *)_draftsFolder {
- return [@"could not create a new draft in folder: "
- stringByAppendingString:[_draftsFolder description]];
-}
-- (id)didNotFindMailError {
- return [NSException exceptionWithHTTPStatus:404 /* Not Found */
- reason:@"Did not find mail for operation!"];
-}
-
-/* compose */
-
-- (id) composeAction
-{
- SOGoDraftsFolder *drafts;
- WOResponse *r;
- NSString *urlBase, *url;
- NSMutableDictionary *urlParams;
- id parameter;
- id returnValue;
-
- drafts = [self draftsFolder];
- if ([drafts isNotNull])
- {
- if ([drafts isKindOfClass: [NSException class]])
- returnValue = drafts;
- else
- {
- urlBase = [drafts newObjectBaseURLInContext: context];
- if ([urlBase isNotNull])
- {
- urlParams = [NSMutableDictionary new];
- [urlParams autorelease];
-
- /* attach mail-account info */
- parameter
- = [[self clientObject] valueForKey: @"mailAccountFolder"];
- if (parameter && ![parameter isExceptionOrNull])
- [urlParams setObject: [parameter nameInContainer]
- forKey: @"account"];
-
- parameter = [[self request] formValueForKey: @"mailto"];
- if (parameter)
- [urlParams setObject: parameter
- forKey: @"mailto"];
-
- url = [urlBase composeURLWithAction: @"edit"
- parameters: urlParams
- andHash: NO];
-
- /* perform redirect */
-
- [self debugWithFormat:@"compose on %@: %@", drafts, url];
-
- r = [context response];
- [r setStatus: 302 /* move d */];
- [r setHeader: url forKey: @"location"];
- [self reset];
-
- returnValue = r;
- }
- else
- returnValue = [self couldNotCreateDraftError: drafts];
- }
- }
- else
- returnValue = [self didNotFindDraftsError];
-
- return returnValue;
-}
-
-/* creating new draft object */
-
-- (id)newDraftObject {
- SOGoDraftsFolder *drafts;
-
- drafts = [self draftsFolder];
- if (![drafts isNotNull])
- return [self didNotFindDraftsError];
- if ([drafts isKindOfClass:[NSException class]])
- return drafts;
-
- return [drafts newObjectInContext:context];
-}
-
-- (NSException *)_setupNewDraft {
- SOGoDraftObject *tmp;
-
- /* create draft object */
-
- if ([(tmp = [self newDraftObject]) isKindOfClass:[NSException class]])
- return (NSException *)tmp;
- if (![tmp isNotNull]) { /* Note: should never happen? */
- [self logWithFormat:@"WARNING: got no new draft object and no error!"];
- return [self didNotFindDraftsError]; // TODO: not exact
- }
-
- ASSIGN(self->newDraft, tmp);
- //[self debugWithFormat:@"NEW DRAFT: %@", self->newDraft];
-
- return nil;
-}
-
-- (WOResponse *)redirectToEditNewDraft {
- WOResponse *r;
- NSString *url;
-
- if (![self->newDraft isNotNull]) {
- [self logWithFormat:@"ERROR(%s): missing new draft (already -reset?)",
- __PRETTY_FUNCTION__];
- return nil;
- }
-
- url = [self->newDraft baseURLInContext:context];
- if (![url hasSuffix:@"/"]) url = [url stringByAppendingString:@"/"];
- url = [url stringByAppendingString:@"edit"];
-
- // TODO: debug log
- [self logWithFormat:@"compose on %@", url];
-
- r = [context response];
- [r setStatus:302 /* moved */];
- [r setHeader:url forKey:@"location"];
- [self reset];
- return r;
-}
-
-@end /* UIxMailEditorAction */
@class WOResponse;
@interface UIxMailFolderActions : WODirectAction
-{
-}
- (WOResponse *) createFolderAction;
- (WOResponse *) renameFolderAction;
- (WOResponse *) deleteFolderAction;
+- (WOResponse *) expungeAction;
+- (WOResponse *) emptyTrashAction;
+- (WOResponse *) subscribeAction;
+- (WOResponse *) unsubscribeAction;
+- (WOResponse *) quotasAction;
@end
error = [connection createMailbox: folderName atURL: [co imap4URL]];
if (error)
{
- [response setStatus: 403];
+ [response setStatus: 500];
[response appendContentString: @"Unable to create folder."];
}
else
}
else
{
- [response setStatus: 403];
+ [response setStatus: 500];
[response appendContentString: @"Missing 'name' parameter."];
}
toURL: destURL];
if (error)
{
- [response setStatus: 403];
+ [response setStatus: 500];
[response appendContentString: @"Unable to rename folder."];
}
else
}
else
{
- [response setStatus: 403];
+ [response setStatus: 500];
[response appendContentString: @"Missing 'name' parameter."];
}
toURL: destURL];
if (error)
{
- [response setStatus: 403];
+ [response setStatus: 500];
[response appendContentString: @"Unable to move folder."];
}
else
return response;
}
+- (WOResponse *) expungeAction
+{
+ NSException *error;
+ SOGoTrashFolder *co;
+ WOResponse *response;
+
+ co = [self clientObject];
+ response = [context response];
+
+ error = [co expunge];
+ if (error)
+ {
+ [response setStatus: 500];
+ [response appendContentString: @"Unable to expunge folder."];
+ }
+ else
+ {
+ [co flushMailCaches];
+ [response setStatus: 204];
+ }
+
+ return response;
+}
+
- (WOResponse *) emptyTrashAction
{
NSException *error;
}
if (error)
{
- [response setStatus: 403];
+ [response setStatus: 500];
[response appendContentString: @"Unable to empty the trash folder."];
}
else
}
else
{
- [response setStatus: 403];
+ [response setStatus: 500];
[response appendContentString: @"How did you end up here?"];
}
+++ /dev/null
-/*
- Copyright (C) 2004-2005 SKYRIX Software AG
-
- This file is part of OpenGroupware.org.
-
- OGo is free software; you can redistribute it and/or modify it under
- the terms of the GNU Lesser General Public License as published by the
- Free Software Foundation; either version 2, or (at your option) any
- later version.
-
- OGo 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 Lesser General Public
- License for more details.
-
- You should have received a copy of the GNU Lesser General Public
- License along with OGo; see the file COPYING. If not, write to the
- Free Software Foundation, 59 Temple Place - Suite 330, Boston, MA
- 02111-1307, USA.
-*/
-
-#import <Foundation/NSDictionary.h>
-#import <Foundation/NSString.h>
-
-#import <NGExtensions/NSNull+misc.h>
-
-#import <SoObjects/Mailer/SOGoMailObject.h>
-#import <SoObjects/Mailer/SOGoDraftObject.h>
-
-#import "UIxMailEditorAction.h"
-
-@interface UIxMailForwardAction : UIxMailEditorAction
-@end
-
-
-@implementation UIxMailForwardAction
-
-- (NSString *)getAttachmentNameForSubject:(NSString *)_subject {
- /* SOGoDraftObject disallows some strings - anything else required? */
- static NSString *sescape[] = {
- @"/", @"..", @"~", @"\"", @"'", @" ", @".", nil
- };
- static int maxFilenameLength = 64;
- NSString *s;
- unsigned i;
-
- if (![_subject isNotNull] || [_subject length] == 0)
- return _subject;
- s = _subject;
-
- if ([s length] > maxFilenameLength)
- s = [s substringToIndex:maxFilenameLength];
-
- for (i = 0; sescape[i] != nil; i++)
- s = [s stringByReplacingString:sescape[i] withString:@"_"];
-
- return [s stringByAppendingString:@".mail"];
-}
-
-- (NSString *)forwardSubject:(NSString *)_subject {
- if (![_subject isNotNull] || [_subject length] == 0)
- return _subject;
-
- /* Note: this is how Thunderbird 1.0 creates the subject */
- _subject = [@"[Fwd: " stringByAppendingString:_subject];
- _subject = [_subject stringByAppendingString:@"]"];
- return _subject;
-}
-
-- (id)forwardAction {
- NSException *error;
- NSData *content;
- NSDictionary *info, *attachment;
- id result;
-
- /* fetch message */
-
- if ((content = [[self clientObject] content]) == nil)
- return [self didNotFindMailError];
- if ([content isKindOfClass:[NSException class]])
- return content;
-
- /* setup draft */
-
- if ((error = [self _setupNewDraft]) != nil)
- return error;
-
- /* set subject (do we need to set anything else?) */
-
- info = [NSDictionary dictionaryWithObjectsAndKeys:
- [self forwardSubject:[[self clientObject] subject]],
- @"subject",
- nil];
- if ((error = [newDraft storeInfo:info]) != nil)
- return error;
-
- /* attach message */
-
- // TODO: use subject for filename?
-// error = [newDraft saveAttachment:content withName:@"forward.mail"];
- attachment = [NSDictionary dictionaryWithObjectsAndKeys:
- @"forward.mail", @"filename",
- @"message/rfc822", @"mime-type",
- nil];
- error = [newDraft saveAttachment: content
- withMetadata: attachment];
- if (error != nil)
- return error;
-
- // TODO: we might want to pass the original URL to the editor for a final
- // redirect back to the message?
- result = [self redirectToEditNewDraft];
- [self reset];
- return result;
-}
-
-@end /* UIxMailForwardAction */
return [self redirectToLocation:@"view"];
}
-- (id) expungeAction
-{
- // TODO: we might want to flush the caches?
- NSException *error;
- id clientObject;
-
- clientObject = [self clientObject];
- if (clientObject)
- {
- error = [clientObject expunge];
- if (!error)
- {
- if ([clientObject respondsToSelector: @selector(flushMailCaches)])
- [clientObject flushMailCaches];
- return [self redirectToLocation:@"view"];
- }
- }
- else
- error = [NSException exceptionWithHTTPStatus:404 /* Not Found */
- reason: @"did not find mail folder"];
-
- return error;
-}
-
@end
/* UIxMailListView */
-/* UIxMailMainFrame.h - this file is part of $PROJECT_NAME_HERE$
+/* UIxMailMainFrame.h - this file is part of SOGo
*
* Copyright (C) 2006 Inverse groupe conseil
*
#ifndef UIXMAILMAINFRAME_H
#define UIXMAILMAINFRAME_H
-#import "../Common/UIxPageFrame.h"
+#import "../SOGoUI/UIxComponent.h"
-@interface UIxMailMainFrame : UIxPageFrame
-{
- NSString *rootURL;
- NSString *userRootURL;
- struct {
- int hideFolderTree:1;
- int reserved:31;
- } mmfFlags;
-}
-
-- (NSString *)rootURL;
-- (NSString *)userRootURL;
-- (NSString *)calendarRootURL;
+@interface UIxMailMainFrame : UIxComponent
@end
#import <SoObjects/Mailer/SOGoMailObject.h>
#import <SoObjects/Mailer/SOGoMailAccounts.h>
-#import <SoObjects/SOGo/NSString+Utilities.h>
-#import <SoObjects/SOGo/NSObject+Utilities.h>
+#import <SoObjects/SOGo/NSArray+Utilities.h>
+#import <SoObjects/SOGo/SOGoUser.h>
#import <SOGoUI/UIxComponent.h>
#import "UIxMailMainFrame.h"
@implementation UIxMailMainFrame
-static NSString *treeRootClassName = nil;
-
-+ (void)initialize {
- NSUserDefaults *ud = [NSUserDefaults standardUserDefaults];
-
- treeRootClassName = [[ud stringForKey:@"SOGoMailTreeRootClass"] copy];
- if (treeRootClassName)
- NSLog(@"Note: use class '%@' as root for mail tree.", treeRootClassName);
- else
- treeRootClassName = @"SOGoMailAccounts";
-}
-
-- (void)dealloc {
- [self->rootURL release];
- [self->userRootURL release];
- [super dealloc];
-}
-
/* accessors */
- (NSString *) mailAccounts
{
- SOGoMailAccounts *co;
+ NSArray *accounts, *accountNames;
- co = [self clientObject];
+ accounts = [[context activeUser] mailAccounts];
+ accountNames = [accounts objectsForKey: @"name"];
- return [[co fetchAllIdentities] jsonRepresentation];
+ return [accountNames jsonRepresentation];
}
-- (NSString *)treeRootClassName {
- return treeRootClassName;
-}
-
-- (void)setHideFolderTree:(BOOL)_flag {
- self->mmfFlags.hideFolderTree = _flag ? 1 : 0;
-}
-- (BOOL)hideFolderTree {
- return self->mmfFlags.hideFolderTree ? YES : NO;
-}
-
-- (NSString *) pageFormURL {
+- (NSString *) pageFormURL
+{
NSString *u;
NSRange r;
return [u hasSuffix:@"/"] ? @"view" : @"#";
}
-- (BOOL)showLinkBanner {
- return YES;
-}
-
-- (NSString *)bannerToolbarStyle {
- return nil;
-}
-
-- (NSString *)bannerConsumeStyle {
- return nil;
-}
-
-/* URL generation */
-// TODO: I think all this should be done by the clientObject?!
-// TODO: is the stuff below necessary at all in the mailer frame?
-
-- (NSString *)rootURL {
- WOContext *ctx;
- NSArray *traversalObjects;
-
- if (self->rootURL != nil)
- return self->rootURL;
-
- ctx = [self context];
- traversalObjects = [ctx objectTraversalStack];
- self->rootURL = [[[traversalObjects objectAtIndex:0]
- rootURLInContext:ctx]
- copy];
- return self->rootURL;
-}
-
-- (NSString *)userRootURL {
- WOContext *ctx;
- NSArray *traversalObjects;
-
- if (self->userRootURL)
- return self->userRootURL;
-
- ctx = [self context];
- traversalObjects = [ctx objectTraversalStack];
- self->userRootURL = [[[[traversalObjects objectAtIndex:1]
- baseURLInContext:ctx]
- stringByAppendingString:@"/"]
- retain];
- return self->userRootURL;
-}
-
-- (NSString *)calendarRootURL {
- return [[self userRootURL] stringByAppendingString:@"Calendar/"];
-}
-
-- (NSString *)contactsRootURL {
- return [[self userRootURL] stringByAppendingString:@"Contacts/"];
-}
-
-/* error handling */
-
-- (BOOL)hasErrorText {
- return [[[[self context] request] formValueForKey:@"error"] length] > 0
- ? YES : NO;
-}
-- (NSString *)errorText {
- return [[[self context] request] formValueForKey:@"error"];
-}
-
-- (NSString *)errorAlertJavaScript {
- NSString *errorText;
-
- if ([(errorText = [self errorText]) length] == 0)
- return nil;
-
- // TODO: proper JavaScript escaping
- errorText = [errorText stringByEscapingHTMLString];
- errorText = [errorText stringByReplacingString:@"\"" withString:@"'"];
-
- return [NSString stringWithFormat:
- @"<script language=\"JavaScript\">"
- @"alert(\"%@\");"
- @"</script>", errorText];
-}
-
-/* FIXME: migrated methods which might not work yet... */
-// #warning check this
-// - (NSString *) mailFolderName
-// {
-// NSMutableArray *mailboxes;
-// SOGoMailObject *currentObject;
-
-// mailboxes = [NSMutableArray new];
-// [mailboxes autorelease];
-
-// currentObject = [self clientObject];
-// while (![currentObject isKindOfClass: [SOGoMailAccounts class]])
-// {
-// [mailboxes insertObject: [currentObject nameInContainer] atIndex: 0];
-// currentObject = [currentObject container];
-// }
-
-// return [NSString stringWithFormat: @"/%@",
-// [mailboxes componentsJoinedByString: @"/"]];
-// }
-
-- (id) composeAction
-{
- NSArray *c;
- NSString *inbox, *url, *parameter;
- NSMutableDictionary *urlParams;
- id actionResult;
-
- c = [[self clientObject] toManyRelationshipKeys];
- if ([c count] > 0)
- {
- urlParams = [NSMutableDictionary new];
- [urlParams autorelease];
-
- parameter = [self queryParameterForKey: @"mailto"];
- if (parameter)
- [urlParams setObject: parameter
- forKey: @"mailto"];
- inbox = [NSString stringWithFormat: @"%@/folderINBOX",
- [c objectAtIndex: 0]];
- url = [inbox composeURLWithAction: @"compose"
- parameters: urlParams
- andHash: NO];
- actionResult = [self redirectToLocation: url];
- }
- else
- actionResult = self;
-
- return actionResult;
-}
-
@end /* UIxMailMainFrame */
+++ /dev/null
-/*
- Copyright (C) 2004-2005 SKYRIX Software AG
-
- This file is part of OpenGroupware.org.
-
- OGo is free software; you can redistribute it and/or modify it under
- the terms of the GNU Lesser General Public License as published by the
- Free Software Foundation; either version 2, or (at your option) any
- later version.
-
- OGo 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 Lesser General Public
- License for more details.
-
- You should have received a copy of the GNU Lesser General Public
- License along with OGo; see the file COPYING. If not, write to the
- Free Software Foundation, 59 Temple Place - Suite 330, Boston, MA
- 02111-1307, USA.
-*/
-
-#import <Foundation/NSArray.h>
-#import <Foundation/NSString.h>
-#import <NGExtensions/NSNull+misc.h>
-#import <NGExtensions/NSString+misc.h>
-#import <SoObjects/Mailer/SOGoMailObject.h>
-#import <SoObjects/Mailer/SOGoDraftObject.h>
-#import <NGImap4/NGImap4EnvelopeAddress.h>
-#import <NGImap4/NGImap4Envelope.h>
-
-#import "UIxMailEditorAction.h"
-
-@interface UIxMailReplyAction : UIxMailEditorAction
-@end
-
-@implementation UIxMailReplyAction
-
-- (BOOL)hasReplyPrefix:(NSString *)_subject {
- static NSString *replyPrefixes[] = {
- @"Re:", // regular
- @"RE:", // Outlook v11 (English?)
- @"AW:", // German Outlook v11
- @"Re[", // numbered Re, eg "Re[2]:"
- nil
- };
- unsigned i;
- for (i = 0; replyPrefixes[i] != nil; i++) {
- if ([_subject hasPrefix:replyPrefixes[i]])
- return YES;
- }
- return NO;
-}
-
-- (NSString *)replySubject:(NSString *)_subject {
- if (![_subject isNotNull] || [_subject length] == 0)
- return _subject;
-
- if ([self hasReplyPrefix:_subject]) {
- /* do not do: "Re: Re: Re: My Mail" - a single Re is sufficient ;-) */
- return _subject;
- }
-
- return [@"Re: " stringByAppendingString:_subject];
-}
-
-- (void)addEMailsOfAddresses:(NSArray *)_addrs toArray:(NSMutableArray *)_ma {
- unsigned i, count;
-
- for (i = 0, count = [_addrs count]; i < count; i++)
- [_ma addObject:[(NGImap4EnvelopeAddress *)[_addrs objectAtIndex:i] email]];
-}
-
-- (void)fillInReplyAddresses:(NSMutableDictionary *)_info
- replyToAll:(BOOL)_replyToAll
- envelope:(NGImap4Envelope *)_envelope
-{
- /*
- The rules as implemented by Thunderbird:
- - if there is a 'reply-to' header, only include that (as TO)
- - if we reply to all, all non-from addresses are added as CC
- - the from is always the lone TO (except for reply-to)
-
- Note: we cannot check reply-to, because Cyrus even sets a reply-to in the
- envelope if none is contained in the message itself! (bug or
- feature?)
-
- TODO: what about sender (RFC 822 3.6.2)
- */
- NSMutableArray *to;
- NSArray *addrs;
-
- to = [NSMutableArray arrayWithCapacity:2];
-
- /* first check for "reply-to" */
-
- addrs = [_envelope replyTo];
- if ([addrs count] == 0) {
- /* no "reply-to", try "from" */
- addrs = [_envelope from];
- }
- [self addEMailsOfAddresses:addrs toArray:to];
- [_info setObject:to forKey:@"to"];
-
- /* CC processing if we reply-to-all: add all 'to' and 'cc' */
-
- if (_replyToAll) {
- to = [NSMutableArray arrayWithCapacity:8];
-
- [self addEMailsOfAddresses:[_envelope to] toArray:to];
- [self addEMailsOfAddresses:[_envelope cc] toArray:to];
-
- [_info setObject:to forKey:@"cc"];
- }
-}
-
-- (NSString *) contentForReplyOnParts: (NSDictionary *) _prts
- keys: (NSArray *) _k
-{
- static NSString *textPartSeparator = @"\n---\n";
- NSMutableString *ms;
- unsigned i, count;
-
- ms = [NSMutableString stringWithCapacity:16000];
- for (i = 0, count = [_k count]; i < count; i++) {
- NSString *k, *v;
-
- k = [_k objectAtIndex:i];
-
- // TODO: this is DUP code to SOGoMailObject
- if ([k isEqualToString:@"body[text]"])
- k = @"";
- else if ([k hasPrefix:@"body["]) {
- k = [k substringFromIndex:5];
- if ([k length] > 0) k = [k substringToIndex:([k length] - 1)];
- }
-
- v = [_prts objectForKey:k];
- if (![v isKindOfClass:[NSString class]]) {
- [self logWithFormat:@"Note: cannot show part %@", k];
- continue;
- }
- if ([v length] == 0)
- continue;
-
- if (i != 0) [ms appendString:textPartSeparator];
- [ms appendString:[v stringByApplyingMailQuoting]];
- }
- return ms;
-}
-
-- (NSString *)contentForReply {
- NSArray *keys, *partInfos;
- NSDictionary *parts, *infos;
-
- keys = [[self clientObject] plainTextContentFetchKeys];
-// SOGoMailObject *co;
-
-// co = [self clientObject];
-// keys = [co plainTextContentFetchKeys];
-// infos = [co fetchCoreInfos];
-// partInfos = [infos objectForKey: keys];
-// NSLog (@"infos: '%@'", infos);
-
- if ([keys count] == 0)
- return nil;
-
- if ([keys count] > 1) {
- /* filter keys, only include top-level, or if none, the first */
- NSMutableArray *topLevelKeys = nil;
- unsigned i;
-
- for (i = 0; i < [keys count]; i++) {
- NSRange r;
-
- r = [[keys objectAtIndex:i] rangeOfString:@"."];
- if (r.length > 0)
- continue;
-
- if (topLevelKeys == nil)
- topLevelKeys = [NSMutableArray arrayWithCapacity:4];
- [topLevelKeys addObject:[keys objectAtIndex:i]];
- }
-
- if ([topLevelKeys count] > 0) {
- /* use top-level keys if we have some */
- keys = topLevelKeys;
- }
- else {
- /* just take the first part */
- keys = [NSArray arrayWithObject:[keys objectAtIndex:0]];
- }
- }
-
- parts = [[self clientObject] fetchPlainTextStrings:keys];
- return [self contentForReplyOnParts:parts keys:keys];
-}
-
-- (id)replyToAll:(BOOL)_replyToAll {
- NSMutableDictionary *info;
- NSException *error;
- id result;
- id tmp;
-
- /* ensure mail exists and is filled */
-
- // TODO: we could transport the body structure in a hidden field of the mail
- // viewer to avoid refetching the core-info?
- tmp = [[self clientObject] fetchCoreInfos];
- if ([tmp isKindOfClass:[NSException class]])
- return tmp;
- if (![tmp isNotNull])
- return [self didNotFindMailError];
-
- /* setup draft */
-
- if ((error = [self _setupNewDraft]) != nil)
- return error;
-
- /* fill draft info */
-
- info = [NSMutableDictionary dictionaryWithCapacity:16];
-
- [info setObject:[self replySubject:[[self clientObject] subject]]
- forKey:@"subject"];
- [self fillInReplyAddresses:info replyToAll:_replyToAll
- envelope:[[self clientObject] envelope]];
-
- /* fill in text content */
-
- if ((tmp = [self contentForReply]) != nil)
- [info setObject:tmp forKey:@"text"];
-
- /* save draft info */
-
- if ((error = [self->newDraft storeInfo:info]) != nil)
- return error;
-
- // TODO: we might want to pass the original URL to the editor for a final
- // redirect back to the message?
- result = [self redirectToEditNewDraft];
- [self reset];
- return result;
-}
-
-- (id)replyAction {
- return [self replyToAll:NO];
-}
-- (id)replyallAction {
- return [self replyToAll:YES];
-}
-
-@end /* UIxMailReplyAction */
return NO;
}
+- (BOOL) mailIsDraft
+{
+ return [[self clientObject] isInDraftsFolder];
+}
+
- (id) redirectToParentFolder
{
id url;
};
expunge = {
protectedBy = "View";
- pageName = "UIxMailListView";
- actionName = "expunge";
+ actionClass = "UIxMailFolderActions";
+ actionName = "emptyTrash";
};
createFolder = {
protectedBy = "View";
actionClass = "UIxMailFolderActions";
actionName = "deleteFolder";
};
- compose = {
- protectedBy = "View";
- actionClass = "UIxMailEditorAction";
- actionName = "compose";
- };
userRights = {
protectedBy = "ReadAcls";
pageName = "UIxMailUserRightsEditor";
};
edit = {
protectedBy = "View";
- pageName = "UIxMailEditor";
- };
- compose = {
- protectedBy = "View";
- actionClass = "UIxMailEditorAction";
- actionName = "compose";
+ actionClass = "UIxMailActions";
+ actionName = "edit";
};
reply = {
protectedBy = "View";
- actionClass = "UIxMailReplyAction";
+ actionClass = "UIxMailActions";
actionName = "reply";
};
replyall = {
protectedBy = "View";
- actionClass = "UIxMailReplyAction";
- actionName = "replyall";
+ actionClass = "UIxMailActions";
+ actionName = "replyToAll";
};
forward = {
protectedBy = "View";
- actionClass = "UIxMailForwardAction";
+ actionClass = "UIxMailActions";
actionName = "forward";
};
};
protectedBy = "View";
pageName = "UIxMailMainFrame";
};
- compose = {
- protectedBy = "View";
- pageName = "UIxMailMainFrame";
- actionName = "compose";
- };
getMail = {
protectedBy = "View";
pageName = "UIxMailAccountsView";
methods = {
compose = {
protectedBy = "View";
- actionClass = "UIxMailEditorAction";
+ actionClass = "UIxMailAccountActions";
actionName = "compose";
};
mailboxes = {
protectedBy = "View";
pageName = "UIxMailListView";
};
- compose = {
- protectedBy = "View";
- actionClass = "UIxMailEditorAction";
- actionName = "compose";
- };
};
};
};
};
methods = {
- view = { /* somewhat hackish */
- protectedBy = "View";
- pageName = "UIxMailView";
- };
edit = {
protectedBy = "View";
pageName = "UIxMailEditor";
pageName = "UIxMailEditor";
actionName = "save";
};
- delete = {
- protectedBy = "View";
- pageName = "UIxMailEditor";
- actionName = "delete";
- };
-
- viewAttachments = {
- protectedBy = "View";
- pageName = "UIxMailEditorAttach";
- actionName = "viewAttachments";
- };
- attach = {
- protectedBy = "View";
- pageName = "UIxMailEditorAttach";
- actionName = "attach";
- };
- deleteAttachment = {
- protectedBy = "View";
- pageName = "UIxMailEditorAttach";
- actionName = "deleteAttachment";
- };
send = {
protectedBy = "View";
pageName = "UIxMailEditor";
MainUI_RESOURCE_FILES += \
Version \
product.plist \
+ AgenorProfile.sql \
+ AgenorProfile-oracle.sql \
OCSFolderInfo.sql \
- AgenorProfile.sql
+ OCSFolderInfo-oracle.sql \
MainUI_LOCALIZED_RESOURCE_FILES += \
Locale Localizable.strings
NGMimeMessage *message;
NGMimeMultipartBody *body;
SOGoUser *activeUser;
+ NSDictionary *identity;
+ NSString *from, *fullMail;
activeUser = [context activeUser];
- recipient = [[LDAPUserManager sharedUserManager] getFullEmailForUID: recipientUID];
+ identity = [activeUser primaryIdentity];
+ from = [identity objectForKey: @"email"];
+ fullMail = [NSString stringWithFormat: @"%@ <%@>",
+ [identity objectForKey: @"fullName"], from];
+
+ recipient = [[LDAPUserManager sharedUserManager]
+ getFullEmailForUID: recipientUID];
headerMap = [NGMutableHashMap hashMapWithCapacity: 5];
[headerMap setObject: @"multipart/alternative" forKey: @"content-type"];
- [headerMap setObject: [activeUser fullEmail] forKey: @"From"];
+ [headerMap setObject: fullMail forKey: @"From"];
[headerMap setObject: recipient forKey: @"To"];
date = [[NSCalendarDate date] rfc822DateString];
[headerMap setObject: date forKey: @"Date"];
[[SOGoMailer sharedMailer] sendMimePart: message
toRecipients: [NSArray arrayWithObject: recipient]
- sender: [activeUser primaryEmail]];
+ sender: from];
}
@end
- (NSArray *) filterAppointments:(NSArray *) _apts
{
NSMutableArray *filtered;
- unsigned i, count;
- NSString *email;
- NSDictionary *info;
- NSArray *partmails;
- unsigned p, pCount;
+ unsigned i, count, p, pCount;
+ NSString *email, *partmailsString, *state, *pEmail;
+ NSDictionary *info, *primaryIdentity;
+ NSArray *partmails, *partstates;
BOOL shouldAdd;
- NSString *partmailsString;
- NSArray *partstates;
- NSString *state;
- NSString *pEmail;
if ([self shouldDisplayRejectedAppointments])
return _apts;
{
count = [_apts count];
filtered = [[[NSMutableArray alloc] initWithCapacity: count] autorelease];
- email = [[context activeUser] primaryEmail];
+
+ primaryIdentity = [[context activeUser] primaryIdentity];
+ email = [primaryIdentity objectForKey: @"email"];
for (i = 0; i < count; i++)
{
{
NSString *organizerEmail;
SOGoUser *activeUser;
+ NSDictionary *primaryIdentity;
organizerEmail = [[component organizer] email];
if ([organizerEmail length] == 0)
{
ASSIGN (organizer, [iCalPerson elementWithTag: @"organizer"]);
activeUser = [context activeUser];
+ primaryIdentity = [activeUser primaryIdentity];
[organizer setCn: [activeUser cn]];
- [organizer setEmail: [activeUser primaryEmail]];
+ [organizer setEmail: [primaryIdentity objectForKey: @"email"]];
[component setOrganizer: organizer];
}
}
<var:foreach list="contactInfos" item="currentContact">
<tr class="tableview"
var:id="currentContact.c_name"
- var:contactname="displayName"
+ var:contactname="currentContact.displayName"
var:contactid="currentContact.c_uid"
onclick="return onContactRowClick(event, this);"
ondblclick="return onContactRowDblClick(event, this);"
<?xml version='1.0' standalone='yes'?>
- <var:component
- xmlns="http://www.w3.org/1999/xhtml"
- xmlns:var="http://www.skyrix.com/od/binding"
- xmlns:const="http://www.skyrix.com/od/constant"
- xmlns:uix="OGo:uix"
- xmlns:rsrc="OGo:url"
- xmlns:label="OGo:label"
- className="UIxPageFrame"
- title="panelTitle"
- const:popup="YES">
- <div class="menu" id="attachmentsMenu">
- <ul>
- <li><var:string label:value="Open"/></li>
- <li><var:string label:value="Delete" /></li>
- <li><var:string label:value="Select All" /></li>
- <li><!-- separator --></li>
- <li><var:string label:value="Attach File(s)..." /></li>
- <li><var:string label:value="Attach Web Page..." /></li>
- </ul>
- </div>
+<!DOCTYPE var:component>
+<var:component
+ xmlns="http://www.w3.org/1999/xhtml"
+ xmlns:var="http://www.skyrix.com/od/binding"
+ xmlns:const="http://www.skyrix.com/od/constant"
+ xmlns:uix="OGo:uix"
+ xmlns:rsrc="OGo:url"
+ xmlns:label="OGo:label"
+ className="UIxPageFrame"
+ title="panelTitle"
+ const:popup="YES">
+ <div class="menu" id="attachmentsMenu">
+ <ul>
+ <li><var:string label:value="Open"/></li>
+ <li><var:string label:value="Delete" /></li>
+ <li><var:string label:value="Select All" /></li>
+ <li><!-- separator --></li>
+ <li><var:string label:value="Attach File(s)..." /></li>
+ <li><var:string label:value="Attach Web Page..." /></li>
+ </ul>
+ </div>
- <form name="pageform" enctype="multipart/form-data">
- <div id="headerArea">
- <div id="attachmentsArea">
- <var:string label:value="Attachments:" />
- <ul id="attachments">
- <var:foreach list="attachmentNames" item="attachmentName"
- ><img rsrc:img="attachment.gif"
- /><var:string value="attachmentName"
- /></var:foreach>
- </ul>
- </div>
- <span class="headerField"><var:string label:value="From" />:</span>
- <var:popup const:name="from"
- list="fromEMails"
- item="item"
- selection="from"
- /><br />
- <div>
- <var:component className="UIxMailToSelection"
- to="to" cc="cc" bcc="bcc" />
- </div>
- <div class="addressListElement" id="subjectRow"
- ><span class="headerField"><var:string label:value="Subject"
- />:</span
- ><span class="headerInput"
- ><input name="subject"
- type="text"
- class="textField"
- var:value="subject"
- /></span></div>
+ <form name="pageform" enctype="multipart/form-data">
+ <div id="headerArea">
+ <div id="attachmentsArea">
+ <var:string label:value="Attachments:" />
+ <ul id="attachments">
+ <var:foreach list="attachmentNames" item="attachmentName"
+ ><img rsrc:img="attachment.gif"
+ /><var:string value="attachmentName"
+ /></var:foreach>
+ </ul>
+ </div>
+ <span class="headerField"><var:string label:value="From" />:</span>
+ <var:popup const:name="from"
+ list="fromEMails"
+ item="item"
+ selection="from"
+ /><br />
+ <div>
+ <var:component className="UIxMailToSelection"
+ to="to" cc="cc" bcc="bcc" />
</div>
+ <div class="addressListElement" id="subjectRow"
+ ><span class="headerField"><var:string label:value="Subject"
+ />:</span
+ ><span class="headerInput"
+ ><input name="subject"
+ type="text"
+ class="textField"
+ var:value="subject"
+ /></span></div>
+ </div>
<!-- separator line -->
- <textarea name="text" var:value="text" />
+ <textarea name="text" rows="30" var:value="text" />
<!-- img rsrc:src="tbird_073_compose.png" alt="screenshot" / -->
- </form>
- </var:component>
+ </form>
+</var:component>
<?xml version="1.0" standalone="yes"?>
- <var:component xmlns="http://www.w3.org/1999/xhtml"
- xmlns:var="http://www.skyrix.com/od/binding"
- xmlns:const="http://www.skyrix.com/od/constant"
- xmlns:rsrc="OGo:url"
- xmlns:label="OGo:label"
- className="UIxPageFrame"
- title="title"
- popup="isPopup"
- >
- <script type="text/javascript" rsrc:src="dtree.js"><!-- space --></script>
- <script type="text/javascript" rsrc:src="MailerUI+dTree.js"><!-- space --></script>
- <script type="text/javascript">
- var mailAccounts = '<var:string value="mailAccounts" const:escapeHTML="NO"/>'.evalJSON(true);
- </script>
- <div class="menu" id="accountIconMenu">
- <ul>
- <li><var:string label:value="Subscribe..." /></li>
- <li><var:string label:value="Get Messages for Account" /></li>
- <li><var:string label:value="New Folder..." /></li>
- <li><!-- separator --></li>
- <li><var:string label:value="Search Messages..." /></li>
- <li><var:string label:value="Properties..." /></li>
- </ul>
- </div>
+<!DOCTYPE var:component>
+<var:component xmlns="http://www.w3.org/1999/xhtml"
+ xmlns:var="http://www.skyrix.com/od/binding"
+ xmlns:const="http://www.skyrix.com/od/constant"
+ xmlns:rsrc="OGo:url"
+ xmlns:label="OGo:label"
+ className="UIxPageFrame"
+ title="title"
+ >
+ <script type="text/javascript" rsrc:src="dtree.js"><!-- space --></script>
+ <script type="text/javascript" rsrc:src="MailerUI+dTree.js"><!-- space --></script>
+ <script type="text/javascript">
+ var mailAccounts = '<var:string value="mailAccounts" const:escapeHTML="NO"/>'.evalJSON(true);
+ </script>
+ <div class="menu" id="accountIconMenu">
+ <ul>
+ <li><var:string label:value="Subscribe..." /></li>
+ <li><var:string label:value="Get Messages for Account" /></li>
+ <li><var:string label:value="New Folder..." /></li>
+ <li><!-- separator --></li>
+ <li><var:string label:value="Search Messages..." /></li>
+ <li><var:string label:value="Properties..." /></li>
+ </ul>
+ </div>
- <div class="menu" id="inboxIconMenu">
- <ul>
- <li><var:string label:value="Open in New Mail Window" /></li>
- <li><var:string label:value="Copy Folder Location" /></li>
- <li><var:string label:value="Subscribe..." /></li>
- <li><!-- separator --></li>
- <li><var:string label:value="Mark Folder Read..." /></li>
- <li><var:string label:value="New Folder..." /></li>
- <li><var:string label:value="Compact This Folder" /></li>
- <li><!-- separator --></li>
- <li><var:string label:value="Search Messages..." /></li>
- <li><var:string label:value="Sharing..." /></li>
- </ul>
- </div>
-
- <div class="menu" id="trashIconMenu">
- <ul>
- <li><var:string label:value="Open in New Mail Window" /></li>
- <li><var:string label:value="Copy Folder Location" /></li>
- <li><var:string label:value="Subscribe..." /></li>
- <li><!-- separator --></li>
- <li><var:string label:value="Mark Folder Read..." /></li>
- <li><var:string label:value="New Subfolder..." /></li>
- <li><var:string label:value="Compact This Folder" /></li>
- <li><var:string label:value="Empty Trash" /></li>
- <li><!-- separator --></li>
- <li><var:string label:value="Search Messages..." /></li>
- <li><var:string label:value="Sharing..." /></li>
- </ul>
- </div>
+ <div class="menu" id="inboxIconMenu">
+ <ul>
+ <li><var:string label:value="Open in New Mail Window" /></li>
+ <li><var:string label:value="Copy Folder Location" /></li>
+ <li><var:string label:value="Subscribe..." /></li>
+ <li><!-- separator --></li>
+ <li><var:string label:value="Mark Folder Read..." /></li>
+ <li><var:string label:value="New Folder..." /></li>
+ <li><var:string label:value="Compact This Folder" /></li>
+ <li><!-- separator --></li>
+ <li><var:string label:value="Search Messages..." /></li>
+ <li><var:string label:value="Sharing..." /></li>
+ </ul>
+ </div>
+
+ <div class="menu" id="trashIconMenu">
+ <ul>
+ <li><var:string label:value="Open in New Mail Window" /></li>
+ <li><var:string label:value="Copy Folder Location" /></li>
+ <li><var:string label:value="Subscribe..." /></li>
+ <li><!-- separator --></li>
+ <li><var:string label:value="Mark Folder Read..." /></li>
+ <li><var:string label:value="New Subfolder..." /></li>
+ <li><var:string label:value="Compact This Folder" /></li>
+ <li><var:string label:value="Empty Trash" /></li>
+ <li><!-- separator --></li>
+ <li><var:string label:value="Search Messages..." /></li>
+ <li><var:string label:value="Sharing..." /></li>
+ </ul>
+ </div>
- <div class="menu" id="mailboxIconMenu">
- <ul>
- <li><var:string label:value="Open in New Mail Window" /></li>
- <li><var:string label:value="Copy Folder Location" /></li>
- <li><var:string label:value="Subscribe..." /></li>
- <li><!-- separator --></li>
- <li><var:string label:value="Mark Folder Read..." /></li>
- <li><var:string label:value="New Subfolder..." /></li>
- <li><var:string label:value="Rename Folder..." /></li>
- <li><var:string label:value="Compact This Folder" /></li>
- <li><var:string label:value="Delete Folder" /></li>
- <li><!-- separator --></li>
- <li><var:string label:value="Search Messages..." /></li>
- <li><var:string label:value="Sharing..." /></li>
- </ul>
- </div>
-
- <div class="menu" id="addressMenu">
- <ul>
- <li id="add_to_addressbook"><var:string label:value="Add to Address Book..."/></li>
- <li id="compose_mailto"><var:string label:value="Compose Mail To"/></li>
- <li id="create_filter"><var:string label:value="Create Filter From Message..."/></li>
- </ul>
- </div>
+ <div class="menu" id="mailboxIconMenu">
+ <ul>
+ <li><var:string label:value="Open in New Mail Window" /></li>
+ <li><var:string label:value="Copy Folder Location" /></li>
+ <li><var:string label:value="Subscribe..." /></li>
+ <li><!-- separator --></li>
+ <li><var:string label:value="Mark Folder Read..." /></li>
+ <li><var:string label:value="New Subfolder..." /></li>
+ <li><var:string label:value="Rename Folder..." /></li>
+ <li><var:string label:value="Compact This Folder" /></li>
+ <li><var:string label:value="Delete Folder" /></li>
+ <li><!-- separator --></li>
+ <li><var:string label:value="Search Messages..." /></li>
+ <li><var:string label:value="Sharing..." /></li>
+ </ul>
+ </div>
+
+ <div class="menu" id="addressMenu">
+ <ul>
+ <li id="add_to_addressbook"><var:string label:value="Add to Address Book..."/></li>
+ <li id="compose_mailto"><var:string label:value="Compose Mail To"/></li>
+ <li id="create_filter"><var:string label:value="Create Filter From Message..."/></li>
+ </ul>
+ </div>
- <div class="menu" id="messageListMenu">
- <ul>
- <li><var:string label:value="Open Message In New Window"/></li>
- <li><!-- separator --></li>
- <li><var:string label:value="Reply to Sender Only"/></li>
- <li><var:string label:value="Reply to All"/></li>
- <li><var:string label:value="Forward"/></li>
- <li><var:string label:value="Edit As New..."/></li>
- <li><!-- separator --></li>
- <li><var:string label:value="Move To"/></li>
- <li><var:string label:value="Copy To"/></li>
- <li><var:string label:value="Label"/></li>
- <li><var:string label:value="Mark"/></li>
- <li><!-- separator --></li>
- <li><var:string label:value="Save As..."/></li>
- <li><var:string label:value="View Message Source"/></li>
- <li><var:string label:value="Print Preview"/></li>
- <li><var:string label:value="Print..."/></li>
- <li><var:string label:value="Delete Message"/></li>
- </ul>
- </div>
+ <div class="menu" id="messageListMenu">
+ <ul>
+ <li><var:string label:value="Open Message In New Window"/></li>
+ <li><!-- separator --></li>
+ <li><var:string label:value="Reply to Sender Only"/></li>
+ <li><var:string label:value="Reply to All"/></li>
+ <li><var:string label:value="Forward"/></li>
+ <li><var:string label:value="Edit As New..."/></li>
+ <li><!-- separator --></li>
+ <li><var:string label:value="Move To"/></li>
+ <li><var:string label:value="Copy To"/></li>
+ <li><var:string label:value="Label"/></li>
+ <li><var:string label:value="Mark"/></li>
+ <li><!-- separator --></li>
+ <li><var:string label:value="Save As..."/></li>
+ <li><var:string label:value="View Message Source"/></li>
+ <li><var:string label:value="Print Preview"/></li>
+ <li><var:string label:value="Print..."/></li>
+ <li><var:string label:value="Delete Message"/></li>
+ </ul>
+ </div>
- <div class="menu" id="messageContentMenu">
- <ul>
- <li><var:string label:value="Reply to Sender Only"/></li>
- <li><var:string label:value="Reply to All"/></li>
- <li><var:string label:value="Forward"/></li>
- <li><var:string label:value="Edit As New..."/></li>
- <li><var:string label:value="Move To"/></li>
- <li><var:string label:value="Copy To"/></li>
- <li><!-- separator --></li>
- <li><var:string label:value="Label"/></li>
- <li><var:string label:value="Mark"/></li>
- <li><!-- separator --></li>
- <li><var:string label:value="Save As..."/></li>
- <li><var:string label:value="View Message Source"/></li>
- <li><var:string label:value="Print Preview"/></li>
- <li><var:string label:value="Print..."/></li>
- <li><var:string label:value="Delete Message"/></li>
- </ul>
- </div>
+ <div class="menu" id="messageContentMenu">
+ <ul>
+ <li><var:string label:value="Reply to Sender Only"/></li>
+ <li><var:string label:value="Reply to All"/></li>
+ <li><var:string label:value="Forward"/></li>
+ <li><var:string label:value="Edit As New..."/></li>
+ <li><var:string label:value="Move To"/></li>
+ <li><var:string label:value="Copy To"/></li>
+ <li><!-- separator --></li>
+ <li><var:string label:value="Label"/></li>
+ <li><var:string label:value="Mark"/></li>
+ <li><!-- separator --></li>
+ <li><var:string label:value="Save As..."/></li>
+ <li><var:string label:value="View Message Source"/></li>
+ <li><var:string label:value="Print Preview"/></li>
+ <li><var:string label:value="Print..."/></li>
+ <li><var:string label:value="Delete Message"/></li>
+ </ul>
+ </div>
- <div class="menu" id="label-menu">
- <ul id="">
- <li><var:string label:value="None" /></li>
- <li><!-- separator --></li>
- <li><var:string label:value="Important" /></li>
- <li><var:string label:value="Work" /></li>
- <li><var:string label:value="Personal" /></li>
- <li><var:string label:value="To Do" /></li>
- <li><var:string label:value="Later" /></li>
- </ul>
- </div>
+ <div class="menu" id="label-menu">
+ <ul id="">
+ <li><var:string label:value="None" /></li>
+ <li><!-- separator --></li>
+ <li><var:string label:value="Important" /></li>
+ <li><var:string label:value="Work" /></li>
+ <li><var:string label:value="Personal" /></li>
+ <li><var:string label:value="To Do" /></li>
+ <li><var:string label:value="Later" /></li>
+ </ul>
+ </div>
- <div class="menu" id="mark-menu">
- <ul id="">
- <li><var:string label:value="As Read" /></li>
- <li><var:string label:value="Thread As Read" /></li>
- <li><var:string label:value="As Read By Date..." /></li>
- <li><var:string label:value="All Read" /></li>
- <li><!-- separator --></li>
- <li><var:string label:value="Flag" /></li>
- <li><!-- separator --></li>
- <li><var:string label:value="As Junk" /></li>
- <li><var:string label:value="As Not Junk" /></li>
- <li><var:string label:value="Run Junk Mail Controls" /></li>
- </ul>
- </div>
+ <div class="menu" id="mark-menu">
+ <ul id="">
+ <li><var:string label:value="As Read" /></li>
+ <li><var:string label:value="Thread As Read" /></li>
+ <li><var:string label:value="As Read By Date..." /></li>
+ <li><var:string label:value="All Read" /></li>
+ <li><!-- separator --></li>
+ <li><var:string label:value="Flag" /></li>
+ <li><!-- separator --></li>
+ <li><var:string label:value="As Junk" /></li>
+ <li><var:string label:value="As Not Junk" /></li>
+ <li><var:string label:value="Run Junk Mail Controls" /></li>
+ </ul>
+ </div>
- <div id="leftPanel">
- <div class="titlediv"><var:string label:value="Folders" /></div>
- <div id="folderTreeContent"><!-- space --></div>
- </div>
+ <div id="leftPanel">
+ <div class="titlediv"><var:string label:value="Folders" /></div>
+ <div id="folderTreeContent"><!-- space --></div>
+ </div>
- <div class="dragHandle" id="verticalDragHandle"><!-- space --></div>
+ <div class="dragHandle" id="verticalDragHandle"><!-- space --></div>
- <div id="rightPanel">
- <var:component className="UIxMailFilterPanel" qualifier="qualifier" />
- <div id="mailboxContent"><!-- space --></div>
- <div class="dragHandle" id="rightDragHandle"><!-- space --></div>
- <div id="messageContent"><!-- space --></div>
- </div>
+ <div id="rightPanel">
+ <var:component className="UIxMailFilterPanel" qualifier="qualifier" />
+ <div id="mailboxContent"><!-- space --></div>
+ <div class="dragHandle" id="rightDragHandle"><!-- space --></div>
+ <div id="messageContent"><!-- space --></div>
+ </div>
- <var:string value="errorAlertJavaScript" const:escapeHTML="NO" />
- </var:component>
+ <var:string value="errorAlertJavaScript" const:escapeHTML="NO" />
+</var:component>
xmlns:uix="OGo:uix"
xmlns:rsrc="OGo:url"
xmlns:label="OGo:label">
+ <var:if var:condition="mailIsDraft"
+ ><input const:name="editDraftButton" const:id="editDraftButton"
+ type="button" class="button" label:value="Edit Draft..."
+ /></var:if>
<table class="mailer_fieldtable">
<tr class="mailer_fieldrow">
<td class="mailer_fieldname" ><var:string label:value="Subject"/>:</td>
<meta name="description" content="SOGo Web Interface" />
<meta name="author" content="SKYRIX Software AG/Inverse groupe conseil" />
<meta name="robots" content="stop" />
- <link href="mailto:supper@inverse.ca" rev="made" />
+ <link href="mailto:support@inverse.ca" rev="made" />
<link rel="shortcut icon" rsrc:href="lori_16x16.ico" type="image/x-icon" />
<link type="text/css" rel="stylesheet" rsrc:href="generic.css" />
<link type="text/css" rel="stylesheet" rsrc:href="dtree.css" />
function openContactsFolder(contactsFolder, reload, idx) {
if ((contactsFolder && contactsFolder != currentContactFolder)
|| reload) {
- currentContactFolder = contactsFolder; log("openContactsFolder " + contactsFolder);
+ currentContactFolder = contactsFolder;
var url = URLForFolderID(currentContactFolder) +
"/view?noframe=1";
var searchValue = search["value"];
if (searchValue && searchValue.length > 0)
url += ("&search=" + search["criteria"]
- + "&value=" + searchValue);
+ + "&value=" + escape(searchValue));
var sortAttribute = sorting["attribute"];
if (sortAttribute && sortAttribute.length > 0)
url += ("&sort=" + sorting["attribute"]
function onContactFoldersContextMenu(event) {
var menu = $("contactFoldersMenu");
- Event.observe(menu, "hideMenu", onContactFoldersContextMenuHide, false);
+ //Event.observe(menu, "hideMenu", onContactFoldersContextMenuHide, false);
+ Event.observe(menu, "mousedown", onContactFoldersContextMenuHide, false);
popupMenu(event, "contactFoldersMenu", this);
var topNode = $("contactFolders");
var selectedNodes = topNode.getSelectedRows();
topNode.menuSelectedRows = selectedNodes;
for (var i = 0; i < selectedNodes.length; i++)
- selectedNodes[i].deselect();
+ $(selectedNodes[i]).deselect();
topNode.menuSelectedEntry = this;
- this.select();
+ $(this).select();
}
function onContactContextMenu(event, element) {
var menu = $("contactMenu");
- Event.observe(menu, "hideMenu", onContactContextMenuHide, false);
+ //Event.observe(menu, "hideMenu", onContactContextMenuHide, false);
+ Event.observe(menu, "mousedown", onContactContextMenuHide, false);
+ //document.documentElement.onclick = onContactContextMenuHide;
popupMenu(event, "contactMenu", element);
var topNode = $("contactsList");
var selectedNodes = topNode.getSelectedRows();
topNode.menuSelectedRows = selectedNodes;
for (var i = 0; i < selectedNodes.length; i++)
- selectedNodes[i].deselect();
+ $(selectedNodes[i]).deselect();
topNode.menuSelectedEntry = element;
- element.select();
+ $(element).select();
}
function onContactContextMenuHide(event) {
var topNode = $("contactsList");
if (topNode.menuSelectedEntry) {
- topNode.menuSelectedEntry.deselect();
+ $(topNode.menuSelectedEntry).deselect();
topNode.menuSelectedEntry = null;
}
if (topNode.menuSelectedRows) {
var nodes = topNode.menuSelectedRows;
for (var i = 0; i < nodes.length; i++)
- nodes[i].select();
+ $(nodes[i]).select();
topNode.menuSelectedRows = null;
}
}
}
}
-function refreshCurrentFolder() { log("refreshCurrentFolder");
+function refreshCurrentFolder() {
openContactsFolder(currentContactFolder, true);
}
}
/* fields (key/value UI), eg used in mail viewer */
+INPUT#editDraftButton
+{
+ position: absolute;
+ top: 2.5em;
+ right: 1em;
+}
TABLE.mailer_fieldtable
{
top: 0px;
left: 0px;
padding-top: .5em;
+ padding-bottom: .5em;
overflow-y: auto;
overflow-x: hidden;
- height: 7.5em;
border-bottom: 1px solid #808080;
background: #d4d0c8;
}
var maxCachedMessages = 20;
var cachedMessages = new Array();
var currentMailbox = null;
+var currentMailboxType = "";
var usersRightsWindowHeight = 320;
var usersRightsWindowWidth = 400;
}
function onMessageDoubleClick(event) {
- resetSelection(window);
- var msguid = this.parentNode.id.substr(4);
-
- return openMessageWindow(msguid,
- ApplicationBaseURL + currentMailbox + "/"
- + msguid + "/popupview");
+ var action;
+
+ if (currentMailboxType == "draft")
+ action = "edit";
+ else
+ action = "popupview";
+
+ return openMessageWindowsForSelection(action, true);
}
function toggleMailSelect(sender) {
row.className = sender.checked ? "tableview_selected" : "tableview";
}
-function clearSearch(sender) {
- var searchField = window.$("search");
- if (searchField) searchField.value="";
- return true;
-}
-
function openAddressbook(sender) {
var urlstr;
/* mail list reply */
-function openMessageWindowsForSelection(action) {
+function openMessageWindowsForSelection(action, firstOnly) {
if (document.body.hasClassName("popup"))
win = openMessageWindow(window.messageId,
- window.messageURL + "/" + action /* url */);
+ window.messageURL + "/" + action);
else {
var messageList = $("messageList");
var rows = messageList.getSelectedRowsId();
- var idset = "";
- for (var i = 0; i < rows.length; i++)
- win = openMessageWindow(rows[i].substr(4) /* msguid */,
- ApplicationBaseURL + currentMailbox
- + "/" + rows[i].substr(4)
- + "/" + action /* url */);
+ if (rows.length > 0) {
+ if (firstOnly)
+ openMessageWindow(rows[0].substr(4),
+ ApplicationBaseURL + currentMailbox
+ + "/" + rows[0].substr(4)
+ + "/" + action);
+ else
+ for (var i = 0; i < rows.length; i++)
+ openMessageWindow(rows[i].substr(4),
+ ApplicationBaseURL + currentMailbox
+ + "/" + rows[i].substr(4)
+ + "/" + action);
+ }
}
return false;
$("searchValue").value = "";
initCriteria();
- var datatype = this.parentNode.getAttribute("datatype");
- if (datatype == "account" || datatype == "additional") {
+ currentMailboxType = this.parentNode.getAttribute("datatype");
+ if (currentMailboxType == "account" || currentMailboxType == "additional") {
currentMailbox = mailbox;
$("messageContent").innerHTML = "";
var body = $("messageList").tBodies[0];
- for (var i = body.rows.length - 1; i > 0; i--)
- body.deleteRow(i);
+ for (var i = body.rows.length; i > 0; i--)
+ body.deleteRow(i-1);
}
else
openMailbox(mailbox);
return false;
}
+function onComposeMessage() {
+ var topWindow = getTopWindow();
+ if (topWindow)
+ topWindow.composeNewMessage();
+
+ return false;
+}
+
+function composeNewMessage() {
+ var account = currentMailbox.split("/")[1];
+ var url = ApplicationBaseURL + "/" + account + "/compose";
+ window.open(url, null,
+ "width=680,height=520,resizable=1,scrollbars=1,toolbar=0,"
+ + "location=0,directories=0,status=0,menubar=0,copyhistory=0");
+}
+
function openMailbox(mailbox, reload, idx) {
if (mailbox != currentMailbox || reload) {
currentMailbox = mailbox;
var searchValue = search["value"];
if (searchValue && searchValue.length > 0)
url += ("&search=" + search["criteria"]
- + "&value=" + searchValue);
+ + "&value=" + escape(searchValue));
var sortAttribute = sorting["attribute"];
if (sortAttribute && sortAttribute.length > 0)
url += ("&sort=" + sorting["attribute"]
else
Event.observe(anchors[i], "click",
onMessageAnchorClick);
+
+ var editDraftButton = $("editDraftButton");
+ if (editDraftButton)
+ Event.observe(editDraftButton, "click", onMessageEditDraft);
}
function onMessageContentMenu(event) {
popupMenu(event, 'messageContentMenu', this);
}
+function onMessageEditDraft(event) {
+ return openMessageWindowsForSelection("edit", true);
+}
+
function onEmailAddressClick(event) {
popupMenu(event, 'addressMenu', this);
}
var handle = $("verticalDragHandle");
if (handle) {
handle.addInterface(SOGoDragHandlesInterface);
+ handle.leftMargin = 1;
handle.leftBlock=$("leftPanel");
handle.rightBlock=$("rightPanel");
}
}
}
+function onMenuExpungeFolder(event) {
+ var folderID = document.menuTarget.getAttribute("dataname");
+ var urlstr = URLForFolderID(folderID) + "/expunge";
+
+ triggerAjaxRequest(urlstr, folderRefreshCallback, folderID);
+}
+
function onMenuEmptyTrash(event) {
var folderID = document.menuTarget.getAttribute("dataname");
var urlstr = URLForFolderID(folderID) + "/emptyTrash";
- triggerAjaxRequest(urlstr, folderOperationCallback);
+
+ triggerAjaxRequest(urlstr, folderRefreshCallback, folderID);
}
function folderOperationCallback(http) {
window.alert(labels["Operation failed"].decodeEntities());
}
+function folderRefreshCallback(http) {
+ if (http.readyState == 4
+ && http.status == 204) {
+ var oldMailbox = http.callbackData;
+ if (oldMailbox == currentMailbox)
+ refreshCurrentFolder();
+ }
+ else
+ window.alert(labels["Operation failed"].decodeEntities());
+}
+
function getMenus() {
var menus = {}
menus["accountIconMenu"] = new Array(null, null, onMenuCreateFolder, null,
null, null);
menus["inboxIconMenu"] = new Array(null, null, null, "-", null,
- onMenuCreateFolder, null, "-", null,
+ onMenuCreateFolder, onMenuExpungeFolder,
+ "-", null,
onMenuSharing);
menus["trashIconMenu"] = new Array(null, null, null, "-", null,
- onMenuCreateFolder, null,
+ onMenuCreateFolder, onMenuExpungeFolder,
onMenuEmptyTrash, "-", null,
onMenuSharing);
menus["mailboxIconMenu"] = new Array(null, null, null, "-", null,
onMenuCreateFolder,
onMenuRenameFolder,
- null, onMenuDeleteFolder, "-", null,
+ onMenuExpungeFolder,
+ onMenuDeleteFolder, "-", null,
onMenuSharing);
menus["addressMenu"] = new Array(newContactFromEmail, newEmailTo, null);
menus["messageListMenu"] = new Array(onMenuOpenMessage, "-",
var SOGoDragHandlesInterface = {
+ leftMargin: 180,
+ topMargin: 120,
dhType: null,
origX: -1,
origLeft: -1,
rightBlock: null,
upperBlock: null,
lowerBlock: null,
- startHandleDraggingBinded: null,
- stopHandleDraggingBinded: null,
- moveBinded: null,
+ startHandleDraggingBound: null,
+ stopHandleDraggingBound: null,
+ moveBound: null,
bind: function () {
this.startHandleDraggingBound = this.startHandleDragging.bindAsEventListener(this);
Event.observe(this, "mousedown", this.startHandleDraggingBound, false);
startHandleDragging: function (event) {
if (!this.dhType)
this._determineType();
- var targ;
- if (!event)
- var event = window.event;
- if (event.target)
- targ = event.target
- else if (event.srcElement)
- targ = event.srcElement
+ var targ = getTarget(event);
if (targ.nodeType == 1) {
if (this.dhType == 'horizontal') {
this.origX = this.offsetLeft;
this.origLeft = this.leftBlock.offsetWidth;
- delta = 0;
+ delta = 0;
this.origRight = this.rightBlock.offsetLeft - 5;
document.body.setStyle({ cursor: "e-resize" });
} else if (this.dhType == 'vertical') {
this.origY = this.offsetTop;
this.origUpper = this.upperBlock.offsetHeight;
- delta = event.clientY - this.offsetTop - 5;
+ var pointY = Event.pointerY(event);
+ if (pointY <= this.topMargin) delta = this.topMargin;
+ else delta = pointY - this.offsetTop - 5;
this.origLower = this.lowerBlock.offsetTop - 5;
document.body.setStyle({ cursor: "n-resize" });
}
if (!this.dhType)
this._determineType();
if (this.dhType == 'horizontal') {
- var deltaX
- = Math.floor(event.clientX - this.origX - (this.offsetWidth / 2));
- this.rightBlock.setStyle({ left: (this.origRight + deltaX) + 'px' });
- this.leftBlock.setStyle({ width: (this.origLeft + deltaX) + 'px' });
+ var pointerX = Event.pointerX(event);
+ if (pointerX <= this.leftMargin) {
+ this.rightBlock.setStyle({ left: (this.leftMargin) + 'px' });
+ this.leftBlock.setStyle({ width: (this.leftMargin) + 'px' });
+ }
+ else {
+ var deltaX = Math.floor(pointerX - this.origX - (this.offsetWidth / 2));
+ this.rightBlock.setStyle({ left: (this.origRight + deltaX) + 'px' });
+ this.leftBlock.setStyle({ width: (this.origLeft + deltaX) + 'px' });
+ }
} else if (this.dhType == 'vertical') {
- var deltaY
- = Math.floor(event.clientY - this.origY - (this.offsetHeight / 2));
- this.lowerBlock.setStyle({ top: (this.origLower + deltaY - delta) + 'px' });
- this.upperBlock.setStyle({ height: (this.origUpper + deltaY - delta) + 'px' });
+ var pointerY = Event.pointerY(event);
+ if (pointerY <= this.topMargin) {
+ this.lowerBlock.setStyle({ top: (this.topMargin - delta) + 'px' });
+ this.upperBlock.setStyle({ height: (this.topMargin - delta) + 'px' });
+ }
+ else {
+ var deltaY = Math.floor(pointerY - this.origY - (this.offsetHeight / 2));
+ this.lowerBlock.setStyle({ top: (this.origLower + deltaY - delta) + 'px' });
+ this.upperBlock.setStyle({ height: (this.origUpper + deltaY - delta) + 'px' });
+ }
}
-
Event.stopObserving(document.body, "mouseup", this.stopHandleDraggingBound, true);
Event.stopObserving(document.body, "mousemove", this.moveBound, true);
document.body.setAttribute('style', '');
- this.move(event);
- event.cancelBubble = true;
-
- return false;
+ Event.stop(event);
},
move: function (event) {
if (!this.dhType)
this._determineType();
if (this.dhType == 'horizontal') {
+ var hX = Event.pointerX(event);
var width = this.offsetWidth;
- var hX = event.clientX;
- if (hX > -1) {
- var newLeft = Math.floor(hX - (width / 2));
- this.setStyle({ left: newLeft + 'px' });
- event.cancelBubble = true;
-
- return false;
- }
+ if (hX < this.leftMargin)
+ hX = this.leftMargin + Math.floor(width / 2);
+ var newLeft = Math.floor(hX - (width / 2));
+ this.setStyle({ left: newLeft + 'px' });
} else if (this.dhType == 'vertical') {
var height = this.offsetHeight;
- var hY = event.clientY;
- if (hY > -1) {
- var newTop = Math.floor(hY - (height / 2)) - delta;
- this.setStyle({ top: newTop + 'px' });
- event.cancelBubble = true;
-
- return false;
- }
+ var hY = Event.pointerY(event);
+ if (hY < this.topMargin)
+ hY = this.topMargin + Math.floor(height / 2);
+ var newTop = Math.floor(hY - (height / 2)) - delta;
+ this.setStyle({ top: newTop + 'px' });
}
+ Event.stop(event);
},
doubleClick: function (event) {
if (!this.dhType)
preventDefault(event);
}
-function configureSearchField() {
- var searchValue = $("searchValue");
-
- Event.observe(searchValue, "mousedown",
- onSearchMouseDown.bindAsEventListener(searchValue));
- Event.observe(searchValue, "click",
- popupSearchMenu.bindAsEventListener(searchValue));
- Event.observe(searchValue, "blur",
- onSearchBlur.bindAsEventListener(searchValue));
- Event.observe(searchValue, "focus",
- onSearchFocus.bindAsEventListener(searchValue));
- Event.observe(searchValue, "keydown",
- onSearchKeyDown.bindAsEventListener(searchValue));
-}
-
function configureLists() {
var list = $("tasksList");
list.multiselect = true;
DIV#subjectRow INPUT
{ background-image: none;
- width: 38em;
+ width: 100%;
padding-left: .5em; }
div#compose_internetmarker
left: 0em;
right: 0em;
bottom: 0em;
- top: 17em; }
+ top: 17em;
+ width: 100%; }
var stop = false;
var counter = 0;
var currentRow = $('row_' + counter);
- while (currentRow
- && !stop) {
- var currentValue = currentRow.childNodesWithTag("span")[1].childNodesWithTag("input")[0].value;
+ while (currentRow && !stop) {
+ var currentValue = $(currentRow.childNodesWithTag("span")[1]).childNodesWithTag("input")[0].value;
if (currentValue == neededOptionValue) {
stop = true;
insertContact($("addr_" + counter), contactName, contactEmail);
}
function clickedEditorSave(sender) {
- document.pageform.action = "save";
- document.pageform.submit();
- refreshOpener();
+ document.pageform.action = "save";
+ document.pageform.submit();
return false;
}
function clickedEditorDelete(sender) {
- document.pageform.action = "delete";
- document.pageform.submit();
- refreshOpener();
- window.close();
+ document.pageform.action = "delete";
+ document.pageform.submit();
+ window.close();
return false;
}
var list = $("attachments");
$(list).attachMenu("attachmentsMenu");
var elements = list.childNodesWithTag("li");
- for (var i = 0; i < elements.length; i++)
+ for (var i = 0; i < elements.length; i++) {
Event.observe(elements[i], "click",
onRowClick.bindAsEventListener(elements[i]));
+ }
+ onWindowResize(null);
+ Event.observe(window, "resize", onWindowResize);
}
function getMenus() {
nodes[i].select();
}
+function onWindowResize(event) {
+ var textarea = document.pageform.text;
+ var windowheight = (typeof self.innerHeight == "number" ? self.innerHeight : document.body.clientHeight);
+ var textareaoffset = textarea.offsetTop;
+ var rowheight = (Element.getHeight(textarea) / textarea.rows);
+
+ textarea.rows = Math.round((windowheight - textareaoffset) / rowheight);
+ log ("onWindowResize new number of rows = " + textarea.rows);
+}
+
addEvent(window, 'load', initMailEditor);
preventDefault(event);
}
+
+function initMailerPopup(event) {
+ var headerTable = document.getElementsByClassName('mailer_fieldtable')[0];
+ var contentDiv = document.getElementsByClassName('mailer_mailcontent')[0];
+
+ contentDiv.setStyle({ 'top': (Element.getHeight(headerTable) + headerTable.offsetTop) + 'px' });
+}
+
+addEvent(window, 'load', initMailerPopup);
hideMenu(document.currentPopupMenu);
var popup = $(menuId);
- var menuTop = event.pageY;
- var menuLeft = event.pageX;
+ var menuTop = Event.pointerY(event);
+ var menuLeft = Event.pointerX(event);
var heightDiff = (window.innerHeight
- (menuTop + popup.offsetHeight));
if (heightDiff < 0)
visibility: "visible" });
document.currentPopupMenu = popup;
+
Event.observe(document.body, "click", onBodyClickMenuHandler);
preventDefault(event);
preventDefault(event);
}
-function hideMenu(menuNode) {
+function hideMenu(menuNode) { //log ("hideMenu");
var onHide;
if (menuNode.submenu) {
menuNode.parentMenu = null;
}
- if (document.initEvent) {
- var onhideEvent = document.createEvent("UIEvents");
- onhideEvent.initEvent("hideMenu", false, true);
+ if (document.createEvent) {
+ var onhideEvent;
+ if (isSafari())
+ onhideEvent = document.createEvent("UIEvents");
+ else // Mozilla
+ onhideEvent = document.createEvent("Events");
+ onhideEvent.initEvent("mousedown", false, true);
menuNode.dispatchEvent(onhideEvent);
}
- else if (document.createEventObject) {
- // TODO: add support for IE
+ else if (document.createEventObject) { // IE
+ menuNode.fireEvent("onmousedown");
}
}
/* search field */
function popupSearchMenu(event) {
var menuId = this.getAttribute("menuid");
- relX = event.pageX - $(this).cascadeLeftOffset();
- relY = event.pageY - $(this).cascadeTopOffset();
+ var offset = Position.cumulativeOffset(this);
+
+ relX = Event.pointerX(event) - offset[0];
+ relY = Event.pointerY(event) - offset[1];
if (event.button == 0
&& relX < 24) {
hideMenu(document.currentPopupMenu);
var popup = $(menuId);
+ offset = Position.positionedOffset(this);
popup.setStyle({ top: this.offsetHeight + "px",
- left: (this.offsetLeft + 3) + "px",
+ left: (offset[0] + 3) + "px",
visibility: "visible" });
document.currentPopupMenu = popup;
}
function setSearchCriteria(event) {
- searchValue = $("searchValue");
- searchCriteria = $("searchCriteria");
+ var searchValue = $("searchValue");
+ var searchCriteria = $("searchCriteria");
searchValue.setAttribute("ghost-phrase", this.innerHTML);
searchCriteria.value = this.getAttribute('id');
function configureSearchField() {
var searchValue = $("searchValue");
+ if (!searchValue) return;
+
Event.observe(searchValue, "mousedown",
onSearchMouseDown.bindAsEventListener(searchValue));
Event.observe(searchValue, "click",
function onSearchMouseDown(event) {
var superNode = this.parentNode.parentNode.parentNode;
- relX = (event.pageX - superNode.offsetLeft - this.offsetLeft);
- relY = (event.pageY - superNode.offsetTop - this.offsetTop);
+ relX = (Event.pointerX(event) - superNode.offsetLeft - this.offsetLeft);
+ relY = (Event.pointerY(event) - superNode.offsetTop - this.offsetTop);
if (relY < 24) {
event.cancelBubble = true;
}
function onSearchBlur(event) {
- var ghostPhrase = this.getAttribute("ghost-phrase");
-// log ("search blur: '" + this.value + "'");
- if (!this.value) {
+ var ghostPhrase = this.getAttribute("ghost-phrase");
+ //log ("search blur: '" + this.value + "'");
+ if (!this.value) {
this.setAttribute("modified", "");
this.setStyle({ color: "#aaa" });
this.value = ghostPhrase;
+ refreshCurrentFolder();
} else if (this.value == ghostPhrase) {
this.setAttribute("modified", "");
this.setStyle({ color: "#aaa" });
this.timer = setTimeout("onSearchFormSubmit()", 1000);
}
-function onSearchFormSubmit(event) { log("generic.onSearchFormSubmit")
+function onSearchFormSubmit(event) {
var searchValue = $("searchValue");
var searchCriteria = $("searchCriteria");
+ var ghostPhrase = searchValue.getAttribute('ghost-phrase');
+
+ if (searchValue.value == ghostPhrase) return;
search["criteria"] = searchCriteria.value;
search["value"] = searchValue.value;
var searchCriteria = $("searchCriteria");
var searchValue = $("searchValue");
+ if (!searchValue) return;
+
var searchOptions = $("searchOptions").childNodesWithTag("li");
if (searchOptions.length > 0) {
var firstChild = searchOptions[0];
function initMenu(menuDIV, callbacks) {
var lis = $(menuDIV.childNodesWithTag("ul")[0]).childNodesWithTag("li");
for (var j = 0; j < lis.length; j++) {
- var node = lis[j];
+ var node = $(lis[j]);
Event.observe(node, "mousedown", listRowMouseDownHandler, false);
var callback = callbacks[j];
if (callback) {
queryParameters = parseQueryParameters('' + window.location);
if (!$(document.body).hasClassName("popup")) {
initLogConsole();
- initCriteria();
- configureSearchField();
}
+ initCriteria();
+ configureSearchField();
initMenus();
initTabs();
configureDragHandles();