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