]> err.no Git - scalable-opengroupware.org/blob - SOGo/UI/MailerUI/UIxMailEditor.m
use new SoUser API instead of AgenorUserManager
[scalable-opengroupware.org] / SOGo / UI / MailerUI / 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   NSArray  *fromEMails;
41   NSString *from;
42   SOGoMailFolder *sentFolder;
43 }
44
45 @end
46
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>
53 #include "common.h"
54
55 @interface UIxComponent (Scheduler_Privates)
56 - (NSString *)emailForUser;
57 @end
58
59 @implementation UIxMailEditor
60
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;
66
67 + (void)initialize {
68   NSUserDefaults *ud = [NSUserDefaults standardUserDefaults];
69   NSString *s;
70   
71   infoKeys = [[NSArray alloc] initWithObjects:
72                                 @"subject", @"text", @"to", @"cc", @"bcc", 
73                                 @"from", @"replyTo",
74                               nil];
75   
76   keepMailTmpFile = [ud boolForKey:@"SOGoMailEditorKeepTmpFile"];
77   if (keepMailTmpFile)
78     NSLog(@"WARNING: keeping mail files.");
79
80   /* Internet mail settings */
81   
82   showInternetMarker = [ud boolForKey:@"SOGoShowInternetMarker"];
83   if (!showInternetMarker) {
84     NSLog(@"Note: visual Internet marker on mail editor disabled "
85           @"(SOGoShowInternetMarker)");
86   }
87   
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);
93   }
94   if (internetDetectQualifier == nil)
95     NSLog(@"UIxMailEditor: no 'SOGoInternetDetectQualifier' configured.");
96   else {
97     NSLog(@"UIxMailEditor: detect Internet access using: %@", 
98           internetDetectQualifier);
99   }
100   
101   internetMailHeaders = 
102     [[ud dictionaryForKey:@"SOGoInternetMailHeaders"] copy];
103   NSLog(@"Note: specified %d headers for mails send via the Internet.", 
104         [internetMailHeaders count]);
105 }
106
107 - (void)dealloc {
108   [self->sentFolder release];
109   [self->fromEMails release];
110   [self->from    release];
111   [self->text    release];
112   [self->subject release];
113   [self->to      release];
114   [self->cc      release];
115   [self->bcc     release];
116   [super dealloc];
117 }
118
119 /* accessors */
120
121 - (void)setFrom:(NSString *)_value {
122   ASSIGNCOPY(self->from, _value);
123 }
124 - (NSString *)from {
125   if (![self->from isNotNull])
126     return [self emailForUser];
127   return self->from;
128 }
129
130 - (void)setReplyTo:(NSString *)_ignore {
131 }
132 - (NSString *)replyTo {
133   /* we are here for future extensibility */
134   return @"";
135 }
136
137 - (void)setSubject:(NSString *)_value {
138   ASSIGNCOPY(self->subject, _value);
139 }
140 - (NSString *)subject {
141   return self->subject ? self->subject : @"";
142 }
143
144 - (void)setText:(NSString *)_value {
145   ASSIGNCOPY(self->text, _value);
146 }
147 - (NSString *)text {
148   return [self->text isNotNull] ? self->text : @"";
149 }
150
151 - (void)setTo:(NSArray *)_value {
152   ASSIGNCOPY(self->to, _value);
153 }
154 - (NSArray *)to {
155   return [self->to isNotNull] ? self->to : [NSArray array];
156 }
157
158 - (void)setCc:(NSArray *)_value {
159   ASSIGNCOPY(self->cc, _value);
160 }
161 - (NSArray *)cc {
162   return [self->cc isNotNull] ? self->cc : [NSArray array];
163 }
164
165 - (void)setBcc:(NSArray *)_value {
166   ASSIGNCOPY(self->bcc, _value);
167 }
168 - (NSArray *)bcc {
169   return [self->bcc isNotNull] ? self->bcc : [NSArray array];
170 }
171
172 /* from addresses */
173
174 - (NSArray *)fromEMails {
175   NSString *primary, *uid;
176   NSArray  *shares;
177   
178   if (self->fromEMails != nil) 
179     return self->fromEMails;
180   
181   uid     = [[self user] login];
182   primary = [self emailForUser];
183   if (![[self context] isAccessFromIntranet]) {
184     self->fromEMails = [[NSArray alloc] initWithObjects:&primary count:1];
185     return self->fromEMails;
186   }
187   
188   shares = 
189     [[[self context] activeUser] valueForKey:@"primaryIMAP4AccountString"];
190   if ([shares count] == 0)
191     self->fromEMails = [[NSArray alloc] initWithObjects:&primary count:1];
192   else {
193     id tmp;
194
195     tmp = [[NSArray alloc] initWithObjects:&primary count:1];
196     self->fromEMails = [[tmp arrayByAddingObjectsFromArray:shares] copy];
197     [tmp release]; tmp = nil;
198   }
199   return self->fromEMails;
200 }
201
202 /* title */
203
204 - (NSString *)panelTitle {
205   return [self labelForKey:@"Compose Mail"];
206 }
207
208 /* detect webmail being accessed from the outside */
209
210 - (BOOL)isInternetRequest {
211   return [[self context] isAccessFromIntranet] ? NO : YES;
212 }
213
214 - (BOOL)showInternetMarker {
215   if (!showInternetMarker)
216     return NO;
217   return [self isInternetRequest];
218 }
219
220 /* info loading */
221
222 - (void)loadInfo:(NSDictionary *)_info {
223   if (![_info isNotNull]) return;
224   [self debugWithFormat:@"loading info ..."];
225   [self takeValuesFromDictionary:_info];
226 }
227 - (NSDictionary *)storeInfo {
228   [self debugWithFormat:@"storing info ..."];
229   return [self valuesForKeys:infoKeys];
230 }
231
232 /* requests */
233
234 - (BOOL)shouldTakeValuesFromRequest:(WORequest *)_rq inContext:(WOContext*)_c{
235   return YES;
236 }
237
238 /* IMAP4 store */
239
240 - (NSException *)patchFlagsInStore {
241   /*
242     Flags we should set:
243       if the draft is a reply   => [message markAnswered]
244       if the draft is a forward => [message addFlag:@"forwarded"]
245       
246     This is hard, we would need to find the original message in Cyrus.
247   */
248   return nil;
249 }
250
251 - (id)lookupSentFolder {
252   SOGoMailAccount *account;
253   SOGoMailFolder  *folder;
254   
255   if (self->sentFolder != nil)
256     return self;
257   
258   account = [[self clientObject] mailAccountFolder];
259   if ([account isKindOfClass:[NSException class]]) return account;
260   
261   folder = [account sentFolderInContext:[self context]];
262   if ([folder isKindOfClass:[NSException class]]) return folder;
263   return ((self->sentFolder = [folder retain]));
264 }
265
266 - (NSException *)storeMailInSentFolder:(NSString *)_path {
267   SOGoMailFolder *folder;
268   NSData *data;
269   id result;
270   
271   folder = [self lookupSentFolder];
272   if ([folder isKindOfClass:[NSException class]]) return (id)folder;
273   
274   if ((data = [[NSData alloc] initWithContentsOfMappedFile:_path]) == nil) {
275     return [NSException exceptionWithHTTPStatus:500 /* server error */
276                         reason:@"could not temporary draft file!"];
277   }
278   
279   result = [folder postData:data flags:@"seen"];
280   [data release]; data = nil;
281   return result;
282 }
283
284 /* actions */
285
286 - (BOOL)_saveFormInfo {
287   NSDictionary *info;
288   
289   if ((info = [self storeInfo]) != nil) {
290     NSException *error;
291     
292     if ((error = [[self clientObject] storeInfo:info]) != nil) {
293       [self errorWithFormat:@"failed to store draft: %@", error];
294       // TODO: improve error handling
295       return NO;
296     }
297   }
298   
299   // TODO: wrap content
300   
301   return YES;
302 }
303 - (id)failedToSaveFormResponse {
304   // TODO: improve error handling
305   return [NSException exceptionWithHTTPStatus:500 /* server error */
306                       reason:@"failed to store draft object on server!"];
307 }
308
309 - (id)defaultAction {
310   return [self redirectToLocation:@"edit"];
311 }
312
313 - (id)editAction {
314 #if 0
315   [self logWithFormat:@"edit action, load content from: %@",
316           [self clientObject]];
317 #endif
318   
319   [self loadInfo:[[self clientObject] fetchInfo]];
320   return self;
321 }
322
323 - (id)saveAction {
324   return [self _saveFormInfo] ? self : [self failedToSaveFormResponse];
325 }
326
327 - (id)sendAction {
328   NSException  *error;
329   NSString     *mailPath;
330   NSDictionary *h;
331   
332   // TODO: need to validate whether we have a To etc
333   
334   /* first, save form data */
335   
336   if (![self _saveFormInfo])
337     return [self failedToSaveFormResponse];
338
339   /* setup some extra headers if required */
340   
341   h = [self isInternetRequest] ? internetMailHeaders : nil;
342   
343   /* save mail to file (so that we can upload the mail to Cyrus) */
344   // TODO: all this could be handled by the SOGoDraftObject?
345   
346   mailPath = [[self clientObject] saveMimeMessageToTemporaryFileWithHeaders:h];
347   
348   /* then, send mail */
349   
350   if ((error = [[self clientObject] sendMimeMessageAtPath:mailPath]) != nil) {
351     // TODO: improve error handling
352     [[NSFileManager defaultManager] removeFileAtPath:mailPath handler:nil];
353     return error;
354   }
355   
356   /* patch flags in store for replies etc */
357   
358   if ((error = [self patchFlagsInStore]) != nil)
359      return error;
360   
361   /* finally store in Sent */
362
363   if ((error = [self storeMailInSentFolder:mailPath]) != nil)
364     return error;
365   
366   /* delete temporary mail file */
367   
368   if (keepMailTmpFile)
369     [self warnWithFormat:@"keeping mail file: '%@'", mailPath];
370   else
371     [[NSFileManager defaultManager] removeFileAtPath:mailPath handler:nil];
372   mailPath = nil;
373   
374   /* delete draft */
375   
376   if ((error = [[self clientObject] delete]) != nil)
377     return error;
378
379   // if everything is ok, close the window (send a JS closing the Window)
380   return [self pageWithName:@"UIxMailWindowCloser"];
381 }
382
383 - (id)deleteAction {
384   NSException *error;
385   id page;
386   
387   if ((error = [[self clientObject] delete]) != nil)
388     return error;
389   
390 #if 1
391   page = [self pageWithName:@"UIxMailWindowCloser"];
392   [page takeValue:@"YES" forKey:@"refreshOpener"];
393   return page;
394 #else
395   // TODO: if we just return nil, we produce a 500
396   return [NSException exceptionWithHTTPStatus:204 /* No Content */
397                       reason:@"object was deleted."];
398 #endif
399 }
400
401 @end /* UIxMailEditor */