+2007-08-09 Wolfgang Sourdeau <wsourdeau@inverse.ca>
+
+ * SoObjects/SOGo/NSString+Utilities.m ([NSString
+ -pureEMailAddress]): new utility method inspired by the
+ "_rawSender" private method in SOGoDraftObject, which it now
+ replaces.
+
+ * SoObjects/SOGo/SOGoMailer.m: new abstraction class module that
+ provides a common API for sending emails, no matter what the
+ transport is.
+
2007-08-08 Wolfgang Sourdeau <wsourdeau@inverse.ca>
+ * UI/MailerUI/UIxMailFolderActions.m ([UIxMailFolderActions
+ -quotasAction]): invoke "relativeImap4Name" instead of
+ "nameInContainer" since the latter also returns the "folder"
+ prefix.
+
+ * UI/MailerUI/UIxMailAccountActions.m ([UIxMailAccountActions
+ -listMailboxesAction]): declare the output as text/plain in UTF-8.
+
* UI/MailerUI/UIxMailFolderActions.m ([UIxMailFolderActions
-deleteFolderAction]): fixed the url of the destination folder.
- fixed a bug where folders starting with digits would not be displayed;
- improved IE7 and Safari support: priority menus, attendees selector;
- 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;
0.9.0-20070713
--------------
*/
#import <Foundation/NSString.h>
+#import <Foundation/NSUserDefaults.h>
#import <NGObjWeb/NSException+HTTP.h>
#import <NGObjWeb/SoSecurityManager.h>
#import <NGObjWeb/WOApplication.h>
#import <NGObjWeb/WOContext+SoObjects.h>
+#import <NGExtensions/NSObject+Logs.h>
+#import <NGExtensions/NGHashMap.h>
#import <NGCards/iCalCalendar.h>
#import <NGCards/iCalPerson.h>
#import <NGCards/iCalRepeatableEntityObject.h>
-#import <NGMime/NGMime.h>
-#import <NGMail/NGMail.h>
-#import <NGMail/NGSendMail.h>
+#import <NGMime/NGMimeBodyPart.h>
+#import <NGMime/NGMimeMultipartBody.h>
+#import <NGMail/NGMimeMessage.h>
#import <SoObjects/SOGo/LDAPUserManager.h>
#import <SoObjects/SOGo/NSCalendarDate+SOGo.h>
+#import <SoObjects/SOGo/SOGoMailer.h>
#import <SoObjects/SOGo/SOGoPermissions.h>
#import <SoObjects/SOGo/SOGoUser.h>
#import <SoObjects/Appointments/SOGoAppointmentFolder.h>
NSString *pageName;
iCalPerson *organizer;
NSString *cn, *email, *sender, *iCalString;
- NGSendMail *sendmail;
WOApplication *app;
unsigned i, count;
iCalPerson *attendee;
/* generate iCalString once */
iCalString = [[_newObject parent] versitString];
- /* get sendmail object */
- sendmail = [NGSendMail sharedSendMail];
-
/* get WOApplication instance */
app = [WOApplication application];
[body release];
/* send the damn thing */
- [sendmail sendMimePart: msg
- toRecipients: [NSArray arrayWithObject: email]
- sender: [organizer rfc822Email]];
+ [[SOGoMailer sharedMailer]
+ sendMimePart: msg
+ toRecipients: [NSArray arrayWithObject: email]
+ sender: [organizer rfc822Email]];
}
}
}
02111-1307, USA.
*/
-#import "SOGoTaskObject.h"
+#import <Foundation/NSException.h>
+#import <Foundation/NSUserDefaults.h>
+#import <NGExtensions/NSObject+Logs.h>
+#import <NGExtensions/NSNull+misc.h>
#import <NGCards/iCalCalendar.h>
#import <NGCards/iCalToDo.h>
#import <NGCards/iCalEventChanges.h>
#import <NGCards/iCalPerson.h>
#import <SOGo/LDAPUserManager.h>
-#import <NGMime/NGMime.h>
-#import <NGMail/NGMail.h>
-#import <NGMail/NGSendMail.h>
-#import "SOGoAptMailNotification.h"
+
+#import <SoObjects/SOGo/SOGoMailer.h>
#import "NSArray+Appointments.h"
+#import "SOGoAptMailNotification.h"
+
+#import "SOGoTaskObject.h"
@interface SOGoTaskObject (PrivateAPI)
/* contents */
-- (NSDictionary *)fetchInfo;
-- (NSException *)storeInfo:(NSDictionary *)_info;
+- (NSDictionary *) fetchInfo;
+- (NSException *) storeInfo: (NSDictionary *) _info;
/* attachments */
-- (NSArray *)fetchAttachmentNames;
-- (BOOL)isValidAttachmentName:(NSString *)_name;
-- (NSException *) saveAttachment: (NSData *)_attach
+- (NSArray *) fetchAttachmentNames;
+- (BOOL) isValidAttachmentName: (NSString *) _name;
+- (NSException *) saveAttachment: (NSData *) _attach
withMetadata: (NSDictionary *) metadata;
-- (NSException *)deleteAttachmentWithName:(NSString *)_name;
+- (NSException *) deleteAttachmentWithName: (NSString *) _name;
/* NGMime representations */
-- (NGMimeMessage *)mimeMessage;
+- (NGMimeMessage *) mimeMessage;
-- (NSString *)saveMimeMessageToTemporaryFile;
-- (NSString *)saveMimeMessageToTemporaryFileWithHeaders:(NSDictionary *)_addh;
-- (NSException *)sendMimeMessageAtPath:(NSString *)_path;
+- (NSString *) saveMimeMessageToTemporaryFile;
+- (NSString *) saveMimeMessageToTemporaryFileWithHeaders:(NSDictionary *)_addh;
-- (NSException *)sendMail;
+- (NSException *) sendMail;
/* operations */
-- (NSException *)delete;
+- (NSException *) delete;
/* fake being a SOGoMailObject */
-- (id)fetchParts:(NSArray *)_parts;
+- (id) fetchParts: (NSArray *) _parts;
@end
#import <NGImap4/NGImap4EnvelopeAddress.h>
#import <NGMail/NGMimeMessage.h>
#import <NGMail/NGMimeMessageGenerator.h>
-#import <NGMail/NGSendMail.h>
#import <NGMime/NGMimeBodyPart.h>
#import <NGMime/NGMimeFileData.h>
#import <NGMime/NGMimeMultipartBody.h>
#import <NGMime/NGMimeHeaderFieldGenerator.h>
#import <SoObjects/SOGo/NSCalendarDate+SOGo.h>
+#import <SoObjects/SOGo/SOGoMailer.h>
#import "SOGoDraftObject.h"
return ma;
}
-- (NSString *) _rawSender
+- (NSException *) sendMail
{
- NSString *startEmail, *rawSender;
- NSRange delimiter;
-
- startEmail = [self sender];
- delimiter = [startEmail rangeOfString: @"<"];
- if (delimiter.location == NSNotFound)
- rawSender = startEmail;
- else
- {
- rawSender = [startEmail substringFromIndex: NSMaxRange (delimiter)];
- delimiter = [rawSender rangeOfString: @">"];
- if (delimiter.location != NSNotFound)
- rawSender = [rawSender substringToIndex: delimiter.location];
- }
-
- return rawSender;
-}
-
-- (NSException *)sendMimeMessageAtPath:(NSString *)_path {
- static NGSendMail *mailer = nil;
- NSArray *recipients;
- NSString *from;
-
- /* validate */
-
- recipients = [self allRecipients];
- from = [self _rawSender];
- if ([recipients count] == 0) {
- return [NSException exceptionWithHTTPStatus:500 /* server error */
- reason:@"draft has no recipients set!"];
- }
- if ([from length] == 0) {
- return [NSException exceptionWithHTTPStatus:500 /* server error */
- reason:@"draft has no sender (from) set!"];
- }
-
- /* setup mailer object */
-
- if (mailer == nil)
- mailer = [[NGSendMail sharedSendMail] retain];
- if (![mailer isSendMailAvailable]) {
- [self errorWithFormat:@"missing sendmail binary!"];
- return [NSException exceptionWithHTTPStatus:500 /* server error */
- reason:@"did not find sendmail binary!"];
- }
-
- /* send mail */
-
- return [mailer sendMailAtPath:_path toRecipients:recipients sender:from];
-}
-
-- (NSException *)sendMail {
NSException *error;
NSString *tmpPath;
}
/* send mail */
- error = [self sendMimeMessageAtPath:tmpPath];
-
+ error = [[SOGoMailer sharedMailer] sendMailAtPath: tmpPath
+ toRecipients: [self allRecipients]
+ sender: [self sender]];
+
/* delete temporary file */
[self deleteTemporaryMessageFile:tmpPath];
- (NGImap4Connection *)imap4Connection;
- (NGImap4ConnectionManager *)mailManager;
+
+- (NSString *) relativeImap4Name;
+- (NSMutableString *) imap4URLString;
+
- (NSURL *)imap4URL;
- (NSString *)imap4Login;
- (NSString *)imap4Password;
NSCalendarDate+SOGo.h \
\
SOGoAuthenticator.h \
+ SOGoMailer.h \
SOGoUser.h \
libSOGo_OBJC_FILES = \
NSCalendarDate+SOGo.m \
\
SOGoAuthenticator.m \
+ SOGoMailer.m \
SOGoUser.m \
# tools
- (NSString *) jsonRepresentation;
+/* bare email addresses */
+- (NSString *) pureEMailAddress;
+
#ifndef GNUSTEP_BASE_LIBRARY
- (BOOL) boolValue;
#endif
return [NSString stringWithFormat: @"\"%@\"", representation];
}
+- (NSString *) pureEMailAddress
+{
+ NSString *pureAddress;
+ NSRange delimiter;
+
+ delimiter = [self rangeOfString: @"<"];
+ if (delimiter.location == NSNotFound)
+ pureAddress = self;
+ else
+ {
+ pureAddress = [self substringFromIndex: NSMaxRange (delimiter)];
+ delimiter = [pureAddress rangeOfString: @">"];
+ if (delimiter.location != NSNotFound)
+ pureAddress = [pureAddress substringToIndex: delimiter.location];
+ }
+
+ return pureAddress;
+}
+
#if LIB_FOUNDATION_LIBRARY
- (BOOL) boolValue
{
--- /dev/null
+/* SOGoMailer.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 SOGOMAILER_H
+#define SOGOMAILER_H
+
+@class NSArray;
+@class NSException;
+@class NSString;
+
+@protocol NGMimePart;
+
+@interface SOGoMailer : NSObject
+{
+ NSString *mailingMechanism;
+ NSString *smtpServer;
+}
+
++ (id) sharedMailer;
+
+- (NSException *) sendMailAtPath: (NSString *) filename
+ toRecipients: (NSArray *) recipients
+ sender: (NSString *) sender;
+
+- (NSException *) sendMimePart: (id <NGMimePart>) part
+ toRecipients: (NSArray *) recipients
+ sender: (NSString *) sender;
+
+@end
+
+#endif /* SOGOMAILER_H */
--- /dev/null
+/* SOGoMailer.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 <Foundation/NSEnumerator.h>
+#import <Foundation/NSException.h>
+#import <Foundation/NSString.h>
+#import <Foundation/NSUserDefaults.h>
+
+#import <NGObjWeb/NSException+HTTP.h>
+#import <NGExtensions/NSObject+Logs.h>
+#import <NGMail/NGSendMail.h>
+#import <NGMail/NGSmtpClient.h>
+#import <NGMime/NGMimePartGenerator.h>
+
+#import "NSString+Utilities.h"
+#import "SOGoMailer.h"
+
+#define defaultMailingMechanism @"sendmail"
+#define defaultSMTPServer @"localhost"
+
+@implementation SOGoMailer
+
++ (id) sharedMailer
+{
+ static id sharedMailer = nil;
+
+ if (!sharedMailer)
+ sharedMailer = [self new];
+
+ return sharedMailer;
+}
+
+- (id) init
+{
+ NSUserDefaults *ud;
+
+ if ((self = [super init]))
+ {
+ ud = [NSUserDefaults standardUserDefaults];
+ mailingMechanism = [ud stringForKey: @"SOGoMailingMechanism"];
+ if (mailingMechanism)
+ {
+ if (!([mailingMechanism isEqualToString: @"sendmail"]
+ || [mailingMechanism isEqualToString: @"smtp"]))
+ {
+ [self logWithFormat: @"mechanism '%@' is invalid and"
+ @" should be set to 'sendmail' or 'smtp' instead",
+ mailingMechanism];
+ [self logWithFormat: @"falling back to default '%@' mechanism",
+ defaultMailingMechanism];
+ mailingMechanism = defaultMailingMechanism;
+ }
+ }
+ else
+ {
+ [self logWithFormat: @"default mailing mechanism set to '%@'",
+ defaultMailingMechanism];
+ mailingMechanism = defaultMailingMechanism;
+ }
+ [mailingMechanism retain];
+
+ if ([mailingMechanism isEqualToString: @"smtp"])
+ {
+ smtpServer = [ud stringForKey: @"SOGoSMTPServer"];
+ if (!smtpServer)
+ {
+ [self logWithFormat: @"default smtp server set to '%@'",
+ defaultSMTPServer];
+ smtpServer = defaultSMTPServer;
+ }
+ [smtpServer retain];
+ }
+ else
+ smtpServer = nil;
+ }
+
+ return self;
+}
+
+- (void) dealloc
+{
+ [mailingMechanism release];
+ [smtpServer release];
+ [super dealloc];
+}
+
+- (NSException *) _sendmailSendData: (NSData *) mailData
+ toRecipients: (NSArray *) recipients
+ sender: (NSString *) sender
+{
+ NSException *result;
+ NGSendMail *mailer;
+
+ mailer = [NGSendMail sharedSendMail];
+ if ([mailer isSendMailAvailable])
+ result = [mailer sendMailData: mailData
+ toRecipients: recipients
+ sender: sender];
+ else
+ result = [NSException exceptionWithHTTPStatus: 500
+ reason: @"cannot send message:"
+ @" no sendmail binary!"];
+
+ return result;
+}
+
+- (NSException *) _sendMailData: (NSData *) mailData
+ withClient: (NGSmtpClient *) client
+ andRejections: (unsigned int) toErrors
+{
+ NSException *result;
+
+ if (toErrors > 0)
+ [self logWithFormat: @"sending email despite address rejections"];
+ if ([client sendData: mailData])
+ result = nil;
+ else
+ result = [NSException exceptionWithHTTPStatus: 500
+ reason: @"cannot send message:"
+ @" (smtp) failure when sending data"];
+
+ return result;
+}
+
+- (NSException *) _smtpSendData: (NSData *) mailData
+ toRecipients: (NSArray *) recipients
+ sender: (NSString *) sender
+{
+ NGSmtpClient *client;
+ NSEnumerator *addresses;
+ NSString *currentTo;
+ unsigned int toErrors;
+ NSException *result;
+
+ client = [NGSmtpClient smtpClient];
+ if ([client connectToHost: smtpServer])
+ {
+ if ([client hello]
+ && [client mailFrom: sender])
+ {
+ toErrors = 0;
+ addresses = [recipients objectEnumerator];
+ currentTo = [addresses nextObject];
+ while (currentTo)
+ {
+ if (![client recipientTo: [currentTo pureEMailAddress]])
+ {
+ [self logWithFormat: @"error with recipient '%@'", currentTo];
+ toErrors++;
+ }
+ currentTo = [addresses nextObject];
+ }
+ if (toErrors == [recipients count])
+ result = [NSException exceptionWithHTTPStatus: 500
+ reason: @"cannot send message:"
+ @" (smtp) all recipients discarded"];
+ else
+ result = [self _sendMailData: mailData withClient: client
+ andRejections: toErrors];
+ }
+ else
+ result = [NSException exceptionWithHTTPStatus: 500
+ reason: @"cannot send message:"
+ @" (smtp) error when connecting"];
+ [client quit];
+ [client disconnect];
+ }
+
+ return result;
+}
+
+- (NSException *) sendMailData: (NSData *) data
+ toRecipients: (NSArray *) recipients
+ sender: (NSString *) sender
+{
+ NSException *result;
+
+ if (![recipients count])
+ result = [NSException exceptionWithHTTPStatus: 500
+ reason: @"cannot send message: no recipients set"];
+ else
+ {
+ if (![sender length])
+ result = [NSException exceptionWithHTTPStatus: 500
+ reason: @"cannot send message: no sender set"];
+ else
+ {
+ if ([mailingMechanism isEqualToString: @"sendmail"])
+ result = [self _sendmailSendData: data
+ toRecipients: recipients
+ sender: [sender pureEMailAddress]];
+ else
+ result = [self _smtpSendData: data
+ toRecipients: recipients
+ sender: [sender pureEMailAddress]];
+ }
+ }
+
+ return result;
+}
+
+- (NSException *) sendMimePart: (id <NGMimePart>) part
+ toRecipients: (NSArray *) recipients
+ sender: (NSString *) sender
+{
+ NSData *mailData;
+
+ mailData = [[NGMimePartGenerator mimePartGenerator]
+ generateMimeFromPart: part];
+
+ return [self sendMailData: mailData
+ toRecipients: recipients
+ sender: sender];
+}
+
+- (NSException *) sendMailAtPath: (NSString *) filename
+ toRecipients: (NSArray *) recipients
+ sender: (NSString *) sender
+{
+ NSException *result;
+ NSData *mailData;
+
+ mailData = [NSData dataWithContentsOfFile: filename];
+ if ([mailData length] > 0)
+ result = [self sendMailData: mailData
+ toRecipients: recipients
+ sender: sender];
+ else
+ result = [NSException exceptionWithHTTPStatus: 500
+ reason: @"cannot send message: no data"
+ @" (missing or empty file?)"];
+
+ return nil;
+}
+
+@end
folders = [self _jsonFolders: [rawFolders objectEnumerator]];
response = [context response];
+ [response setStatus: 200];
+ [response setHeader: @"text/plain; charset=utf-8"
+ forKey: @"content-type"];
[response appendContentString: [folders jsonRepresentation]];
return response;
folderURL = [folder imap4URL];
client = [[folder imap4Connection] client];
- infos = [client getQuotaRoot: [folder nameInContainer]];
+ infos = [client getQuotaRoot: [folder relativeImap4Name]];
responseString = [[infos objectForKey: @"quotas"] jsonRepresentation];
[response appendContentString: responseString];
#import <NGObjWeb/WOResponse.h>
#import <NGExtensions/NGHashMap.h>
#import <NGMail/NGMimeMessage.h>
-#import <NGMail/NGSendMail.h>
#import <NGMime/NGMimeBodyPart.h>
#import <NGMime/NGMimeMultipartBody.h>
+#import <SoObjects/SOGo/SOGoMailer.h>
#import <SoObjects/SOGo/SOGoUser.h>
#import <SoObjects/SOGo/SOGoObject.h>
#import <SoObjects/SOGo/LDAPUserManager.h>
[message setBody: body];
[body release];
- [[NGSendMail sharedSendMail] sendMimePart: message
- toRecipients: [NSArray arrayWithObject: recipient]
- sender: [activeUser primaryEmail]];
+ [[SOGoMailer sharedMailer] sendMimePart: message
+ toRecipients: [NSArray arrayWithObject: recipient]
+ sender: [activeUser primaryEmail]];
}
@end
"-", "label-menu", "mark-menu",
"-",
null, onMenuViewMessageSource,
- null, null,
+ null, onPrintCurrentMessage,
onMenuDeleteMessage);
menus["label-menu"] = new Array(null, "-", null , null, null, null , null,
null);