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