]> err.no Git - scalable-opengroupware.org/blob - SoObjects/Mailer/SOGoMailAccount.m
git-svn-id: http://svn.opengroupware.org/SOGo/inverse/trunk@1207 d1b88da0-ebda-0310...
[scalable-opengroupware.org] / SoObjects / Mailer / SOGoMailAccount.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 #import <Foundation/NSArray.h>
23 #import <Foundation/NSURL.h>
24 #import <Foundation/NSString.h>
25 #import <Foundation/NSUserDefaults.h>
26
27 #import <NGObjWeb/NSException+HTTP.h>
28 #import <NGObjWeb/SoHTTPAuthenticator.h>
29 #import <NGObjWeb/WORequest.h>
30 #import <NGObjWeb/WOContext+SoObjects.h>
31 #import <NGExtensions/NSObject+Logs.h>
32 #import <NGExtensions/NSNull+misc.h>
33 #import <NGImap4/NGImap4Connection.h>
34
35 #import <SoObjects/SOGo/NSArray+Utilities.h>
36 #import <SoObjects/SOGo/SOGoUser.h>
37
38 #import "SOGoMailFolder.h"
39 #import "SOGoMailManager.h"
40 #import "SOGoDraftsFolder.h"
41
42 #import "SOGoMailAccount.h"
43
44 @implementation SOGoMailAccount
45
46 static NSArray *rootFolderNames = nil;
47 static NSString *inboxFolderName = @"INBOX";
48 static NSString *draftsFolderName = @"Drafts";
49 static NSString *sieveFolderName = @"Filters";
50 static NSString *sentFolderName = nil;
51 static NSString *trashFolderName = nil;
52 static NSString *sharedFolderName = @""; // TODO: add English default
53 static NSString *otherUsersFolderName = @""; // TODO: add English default
54
55 + (void) initialize
56 {
57   NSUserDefaults *ud = [NSUserDefaults standardUserDefaults];
58   NSString *cfgDraftsFolderName;
59
60   sharedFolderName = [ud stringForKey:@"SOGoSharedFolderName"];
61   otherUsersFolderName = [ud stringForKey:@"SOGoOtherUsersFolderName"];
62   cfgDraftsFolderName = [ud stringForKey:@"SOGoDraftsFolderName"];
63   if (!sentFolderName)
64     {
65       sentFolderName = [ud stringForKey: @"SOGoSentFolderName"];
66       if (!sentFolderName)
67         sentFolderName = @"Sent";
68       [sentFolderName retain];
69     }
70   if (!trashFolderName)
71     {
72       trashFolderName = [ud stringForKey: @"SOGoTrashFolderName"];
73       if (!trashFolderName)
74         trashFolderName = @"Trash";
75       [trashFolderName retain];
76     }
77   if ([cfgDraftsFolderName length] > 0)
78     {
79       ASSIGN (draftsFolderName, cfgDraftsFolderName);
80       NSLog(@"Note: using drafts folder named:      '%@'", draftsFolderName);
81     }
82
83   NSLog(@"Note: using shared-folders name:      '%@'", sharedFolderName);
84   NSLog(@"Note: using other-users-folders name: '%@'", otherUsersFolderName);
85   if ([ud boolForKey: @"SOGoEnableSieveFolder"])
86     rootFolderNames = [[NSArray alloc] initWithObjects:
87                                         draftsFolderName, 
88                                         sieveFolderName, 
89                                       nil];
90   else
91     rootFolderNames = [[NSArray alloc] initWithObjects:
92                                         draftsFolderName, 
93                                       nil];
94 }
95
96 - (id) init
97 {
98   if ((self = [super init]))
99     {
100       inboxFolder = nil;
101       draftsFolder = nil;
102       sentFolder = nil;
103       trashFolder = nil;
104     }
105
106   return self;
107 }
108
109 - (void) dealloc
110 {
111   [inboxFolder release];
112   [draftsFolder release];
113   [sentFolder release];
114   [trashFolder release];
115   [super dealloc];  
116 }
117
118 /* shared accounts */
119
120 - (BOOL) isSharedAccount
121 {
122   NSString *s;
123   NSRange  r;
124   
125   s = [self nameInContainer];
126   r = [s rangeOfString:@"@"];
127   if (r.length == 0) /* regular HTTP logins are never a shared mailbox */
128     return NO;
129   
130   s = [s substringToIndex:r.location];
131   return [s rangeOfString:@".-."].length > 0 ? YES : NO;
132 }
133
134 - (NSString *) sharedAccountName
135 {
136   return nil;
137 }
138
139 /* listing the available folders */
140
141 - (NSArray *) additionalRootFolderNames
142 {
143   return rootFolderNames;
144 }
145
146 - (BOOL) isInDraftsFolder
147 {
148   return NO;
149 }
150
151 - (NSArray *) toManyRelationshipKeys
152 {
153   NSMutableArray *folders;
154   NSArray *imapFolders, *additionalFolders;
155
156   folders = [NSMutableArray array];
157
158   imapFolders = [[self imap4Connection] subfoldersForURL: [self imap4URL]];
159   additionalFolders = [self additionalRootFolderNames];
160   if ([imapFolders count] > 0)
161     [folders addObjectsFromArray: imapFolders];
162   if ([additionalFolders count] > 0)
163     {
164       [folders removeObjectsInArray: additionalFolders];
165       [folders addObjectsFromArray: additionalFolders];
166     }
167   
168   return folders;
169 }
170
171 /* hierarchy */
172
173 - (SOGoMailAccount *) mailAccountFolder
174 {
175   return self;
176 }
177
178 - (NSArray *) allFolderPaths
179 {
180   NSMutableArray *newFolders;
181   NSArray *rawFolders, *mainFolders;
182   NSString *realDraftsFolderName, *realSentFolderName, *realTrashFolderName;
183
184   rawFolders = [[self imap4Connection] allFoldersForURL: [self imap4URL]];
185
186   realDraftsFolderName
187     = [[self draftsFolderInContext: context] traversalFromMailAccount];
188   realSentFolderName
189     = [[self sentFolderInContext: context] traversalFromMailAccount];
190   realTrashFolderName
191     = [[self trashFolderInContext: context] traversalFromMailAccount];
192
193   mainFolders = [NSArray arrayWithObjects: inboxFolderName,
194                          realDraftsFolderName,
195                          realSentFolderName,
196                          realTrashFolderName, nil];
197   newFolders = [NSMutableArray arrayWithArray: rawFolders];
198   [newFolders removeObjectsInArray: mainFolders];
199   [newFolders sortUsingSelector: @selector (caseInsensitiveCompare:)];
200   [newFolders replaceObjectsInRange: NSMakeRange (0, 0)
201               withObjectsFromArray: mainFolders];
202
203   return newFolders;
204 }
205
206 /* IMAP4 */
207
208 - (BOOL) useSSL
209 {
210   return NO;
211 }
212
213 - (NSString *) imap4LoginFromHTTP
214 {
215   WORequest *rq;
216   NSString  *s;
217   NSArray   *creds;
218   
219   rq = [context request];
220   
221   s = [rq headerForKey:@"x-webobjects-remote-user"];
222   if ([s length] > 0)
223     return s;
224   
225   if ((s = [rq headerForKey:@"authorization"]) == nil) {
226     /* no basic auth */
227     return nil;
228   }
229   
230   creds = [SoHTTPAuthenticator parseCredentials:s];
231   if ([creds count] < 2)
232     /* somehow invalid */
233     return nil;
234   
235   return [creds objectAtIndex:0]; /* the user */
236 }
237
238 - (NSMutableString *) imap4URLString
239 {
240   /* private, overridden by SOGoSharedMailAccount */
241   NSMutableString *urlString;
242   NSString *host;
243
244   urlString = [NSMutableString string];
245
246   if ([self useSSL])
247     [urlString appendString: @"imaps://"];
248   else
249     [urlString appendString: @"imap://"];
250
251   host = [self nameInContainer];
252   if (![host rangeOfString: @"@"].length)
253     [urlString appendFormat: @"%@@", [self imap4LoginFromHTTP]];
254   [urlString appendFormat: @"%@/", host];
255
256   return urlString;
257 }
258
259 - (NSMutableString *) traversalFromMailAccount
260 {
261   return [NSMutableString string];
262 }
263
264 - (NSString *) imap4Login
265 {
266   return [[self imap4URL] user];
267 }
268
269 /* name lookup */
270
271 - (id) lookupFolder: (NSString *) _key
272        ofClassNamed: (NSString *) _cn
273           inContext: (id) _cx
274 {
275   Class clazz;
276   SOGoMailFolder *folder;
277
278   if ((clazz = NSClassFromString(_cn)) == Nil)
279     {
280       [self logWithFormat:@"ERROR: did not find class '%@' for key: '%@'", 
281             _cn, _key];
282       return [NSException exceptionWithHTTPStatus:500 /* server error */
283                           reason:@"did not find mail folder class!"];
284     }
285
286   folder = [clazz objectWithName: _key inContainer: self];
287
288   return folder;
289 }
290
291 - (id) lookupSentFolder: (NSString *) _key
292               inContext: (id) _ctx
293 {
294   return [self lookupFolder: _key ofClassNamed: @"SOGoSentFolder" 
295                inContext: _ctx];
296 }
297
298 - (id) lookupDraftsFolder: (NSString *) _key
299                 inContext: (id) _ctx
300 {
301   return [self lookupFolder: _key ofClassNamed: @"SOGoDraftsFolder" 
302                inContext: _ctx];
303 }
304
305 - (id) lookupTrashFolder: (NSString *) _key
306                inContext: (id) _ctx
307 {
308   return [self lookupFolder: _key ofClassNamed: @"SOGoTrashFolder" 
309                inContext: _ctx];
310 }
311
312 - (id) lookupFiltersFolder: (NSString *) _key inContext: (id) _ctx
313 {
314   return [self lookupFolder:_key ofClassNamed:@"SOGoSieveScriptsFolder" 
315                inContext:_ctx];
316 }
317
318 - (id) lookupName: (NSString *) _key
319         inContext: (id)_ctx
320           acquire: (BOOL) _flag
321 {
322   id obj;
323
324   if ([_key hasPrefix: @"folder"])
325     {
326   // TODO: those should be product.plist bindings? (can't be class bindings
327   //       though because they are 'per-account')
328       if ([_key isEqualToString: [self sentFolderNameInContext: _ctx]])
329         obj = [self lookupSentFolder: _key inContext: _ctx];
330       else if ([_key isEqualToString: [self draftsFolderNameInContext: _ctx]])
331         obj = [self lookupDraftsFolder: _key inContext: _ctx];
332       else if ([_key isEqualToString: [self trashFolderNameInContext: _ctx]])
333         obj = [self lookupTrashFolder: _key inContext: _ctx];
334 //       else if ([_key isEqualToString: [self sieveFolderNameInContext: _ctx]])
335 //      obj = [self lookupFiltersFolder: _key inContext: _ctx];
336       else
337         obj = [self lookupFolder: _key ofClassNamed: @"SOGoMailFolder"
338                     inContext: _ctx];
339     }
340   else
341     obj = [super lookupName: _key inContext: _ctx acquire: NO];
342   
343   /* return 404 to stop acquisition */
344   if (!obj)
345     obj = [NSException exceptionWithHTTPStatus: 404 /* Not Found */];
346
347   return obj;
348 }
349
350 /* special folders */
351
352 - (NSString *) inboxFolderNameInContext: (id)_ctx
353 {
354   /* cannot be changed in Cyrus ? */
355   return [NSString stringWithFormat: @"folder%@", inboxFolderName];
356 }
357
358 - (NSString *) _userFolderNameWithPurpose: (NSString *) purpose
359 {
360   NSUserDefaults *ud;
361   NSMutableDictionary *mailSettings;
362   NSString *folderName;
363
364   folderName = nil;
365   ud = [[context activeUser] userSettings];
366   mailSettings = [ud objectForKey: @"Mail"];
367   if (mailSettings)
368     folderName
369       = [mailSettings objectForKey: [NSString stringWithFormat: @"%@Folder",
370                                               purpose]];
371
372   return folderName;
373 }
374
375 - (NSString *) draftsFolderNameInContext: (id) _ctx
376 {
377   NSString *folderName;
378
379   folderName = [self _userFolderNameWithPurpose: @"Drafts"];
380   if (!folderName)
381     folderName = draftsFolderName;
382
383   return [NSString stringWithFormat: @"folder%@", folderName];
384 }
385
386 - (NSString *) sieveFolderNameInContext: (id) _ctx
387 {
388   return [NSString stringWithFormat: @"folder%@", sieveFolderName];
389 }
390
391 - (NSString *) sentFolderNameInContext: (id)_ctx
392 {
393   NSString *folderName;
394
395   folderName = [self _userFolderNameWithPurpose: @"Sent"];
396   if (!folderName)
397     folderName = sentFolderName;
398
399   return [NSString stringWithFormat: @"folder%@", folderName];
400 }
401
402 - (NSString *) trashFolderNameInContext: (id)_ctx
403 {
404   NSString *folderName;
405
406   folderName = [self _userFolderNameWithPurpose: @"Trash"];
407   if (!folderName)
408     folderName = trashFolderName;
409
410   return [NSString stringWithFormat: @"folder%@", folderName];
411 }
412
413 - (SOGoMailFolder *) inboxFolderInContext: (id) _ctx
414 {
415   // TODO: use some profile to determine real location, use a -traverse lookup
416   if (!inboxFolder)
417     {
418       inboxFolder = [self lookupName: [self inboxFolderNameInContext: _ctx]
419                           inContext: _ctx acquire: NO];
420       [inboxFolder retain];
421     }
422
423   return inboxFolder;
424 }
425
426 - (SOGoDraftsFolder *) draftsFolderInContext: (id) _ctx
427 {
428   // TODO: use some profile to determine real location, use a -traverse lookup
429
430   if (!draftsFolder)
431     {
432       draftsFolder
433         = [self lookupName: [self draftsFolderNameInContext:_ctx]
434                 inContext: _ctx acquire: NO];
435 //       if (![draftsFolder isNotNull])
436 //      draftsFolder = [NSException exceptionWithHTTPStatus: 404 /* not found */
437 //                                  reason: @"did not find Drafts folder!"];
438       [draftsFolder retain];
439     }
440
441   return draftsFolder;
442 }
443
444 - (SOGoMailFolder *) sentFolderInContext: (id) _ctx
445 {
446   // TODO: use some profile to determine real location, use a -traverse lookup
447
448   if (!sentFolder)
449     {
450       sentFolder = [self lookupName: [self sentFolderNameInContext:_ctx]
451                          inContext: _ctx acquire: NO];
452 //       if (![sentFolder isNotNull])
453 //      sentFolder = [NSException exceptionWithHTTPStatus: 404 /* not found */
454 //                                reason: @"did not find Sent folder!"];
455       [sentFolder retain];
456     }
457
458   return sentFolder;
459 }
460
461 - (SOGoMailFolder *) trashFolderInContext: (id) _ctx
462 {
463   if (!trashFolder)
464     {
465       trashFolder = [self lookupName: [self trashFolderNameInContext: _ctx]
466                           inContext: _ctx acquire: NO];
467 //       if (![trashFolder isNotNull])
468 //      trashFolder = [NSException exceptionWithHTTPStatus: 404 /* not found */
469 //                                reason: @"did not find Trash folder!"];
470       [trashFolder retain];
471     }
472
473   return trashFolder;
474 }
475
476 /* WebDAV */
477
478 - (NSString *) davContentType
479 {
480   return @"httpd/unix-directory";
481 }
482
483 - (BOOL) davIsCollection
484 {
485   return YES;
486 }
487
488 - (NSException *) davCreateCollection: (NSString *) _name
489                             inContext: (id) _ctx
490 {
491   return [[self imap4Connection] createMailbox:_name atURL:[self imap4URL]];
492 }
493
494 - (NSString *) shortTitle
495 {
496   NSString *s, *login, *host;
497   NSRange r;
498
499   s = [self nameInContainer];
500   
501   r = [s rangeOfString:@"@"];
502   if (r.length > 0) {
503     login = [s substringToIndex:r.location];
504     host  = [s substringFromIndex:(r.location + r.length)];
505   }
506   else {
507     login = nil;
508     host  = s;
509   }
510   
511   r = [host rangeOfString:@"."];
512   if (r.length > 0)
513     host = [host substringToIndex:r.location];
514   
515   if ([login length] == 0)
516     return host;
517   
518   r = [login rangeOfString:@"."];
519   if (r.length > 0)
520     login = [login substringToIndex:r.location];
521   
522   return [NSString stringWithFormat:@"%@@%@", login, host];
523 }
524
525 - (NSString *) davDisplayName
526 {
527   return [self shortTitle];
528 }
529
530 - (NSString *) sharedFolderName
531 {
532   return sharedFolderName;
533 }
534
535 - (NSString *) otherUsersFolderName
536 {
537   return otherUsersFolderName;
538 }
539
540 @end /* SOGoMailAccount */