]> err.no Git - scalable-opengroupware.org/blob - SoObjects/Mailer/SOGoMailAccount.m
git-svn-id: http://svn.opengroupware.org/SOGo/inverse/trunk@1147 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 "SOGoMailFolder.h"
36 #import "SOGoMailManager.h"
37 #import "SOGoDraftsFolder.h"
38
39 #import "SOGoMailAccount.h"
40
41 @implementation SOGoMailAccount
42
43 static NSArray *rootFolderNames = nil;
44 static NSString *inboxFolderName = @"INBOX";
45 static NSString *draftsFolderName = @"Drafts";
46 static NSString *sieveFolderName = @"Filters";
47 static NSString *sentFolderName = nil;
48 static NSString *trashFolderName = nil;
49 static NSString *sharedFolderName = @""; // TODO: add English default
50 static NSString *otherUsersFolderName = @""; // TODO: add English default
51 static BOOL useAltNamespace = NO;
52
53 + (void) initialize
54 {
55   NSUserDefaults *ud = [NSUserDefaults standardUserDefaults];
56   NSString *cfgDraftsFolderName;
57
58   useAltNamespace = [ud boolForKey:@"SOGoSpecialFoldersInRoot"];
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
183   rawFolders = [[self imap4Connection]
184                  allFoldersForURL: [self imap4URL]];
185
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 *) draftsFolderNameInContext: (id) _ctx
337 {
338   /* SOGo managed folder */
339   return [NSString stringWithFormat: @"folder%@", draftsFolderName];
340 }
341
342 - (NSString *) sieveFolderNameInContext: (id) _ctx
343 {
344   return [NSString stringWithFormat: @"folder%@", sieveFolderName];
345 }
346
347 - (NSString *) sentFolderNameInContext: (id)_ctx
348 {
349   return [NSString stringWithFormat: @"folder%@", sentFolderName];
350 }
351
352 - (NSString *) trashFolderNameInContext: (id)_ctx
353 {
354   return [NSString stringWithFormat: @"folder%@", trashFolderName];
355 }
356
357 - (SOGoMailFolder *) inboxFolderInContext: (id) _ctx
358 {
359   // TODO: use some profile to determine real location, use a -traverse lookup
360   if (!inboxFolder)
361     {
362       inboxFolder = [self lookupName: [self inboxFolderNameInContext: _ctx]
363                           inContext: _ctx acquire: NO];
364       [inboxFolder retain];
365     }
366
367   return inboxFolder;
368 }
369
370 - (SOGoDraftsFolder *) draftsFolderInContext: (id) _ctx
371 {
372   SOGoMailFolder *lookupFolder;
373   // TODO: use some profile to determine real location, use a -traverse lookup
374
375   if (!draftsFolder)
376     {
377       lookupFolder = (useAltNamespace
378                       ? (id) self
379                       : [self inboxFolderInContext:_ctx]);
380       if (![lookupFolder isKindOfClass: [NSException class]])
381         draftsFolder
382           = [lookupFolder lookupName: [self draftsFolderNameInContext:_ctx]
383                           inContext: _ctx acquire: NO];
384       if (![draftsFolder isNotNull])
385         draftsFolder = [NSException exceptionWithHTTPStatus: 404 /* not found */
386                                     reason: @"did not find Drafts folder!"];
387       [draftsFolder retain];
388     }
389
390   return draftsFolder;
391 }
392
393 - (SOGoMailFolder *) sentFolderInContext: (id) _ctx
394 {
395   SOGoMailFolder *lookupFolder;
396   // TODO: use some profile to determine real location, use a -traverse lookup
397
398   if (!sentFolder)
399     {
400       lookupFolder = (useAltNamespace
401                       ? (id) self
402                       : [self inboxFolderInContext:_ctx]);
403       if (![lookupFolder isKindOfClass: [NSException class]])
404         sentFolder = [lookupFolder lookupName: [self sentFolderNameInContext:_ctx]
405                                    inContext: _ctx acquire: NO];
406       if (![sentFolder isNotNull])
407         sentFolder = [NSException exceptionWithHTTPStatus: 404 /* not found */
408                                   reason: @"did not find Sent folder!"];
409       [sentFolder retain];
410     }
411
412   return sentFolder;
413 }
414
415 - (SOGoMailFolder *) trashFolderInContext: (id) _ctx
416 {
417   SOGoMailFolder *lookupFolder;
418   // TODO: use some profile to determine real location, use a -traverse lookup
419
420   if (!trashFolder)
421     {
422       lookupFolder = (useAltNamespace
423                       ? (id) self
424                       : [self inboxFolderInContext:_ctx]);
425       if (![lookupFolder isKindOfClass: [NSException class]])
426         trashFolder = [lookupFolder lookupName: [self trashFolderNameInContext: _ctx]
427                                     inContext: _ctx acquire: NO];
428       if (![trashFolder isNotNull])
429         trashFolder = [NSException exceptionWithHTTPStatus: 404 /* not found */
430                                   reason: @"did not find Trash folder!"];
431       [trashFolder retain];
432     }
433
434   return trashFolder;
435 }
436
437 /* WebDAV */
438
439 - (NSString *) davContentType
440 {
441   return @"httpd/unix-directory";
442 }
443
444 - (BOOL) davIsCollection
445 {
446   return YES;
447 }
448
449 - (NSException *) davCreateCollection: (NSString *) _name
450                             inContext: (id) _ctx
451 {
452   return [[self imap4Connection] createMailbox:_name atURL:[self imap4URL]];
453 }
454
455 - (NSString *) shortTitle
456 {
457   NSString *s, *login, *host;
458   NSRange r;
459
460   s = [self nameInContainer];
461   
462   r = [s rangeOfString:@"@"];
463   if (r.length > 0) {
464     login = [s substringToIndex:r.location];
465     host  = [s substringFromIndex:(r.location + r.length)];
466   }
467   else {
468     login = nil;
469     host  = s;
470   }
471   
472   r = [host rangeOfString:@"."];
473   if (r.length > 0)
474     host = [host substringToIndex:r.location];
475   
476   if ([login length] == 0)
477     return host;
478   
479   r = [login rangeOfString:@"."];
480   if (r.length > 0)
481     login = [login substringToIndex:r.location];
482   
483   return [NSString stringWithFormat:@"%@@%@", login, host];
484 }
485
486 - (NSString *) davDisplayName
487 {
488   return [self shortTitle];
489 }
490
491 - (NSString *) sharedFolderName
492 {
493   return sharedFolderName;
494 }
495
496 - (NSString *) otherUsersFolderName
497 {
498   return otherUsersFolderName;
499 }
500
501 @end /* SOGoMailAccount */