]> err.no Git - scalable-opengroupware.org/blob - SOGo/UI/Mailer/UIxMailEditor.m
made Internet request detection configurable,
[scalable-opengroupware.org] / SOGo / UI / Mailer / UIxMailEditor.m
1 /*
2   Copyright (C) 2004-2005 SKYRIX Software AG
3
4   This file is part of OpenGroupware.org.
5
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
9   later version.
10
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.
15
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
19   02111-1307, USA.
20 */
21
22 #include <SOGoUI/UIxComponent.h>
23
24 /*
25   UIxMailEditor
26   
27   An mail editor component which works on SOGoDraftObject's.
28 */
29
30 @class NSArray, NSString;
31 @class SOGoMailFolder;
32
33 @interface UIxMailEditor : UIxComponent
34 {
35   NSArray  *to;
36   NSArray  *cc;
37   NSArray  *bcc;
38   NSString *subject;
39   NSString *text;
40   SOGoMailFolder *sentFolder;
41 }
42
43 @end
44
45 #include <SOGo/SoObjects/Mailer/SOGoDraftObject.h>
46 #include <SOGo/SoObjects/Mailer/SOGoMailFolder.h>
47 #include <SOGo/SoObjects/Mailer/SOGoMailAccount.h>
48 #include <NGMail/NGMimeMessage.h>
49 #include <NGMail/NGMimeMessageGenerator.h>
50 #include "common.h"
51
52 @interface UIxComponent (Scheduler_Privates)
53 - (NSString *)emailForUser;
54 @end
55
56 @implementation UIxMailEditor
57
58 static BOOL         keepMailTmpFile    = NO;
59 static BOOL         showInternetMarker = NO;
60 static EOQualifier  *internetDetectQualifier = nil;
61 static NSDictionary *internetMailHeaders     = nil;
62 static NSArray      *infoKeys = nil;
63
64 + (void)initialize {
65   NSUserDefaults *ud = [NSUserDefaults standardUserDefaults];
66   NSString *s;
67   
68   infoKeys = [[NSArray alloc] initWithObjects:
69                                 @"subject", @"text", @"to", @"cc", @"bcc", 
70                                 @"from", @"replyTo",
71                               nil];
72   
73   keepMailTmpFile = [ud boolForKey:@"SOGoMailEditorKeepTmpFile"];
74   if (keepMailTmpFile)
75     NSLog(@"WARNING: keeping mail files.");
76
77   /* Internet mail settings */
78   
79   showInternetMarker = [ud boolForKey:@"SOGoShowInternetMarker"];
80   if (!showInternetMarker) {
81     NSLog(@"Note: visual Internet marker on mail editor disabled "
82           @"(SOGoShowInternetMarker)");
83   }
84   
85   if ((s = [ud stringForKey:@"SOGoInternetDetectQualifier"]) != nil) {
86     internetDetectQualifier = 
87       [[EOQualifier qualifierWithQualifierFormat:s] retain];
88     if (internetDetectQualifier == nil)
89       NSLog(@"ERROR: could not parse qualifier: '%@'", s);
90   }
91   if (internetDetectQualifier == nil)
92     NSLog(@"Note: no 'SOGoInternetDetectQualifier' configured.");
93   else
94     NSLog(@"Note: detect Internet access using: %@", internetDetectQualifier);
95   
96   internetMailHeaders = 
97     [[ud dictionaryForKey:@"SOGoInternetMailHeaders"] copy];
98   NSLog(@"Note: specified %d headers for mails send via the Internet.", 
99         [internetMailHeaders count]);
100 }
101
102 - (void)dealloc {
103   [self->sentFolder release];
104   [self->text    release];
105   [self->subject release];
106   [self->to      release];
107   [self->cc      release];
108   [self->bcc     release];
109   [super dealloc];
110 }
111
112 /* accessors */
113
114 - (void)setFrom:(NSString *)_ignore {
115 }
116 - (NSString *)from {
117   return [self emailForUser];
118 }
119 - (void)setReplyTo:(NSString *)_ignore {
120 }
121 - (NSString *)replyTo {
122   /* we are here for future extensibility */
123   return @"";
124 }
125
126 - (void)setSubject:(NSString *)_value {
127   ASSIGNCOPY(self->subject, _value);
128 }
129 - (NSString *)subject {
130   return self->subject ? self->subject : @"";
131 }
132
133 - (void)setText:(NSString *)_value {
134   ASSIGNCOPY(self->text, _value);
135 }
136 - (NSString *)text {
137   return [self->text isNotNull] ? self->text : @"";
138 }
139
140 - (void)setTo:(NSArray *)_value {
141   ASSIGNCOPY(self->to, _value);
142 }
143 - (NSArray *)to {
144   return [self->to isNotNull] ? self->to : [NSArray array];
145 }
146
147 - (void)setCc:(NSArray *)_value {
148   ASSIGNCOPY(self->cc, _value);
149 }
150 - (NSArray *)cc {
151   return [self->cc isNotNull] ? self->cc : [NSArray array];
152 }
153
154 - (void)setBcc:(NSArray *)_value {
155   ASSIGNCOPY(self->bcc, _value);
156 }
157 - (NSArray *)bcc {
158   return [self->bcc isNotNull] ? self->bcc : [NSArray array];
159 }
160
161 /* title */
162
163 - (NSString *)panelTitle {
164   return [self labelForKey:@"Compose Mail"];
165 }
166
167 /* detect webmail being accessed from the outside */
168
169 - (BOOL)isInternetRequest {
170   // TODO: make configurable! (eg allow specification of a qualifier)
171   WORequest *rq;
172   
173   rq = [[self context] request];
174   return [(id<EOQualifierEvaluation>)internetDetectQualifier
175                                      evaluateWithObject:[rq headers]];
176 }
177
178 - (BOOL)showInternetMarker {
179   if (!showInternetMarker)
180     return NO;
181   return [self isInternetRequest];
182 }
183
184 /* info loading */
185
186 - (void)loadInfo:(NSDictionary *)_info {
187   if (![_info isNotNull]) return;
188   [self debugWithFormat:@"loading info ..."];
189   [self takeValuesFromDictionary:_info];
190 }
191 - (NSDictionary *)storeInfo {
192   [self debugWithFormat:@"storing info ..."];
193   return [self valuesForKeys:infoKeys];
194 }
195
196 /* requests */
197
198 - (BOOL)shouldTakeValuesFromRequest:(WORequest *)_rq inContext:(WOContext*)_c{
199   return YES;
200 }
201
202 /* IMAP4 store */
203
204 - (NSException *)patchFlagsInStore {
205   /*
206     Flags we should set:
207       if the draft is a reply   => [message markAnswered]
208       if the draft is a forward => [message addFlag:@"forwarded"]
209       
210     This is hard, we would need to find the original message in Cyrus.
211   */
212   return nil;
213 }
214
215 - (id)lookupSentFolder {
216   SOGoMailAccount *account;
217   SOGoMailFolder  *folder;
218   
219   if (self->sentFolder != nil)
220     return self;
221   
222   account = [[self clientObject] mailAccountFolder];
223   if ([account isKindOfClass:[NSException class]]) return account;
224   
225   folder = [account sentFolderInContext:[self context]];
226   if ([folder isKindOfClass:[NSException class]]) return folder;
227   return ((self->sentFolder = [folder retain]));
228 }
229
230 - (NSException *)storeMailInSentFolder:(NSString *)_path {
231   SOGoMailFolder *folder;
232   NSData *data;
233   id result;
234   
235   folder = [self lookupSentFolder];
236   if ([folder isKindOfClass:[NSException class]]) return (id)folder;
237   
238   if ((data = [[NSData alloc] initWithContentsOfMappedFile:_path]) == nil) {
239     return [NSException exceptionWithHTTPStatus:500 /* server error */
240                         reason:@"could not temporary draft file!"];
241   }
242   
243   result = [folder postData:data flags:@"seen"];
244   [data release]; data = nil;
245   return result;
246 }
247
248 /* actions */
249
250 - (BOOL)_saveFormInfo {
251   NSDictionary *info;
252   
253   if ((info = [self storeInfo]) != nil) {
254     NSException *error;
255     
256     if ((error = [[self clientObject] storeInfo:info]) != nil) {
257       [self errorWithFormat:@"failed to store draft: %@", error];
258       // TODO: improve error handling
259       return NO;
260     }
261   }
262   
263   // TODO: wrap content
264   
265   return YES;
266 }
267 - (id)failedToSaveFormResponse {
268   // TODO: improve error handling
269   return [NSException exceptionWithHTTPStatus:500 /* server error */
270                       reason:@"failed to store draft object on server!"];
271 }
272
273 - (id)defaultAction {
274   return [self redirectToLocation:@"edit"];
275 }
276
277 - (id)editAction {
278   [self logWithFormat:@"edit action, load content from: %@",
279           [self clientObject]];
280   
281   [self loadInfo:[[self clientObject] fetchInfo]];
282   return self;
283 }
284
285 - (id)saveAction {
286   return [self _saveFormInfo] ? self : [self failedToSaveFormResponse];
287 }
288
289 - (id)sendAction {
290   NSException  *error;
291   NSString     *mailPath;
292   NSDictionary *h;
293   
294   // TODO: need to validate whether we have a To etc
295   
296   /* first, save form data */
297   
298   if (![self _saveFormInfo])
299     return [self failedToSaveFormResponse];
300
301   /* setup some extra headers if required */
302   
303   h = [self isInternetRequest] ? internetMailHeaders : nil;
304   
305   /* save mail to file (so that we can upload the mail to Cyrus) */
306   // TODO: all this could be handled by the SOGoDraftObject?
307   
308   mailPath = [[self clientObject] saveMimeMessageToTemporaryFileWithHeaders:h];
309   
310   /* then, send mail */
311   
312   if ((error = [[self clientObject] sendMimeMessageAtPath:mailPath]) != nil) {
313     // TODO: improve error handling
314     [[NSFileManager defaultManager] removeFileAtPath:mailPath handler:nil];
315     return error;
316   }
317   
318   /* patch flags in store for replies etc */
319   
320   if ((error = [self patchFlagsInStore]) != nil)
321      return error;
322   
323   /* finally store in Sent */
324
325   if ((error = [self storeMailInSentFolder:mailPath]) != nil)
326     return error;
327   
328   /* delete temporary mail file */
329   
330   if (keepMailTmpFile)
331     [self warnWithFormat:@"keeping mail file: '%@'", mailPath];
332   else
333     [[NSFileManager defaultManager] removeFileAtPath:mailPath handler:nil];
334   mailPath = nil;
335   
336   /* delete draft */
337   
338   if ((error = [[self clientObject] delete]) != nil)
339     return error;
340
341   // if everything is ok, close the window (send a JS closing the Window)
342   return [self pageWithName:@"UIxMailWindowCloser"];
343 }
344
345 - (id)deleteAction {
346   NSException *error;
347   
348   if ((error = [[self clientObject] delete]) != nil)
349     return error;
350   
351   return nil;
352 }
353
354 @end /* UIxMailEditor */