#include "SOGoDraftObject.h"
#include <NGMail/NGMimeMessage.h>
+#include <NGMail/NGMimeMessageGenerator.h>
+#include <NGMail/NGSendMail.h>
#include <NGMime/NGMimeBodyPart.h>
-#include <NGMime/NGMimeMultipartBody.h>
#include <NGMime/NGMimeFileData.h>
+#include <NGMime/NGMimeMultipartBody.h>
#include <NGMime/NGMimeType.h>
#include <NGExtensions/NSFileManager+Extensions.h>
#include "common.h"
static NGMimeType *TextPlainType = nil;
static NGMimeType *MultiMixedType = nil;
+static BOOL draftDeleteDisabled = NO; // for debugging
+static BOOL debugOn = NO;
+ (void)initialize {
TextPlainType = [[NGMimeType mimeType:@"text" subType:@"plain"] copy];
return self->info;
}
+/* accessors */
+
+- (NSString *)sender {
+ return [[self fetchInfo] objectForKey:@"from"];
+}
+
+/* attachments */
+
- (NSArray *)fetchAttachmentNames {
NSMutableArray *ma;
NSFileManager *fm;
/* add senders */
- if ((s = [lInfo objectForKey:@"from"]) != nil)
+ if ([(s = [lInfo objectForKey:@"from"]) length] > 0)
[map setObject:s forKey:@"from"];
- if ((s = [lInfo objectForKey:@"replyTo"]) != nil)
+
+ if ([(s = [lInfo objectForKey:@"replyTo"]) length] > 0)
+ [map setObject:s forKey:@"reply-to"];
+ else if ([(s = [lInfo objectForKey:@"from"]) length] > 0)
[map setObject:s forKey:@"reply-to"];
/* add subject */
- if ((s = [lInfo objectForKey:@"subject"]) != nil)
+ if ([(s = [lInfo objectForKey:@"subject"]) length] > 0)
[map setObject:s forKey:@"subject"];
/* add standard headers */
if ((map = [self mimeHeaderMap]) == nil)
return nil;
- [self logWithFormat:@"MIME Envelope: %@", map];
+ [self debugWithFormat:@"MIME Envelope: %@", map];
if ((bodyParts = [self bodyPartsForAllAttachments]) == nil) {
[self logWithFormat:
@"ERROR: could not create body parts for attachments!"];
return nil; // TODO: improve error handling, return exception
}
- [self logWithFormat:@"attachments: %@", bodyParts];
+ [self debugWithFormat:@"attachments: %@", bodyParts];
if ([bodyParts count] == 0) {
/* no attachments */
message = [self mimeMultiPartMessageWithHeaderMap:map
andBodyParts:bodyParts];
}
- [self logWithFormat:@"message: %@", message];
+ [self debugWithFormat:@"message: %@", message];
message = [message retain];
[pool release];
return [message autorelease];
}
+- (NSString *)saveMimeMessageToTemporaryFile {
+ NGMimeMessageGenerator *gen;
+ NSAutoreleasePool *pool;
+ NGMimeMessage *message;
+ NSString *tmpPath;
+
+ pool = [[NSAutoreleasePool alloc] init];
+
+ message = [self mimeMessage];
+ if (![message isNotNull])
+ return nil;
+
+ gen = [[NGMimeMessageGenerator alloc] init];
+ tmpPath = [[gen generateMimeFromPartToFile:message] copy];
+ [gen release]; gen = nil;
+
+ [pool release];
+ return [tmpPath autorelease];
+}
+
+- (void)deleteTemporaryMessageFile:(NSString *)_path {
+ NSFileManager *fm;
+
+ if (![_path isNotNull])
+ return;
+
+ fm = [NSFileManager defaultManager];
+ if (![fm fileExistsAtPath:_path])
+ return;
+
+ [fm removeFileAtPath:_path handler:nil];
+}
+
+- (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;
+}
+
+- (NSException *)sendMimeMessageAtPath:(NSString *)_path {
+ static NGSendMail *mailer = nil;
+ NSArray *recipients;
+ NSString *from;
+
+ /* validate */
+
+ recipients = [self allRecipients];
+ from = [self sender];
+ 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 logWithFormat:@"ERROR: 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;
+
+ /* 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 = [self sendMimeMessageAtPath:tmpPath];
+
+ /* delete temporary file */
+ [self deleteTemporaryMessageFile:tmpPath];
+
+ return error;
+}
+
+/* operations */
+
+- (NSException *)delete {
+ NSFileManager *fm;
+ NSString *p, *sp;
+ NSEnumerator *e;
+
+ if ((fm = [self spoolFileManager]) == nil) {
+ [self logWithFormat:@"ERROR: 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!"];
+ }
+ }
+
+ 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!"];
+ }
+ }
+ return nil;
+}
+
+/* actions */
+
+- (id)DELETEAction:(id)_ctx {
+ NSException *error;
+
+ if ((error = [self delete]) != nil)
+ return error;
+
+ return [NSNumber numberWithBool:YES]; /* delete worked out ... */
+}
+
+/* debugging */
+
+- (BOOL)isDebuggingEnabled {
+ return debugOn;
+}
+
@end /* SOGoDraftObject */
NSArray *bcc;
NSString *subject;
NSString *text;
- NSString *tmpMessagePath;
}
@end
nil];
}
-- (void)deleteTemporaryMessageFile {
- NSFileManager *fm;
-
- if (![self->tmpMessagePath isNotNull])
- return;
-
- fm = [NSFileManager defaultManager];
- if (![fm fileExistsAtPath:self->tmpMessagePath])
- return;
-
- [fm removeFileAtPath:self->tmpMessagePath handler:nil];
-}
-
- (void)dealloc {
- [self deleteTemporaryMessageFile];
- [self->tmpMessagePath release];
-
[self->text release];
[self->subject release];
[self->to release];
return [self valuesForKeys:infoKeys];
}
-/* MIME message */
-
-- (NSString *)saveMessageToTemporaryFile:(NGMimeMessage *)_message {
- NGMimeMessageGenerator *gen;
- NSString *path;
-
- if (![_message isNotNull])
- return nil;
-
- gen = [[NGMimeMessageGenerator alloc] init];
- path = [gen generateMimeFromPartToFile:_message];
- [gen release]; gen = nil;
-
- return path;
-}
-
/* requests */
- (BOOL)shouldTakeValuesFromRequest:(WORequest *)_rq inContext:(WOContext*)_c{
return YES;
}
+/* IMAP4 store */
+
+- (void)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.
+ */
+}
+
/* actions */
- (BOOL)_saveFormInfo {
}
- (id)sendAction {
- NGMimeMessage *message;
+ NSException *error;
/* first, save form data */
/* then, send mail */
- if ((message = [[self clientObject] mimeMessage]) == nil) {
- return [NSException exceptionWithHTTPStatus:500 /* server error */
- reason:@"could not create MIME message for draft!"];
+ if ((error = [[self clientObject] sendMail]) != nil) {
+ // TODO: improve error handling
+ return error;
}
- self->tmpMessagePath = [[self saveMessageToTemporaryFile:message] copy];
- if (![self->tmpMessagePath isNotNull]) {
- return [NSException exceptionWithHTTPStatus:500 /* server error */
- reason:@"could not save MIME message for draft!"];
- }
-
- [self logWithFormat:@"saved message to: %@", self->tmpMessagePath];
-
- [self logWithFormat:@"TODO: send mail ..."];
-
- /*
- 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.
- */
+ /* patch flags in store for replies etc */
- /* delete draft */
+ [self patchFlagsInStore];
/* finally store in Sent */
[self logWithFormat:@"TODO: store mail in Sent folder ..."];
+ /* delete draft */
+
+ if ((error = [[self clientObject] delete]) != nil)
+ return error;
+
// if everything is ok, close the window (send a JS closing the Window)
- [self deleteTemporaryMessageFile];
return self;
}