2 Copyright (C) 2004-2005 SKYRIX Software AG
4 This file is part of OpenGroupware.org.
6 OGo is free software; you can redistribute it and/or modify it under
7 the terms of the GNU Lesser General Public License as published by the
8 Free Software Foundation; either version 2, or (at your option) any
11 OGo is distributed in the hope that it will be useful, but WITHOUT ANY
12 WARRANTY; without even the implied warranty of MERCHANTABILITY or
13 FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
14 License for more details.
16 You should have received a copy of the GNU Lesser General Public
17 License along with OGo; see the file COPYING. If not, write to the
18 Free Software Foundation, 59 Temple Place - Suite 330, Boston, MA
22 #include <SOGoUI/UIxComponent.h>
27 An mail editor component which works on SOGoDraftObject's.
30 @class NSArray, NSString;
31 @class SOGoMailFolder;
33 @interface UIxMailEditor : UIxComponent
42 SOGoMailFolder *sentFolder;
47 #include <SoObjects/Mailer/SOGoDraftObject.h>
48 #include <SoObjects/Mailer/SOGoMailFolder.h>
49 #include <SoObjects/Mailer/SOGoMailAccount.h>
50 #include <SoObjects/SOGo/WOContext+Agenor.h>
51 #include <NGMail/NGMimeMessage.h>
52 #include <NGMail/NGMimeMessageGenerator.h>
55 @interface UIxComponent (Scheduler_Privates)
56 - (NSString *)emailForUser;
59 @implementation UIxMailEditor
61 static BOOL keepMailTmpFile = NO;
62 static BOOL showInternetMarker = NO;
63 static EOQualifier *internetDetectQualifier = nil;
64 static NSDictionary *internetMailHeaders = nil;
65 static NSArray *infoKeys = nil;
68 NSUserDefaults *ud = [NSUserDefaults standardUserDefaults];
71 infoKeys = [[NSArray alloc] initWithObjects:
72 @"subject", @"text", @"to", @"cc", @"bcc",
76 keepMailTmpFile = [ud boolForKey:@"SOGoMailEditorKeepTmpFile"];
78 NSLog(@"WARNING: keeping mail files.");
80 /* Internet mail settings */
82 showInternetMarker = [ud boolForKey:@"SOGoShowInternetMarker"];
83 if (!showInternetMarker) {
84 NSLog(@"Note: visual Internet marker on mail editor disabled "
85 @"(SOGoShowInternetMarker)");
88 if ((s = [ud stringForKey:@"SOGoInternetDetectQualifier"]) != nil) {
89 internetDetectQualifier =
90 [[EOQualifier qualifierWithQualifierFormat:s] retain];
91 if (internetDetectQualifier == nil)
92 NSLog(@"UIxMailEditor: could not parse qualifier: '%@'", s);
94 if (internetDetectQualifier == nil)
95 NSLog(@"UIxMailEditor: no 'SOGoInternetDetectQualifier' configured.");
97 NSLog(@"UIxMailEditor: detect Internet access using: %@",
98 internetDetectQualifier);
101 internetMailHeaders =
102 [[ud dictionaryForKey:@"SOGoInternetMailHeaders"] copy];
103 NSLog(@"Note: specified %d headers for mails send via the Internet.",
104 [internetMailHeaders count]);
108 [self->sentFolder release];
109 [self->fromEMails release];
110 [self->from release];
111 [self->text release];
112 [self->subject release];
121 - (void)setFrom:(NSString *)_value {
122 ASSIGNCOPY(self->from, _value);
125 if (![self->from isNotNull])
126 return [self emailForUser];
130 - (void)setReplyTo:(NSString *)_ignore {
132 - (NSString *)replyTo {
133 /* we are here for future extensibility */
137 - (void)setSubject:(NSString *)_value {
138 ASSIGNCOPY(self->subject, _value);
140 - (NSString *)subject {
141 return self->subject ? self->subject : @"";
144 - (void)setText:(NSString *)_value {
145 ASSIGNCOPY(self->text, _value);
148 return [self->text isNotNull] ? self->text : @"";
151 - (void)setTo:(NSArray *)_value {
152 ASSIGNCOPY(self->to, _value);
155 return [self->to isNotNull] ? self->to : [NSArray array];
158 - (void)setCc:(NSArray *)_value {
159 ASSIGNCOPY(self->cc, _value);
162 return [self->cc isNotNull] ? self->cc : [NSArray array];
165 - (void)setBcc:(NSArray *)_value {
166 ASSIGNCOPY(self->bcc, _value);
169 return [self->bcc isNotNull] ? self->bcc : [NSArray array];
172 - (BOOL)hasOneOrMoreRecipients {
173 if ([[self to] count] > 0) return YES;
174 if ([[self cc] count] > 0) return YES;
175 if ([[self bcc] count] > 0) return YES;
181 - (NSArray *)fromEMails {
182 NSString *primary, *uid;
185 if (self->fromEMails != nil)
186 return self->fromEMails;
188 uid = [[self user] login];
189 primary = [self emailForUser];
190 if (![[self context] isAccessFromIntranet]) {
191 self->fromEMails = [[NSArray alloc] initWithObjects:&primary count:1];
192 return self->fromEMails;
196 [[[self context] activeUser] valueForKey:@"additionalEMailAddresses"];
197 if ([shares count] == 0)
198 self->fromEMails = [[NSArray alloc] initWithObjects:&primary count:1];
202 tmp = [[NSArray alloc] initWithObjects:&primary count:1];
203 self->fromEMails = [[tmp arrayByAddingObjectsFromArray:shares] copy];
204 [tmp release]; tmp = nil;
206 return self->fromEMails;
211 - (NSString *)panelTitle {
212 return [self labelForKey:@"Compose Mail"];
215 /* detect webmail being accessed from the outside */
217 - (BOOL)isInternetRequest {
218 return [[self context] isAccessFromIntranet] ? NO : YES;
221 - (BOOL)showInternetMarker {
222 if (!showInternetMarker)
224 return [self isInternetRequest];
229 - (void)loadInfo:(NSDictionary *)_info {
230 if (![_info isNotNull]) return;
231 [self debugWithFormat:@"loading info ..."];
232 [self takeValuesFromDictionary:_info];
234 - (NSDictionary *)storeInfo {
235 [self debugWithFormat:@"storing info ..."];
236 return [self valuesForKeys:infoKeys];
241 - (BOOL)shouldTakeValuesFromRequest:(WORequest *)_rq inContext:(WOContext*)_c{
247 - (NSException *)patchFlagsInStore {
250 if the draft is a reply => [message markAnswered]
251 if the draft is a forward => [message addFlag:@"forwarded"]
253 This is hard, we would need to find the original message in Cyrus.
258 - (id)lookupSentFolder {
259 SOGoMailAccount *account;
260 SOGoMailFolder *folder;
262 if (self->sentFolder != nil)
265 account = [[self clientObject] mailAccountFolder];
266 if ([account isKindOfClass:[NSException class]]) return account;
268 folder = [account sentFolderInContext:[self context]];
269 if ([folder isKindOfClass:[NSException class]]) return folder;
270 return ((self->sentFolder = [folder retain]));
273 - (NSException *)storeMailInSentFolder:(NSString *)_path {
274 SOGoMailFolder *folder;
278 folder = [self lookupSentFolder];
279 if ([folder isKindOfClass:[NSException class]]) return (id)folder;
281 if ((data = [[NSData alloc] initWithContentsOfMappedFile:_path]) == nil) {
282 return [NSException exceptionWithHTTPStatus:500 /* server error */
283 reason:@"could not temporary draft file!"];
286 result = [folder postData:data flags:@"seen"];
287 [data release]; data = nil;
293 - (BOOL)_saveFormInfo {
296 if ((info = [self storeInfo]) != nil) {
299 if ((error = [[self clientObject] storeInfo:info]) != nil) {
300 [self errorWithFormat:@"failed to store draft: %@", error];
301 // TODO: improve error handling
306 // TODO: wrap content
310 - (id)failedToSaveFormResponse {
311 // TODO: improve error handling
312 return [NSException exceptionWithHTTPStatus:500 /* server error */
313 reason:@"failed to store draft object on server!"];
316 - (id)defaultAction {
317 return [self redirectToLocation:@"edit"];
322 [self logWithFormat:@"edit action, load content from: %@",
323 [self clientObject]];
326 [self loadInfo:[[self clientObject] fetchInfo]];
331 return [self _saveFormInfo] ? self : [self failedToSaveFormResponse];
334 - (NSException *)validateForSend {
335 // TODO: localize errors
337 if (![self hasOneOrMoreRecipients]) {
338 return [NSException exceptionWithHTTPStatus:400 /* Bad Request */
339 reason:@"Please select a recipient!"];
341 if ([[self subject] length] == 0) {
342 return [NSException exceptionWithHTTPStatus:400 /* Bad Request */
343 reason:@"Please set a subject!"];
354 // TODO: need to validate whether we have a To etc
356 /* first, save form data */
358 if (![self _saveFormInfo])
359 return [self failedToSaveFormResponse];
361 /* validate for send */
363 if ((error = [self validateForSend]) != nil) {
366 url = [[error reason] stringByEscapingURL];
367 url = [@"edit?error=" stringByAppendingString:url];
368 return [self redirectToLocation:url];
371 /* setup some extra headers if required */
373 h = [self isInternetRequest] ? internetMailHeaders : nil;
375 /* save mail to file (so that we can upload the mail to Cyrus) */
376 // TODO: all this could be handled by the SOGoDraftObject?
378 mailPath = [[self clientObject] saveMimeMessageToTemporaryFileWithHeaders:h];
380 /* then, send mail */
382 if ((error = [[self clientObject] sendMimeMessageAtPath:mailPath]) != nil) {
383 // TODO: improve error handling
384 [[NSFileManager defaultManager] removeFileAtPath:mailPath handler:nil];
388 /* patch flags in store for replies etc */
390 if ((error = [self patchFlagsInStore]) != nil)
393 /* finally store in Sent */
395 if ((error = [self storeMailInSentFolder:mailPath]) != nil)
398 /* delete temporary mail file */
401 [self warnWithFormat:@"keeping mail file: '%@'", mailPath];
403 [[NSFileManager defaultManager] removeFileAtPath:mailPath handler:nil];
408 if ((error = [[self clientObject] delete]) != nil)
411 // if everything is ok, close the window (send a JS closing the Window)
412 return [self pageWithName:@"UIxMailWindowCloser"];
419 if ((error = [[self clientObject] delete]) != nil)
423 page = [self pageWithName:@"UIxMailWindowCloser"];
424 [page takeValue:@"YES" forKey:@"refreshOpener"];
427 // TODO: if we just return nil, we produce a 500
428 return [NSException exceptionWithHTTPStatus:204 /* No Content */
429 reason:@"object was deleted."];
433 @end /* UIxMailEditor */