]> err.no Git - scalable-opengroupware.org/blob - SoObjects/Mailer/SOGoMailFolder.m
babd5684fff939f5d3e461d212874272485651c1
[scalable-opengroupware.org] / SoObjects / Mailer / SOGoMailFolder.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/NSUserDefaults.h>
23
24 #import <NGObjWeb/NSException+HTTP.h>
25 #import <NGExtensions/NSNull+misc.h>
26 #import <NGExtensions/NSURL+misc.h>
27 #import <NGExtensions/NSObject+Logs.h>
28 #import <NGExtensions/NSString+misc.h>
29
30 #import <NGImap4/NGImap4Connection.h>
31 #import <NGImap4/NGImap4Client.h>
32
33 #import <SoObjects/SOGo/SOGoPermissions.h>
34 #import <SoObjects/SOGo/SOGoUser.h>
35 #import <SoObjects/SOGo/NSArray+Utilities.h>
36
37 #import "SOGoMailObject.h"
38 #import "SOGoMailAccount.h"
39 #import "SOGoMailManager.h"
40 #import "SOGoMailFolder.h"
41
42 static NSString *defaultUserID =  @"anyone";
43
44 @implementation SOGoMailFolder
45
46 static BOOL useAltNamespace = NO;
47
48 + (void) initialize
49 {
50   NSUserDefaults *ud = [NSUserDefaults standardUserDefaults];
51
52   useAltNamespace = [ud boolForKey:@"SOGoSpecialFoldersInRoot"];
53 }
54
55 - (void) _adjustOwner
56 {
57   SOGoMailAccount *mailAccount;
58   NSString *path, *folder;
59   NSArray *names;
60
61   mailAccount = [self mailAccountFolder];
62   path = [[self imap4Connection] imap4FolderNameForURL: [self imap4URL]];
63
64   folder = [mailAccount sharedFolderName];
65   if (folder && [path hasPrefix: folder])
66     [self setOwner: @"anyone"];
67   else
68     {
69       folder = [mailAccount otherUsersFolderName];
70       if (folder && [path hasPrefix: folder])
71         {
72           names = [path componentsSeparatedByString: @"/"];
73           if ([names count] > 1)
74             [self setOwner: [names objectAtIndex: 1]];
75           else
76             [self setOwner: @"anyone"];
77         }
78     }
79 }
80
81 - (id) initWithName: (NSString *) newName
82         inContainer: (id) newContainer
83 {
84   if ((self = [super initWithName: newName
85                      inContainer: newContainer]))
86     {
87       [self _adjustOwner];
88       mailboxACL = nil;
89     }
90
91   return self;
92 }
93
94 - (void) dealloc
95 {
96   [filenames  release];
97   [folderType release];
98   [mailboxACL release];
99   [super dealloc];
100 }
101
102 /* IMAP4 */
103
104 - (NSString *) relativeImap4Name
105 {
106   return [nameInContainer substringFromIndex: 6];
107 }
108
109
110 - (NSMutableString *) imap4URLString
111 {
112   NSMutableString *urlString;
113
114   urlString = [super imap4URLString];
115   [urlString appendString: @"/"];
116
117   return urlString;
118 }
119
120 /* listing the available folders */
121
122 - (NSArray *) toManyRelationshipKeys
123 {
124   return [self subfolders];
125 }
126
127 - (NSArray *) subfolders
128 {
129   return [[self imap4Connection] subfoldersForURL: [self imap4URL]];
130 }
131
132 - (NSArray *) subfoldersURL
133 {
134   NSURL *selfURL, *currentURL;
135   NSMutableArray *subfoldersURL;
136   NSEnumerator *subfolders;
137   NSString *selfPath, *currentFolder;
138
139   subfoldersURL = [NSMutableArray array];
140   selfURL = [self imap4URL];
141   selfPath = [selfURL path];
142   subfolders = [[self subfolders] objectEnumerator];
143   currentFolder = [subfolders nextObject];
144   while (currentFolder)
145     {
146       currentURL = [[NSURL alloc]
147                      initWithScheme: [selfURL scheme]
148                      host: [selfURL host]
149                      path: [selfPath stringByAppendingPathComponent:
150                                        currentFolder]];
151       [currentURL autorelease];
152       [subfoldersURL addObject: currentURL];
153       currentFolder = [subfolders nextObject];
154     }
155
156   return subfoldersURL;
157 }
158
159 - (NSString *) davContentType
160 {
161   return @"httpd/unix-directory";
162 }
163
164 - (NSArray *) toOneRelationshipKeys
165 {
166   NSArray *uids;
167   unsigned int count, max;
168   NSString *filename;
169
170   if (!filenames)
171     {
172       filenames = [NSMutableArray new];
173       uids = [self fetchUIDsMatchingQualifier: nil sortOrdering: @"DATE"];
174       if (![uids isKindOfClass: [NSException class]])
175         {
176           max = [uids count];
177           for (count = 0; count < max; count++)
178             {
179               filename = [NSString stringWithFormat: @"%@.mail",
180                                    [uids objectAtIndex: count]];
181               [filenames addObject: filename];
182             }
183         }
184     }
185
186   return filenames;
187 }
188
189 /* messages */
190
191 - (NSArray *) fetchUIDsMatchingQualifier: (id) _q
192                             sortOrdering: (id) _so
193 {
194   /* seems to return an NSArray of NSNumber's */
195   return [[self imap4Connection] fetchUIDsInURL: [self imap4URL]
196                                  qualifier: _q sortOrdering: _so];
197 }
198
199 - (NSArray *) fetchUIDs: (NSArray *) _uids
200                   parts: (NSArray *) _parts
201 {
202   return [[self imap4Connection] fetchUIDs: _uids inURL: [self imap4URL]
203                                  parts: _parts];
204 }
205
206 - (NSException *) postData: (NSData *) _data
207                      flags: (id) _flags
208 {
209   return [[self imap4Connection] postData: _data flags: _flags
210                                  toFolderURL: [self imap4URL]];
211 }
212
213 - (NSException *) expunge
214 {
215   return [[self imap4Connection] expungeAtURL: [self imap4URL]];
216 }
217
218 /* flags */
219
220 - (NSException *) addFlagsToAllMessages: (id) _f
221 {
222   return [[self imap4Connection] addFlags:_f 
223                                  toAllMessagesInURL: [self imap4URL]];
224 }
225
226 /* name lookup */
227
228 - (id) lookupName: (NSString *) _key
229         inContext: (id)_ctx
230           acquire: (BOOL) _acquire
231 {
232   id obj;
233
234   if ([_key hasPrefix: @"folder"])
235     obj = [SOGoMailFolder objectWithName: _key inContainer: self];
236   else
237     {
238       if ([[self imap4Connection] doesMailboxExistAtURL: [self imap4URL]])
239         {
240           if (isdigit ([_key characterAtIndex: 0]))
241             obj = [SOGoMailObject objectWithName: _key inContainer: self];
242           else
243             obj = [super lookupName: _key inContext: _ctx acquire: NO];
244         }
245       else
246         obj = nil;
247     }
248
249   if (!obj && _acquire)
250     obj = [NSException exceptionWithHTTPStatus: 404 /* Not Found */];
251
252   return obj;
253 }
254
255 /* WebDAV */
256
257 - (BOOL) davIsCollection
258 {
259   return YES;
260 }
261
262 - (NSException *) davCreateCollection: (NSString *) _name
263                             inContext: (id) _ctx
264 {
265   return [[self imap4Connection] createMailbox:_name atURL:[self imap4URL]];
266 }
267
268 - (NSException *) delete
269 {
270   /* Note: overrides SOGoObject -delete */
271   return [[self imap4Connection] deleteMailboxAtURL:[self imap4URL]];
272 }
273
274 - (NSException *) davMoveToTargetObject: (id) _target
275                                 newName: (NSString *) _name
276                               inContext: (id)_ctx
277 {
278   NSURL *destImapURL;
279   
280   if ([_name length] == 0) { /* target already exists! */
281     // TODO: check the overwrite request field (should be done by dispatcher)
282     return [NSException exceptionWithHTTPStatus:412 /* Precondition Failed */
283                         reason:@"target already exists"];
284   }
285   if (![_target respondsToSelector:@selector(imap4URL)]) {
286     return [NSException exceptionWithHTTPStatus:502 /* Bad Gateway */
287                         reason:@"target is not an IMAP4 folder"];
288   }
289   
290   /* build IMAP4 URL for target */
291   
292   destImapURL = [_target imap4URL];
293 // -  destImapURL = [NSURL URLWithString:[[destImapURL path] 
294 // -                                   stringByAppendingPathComponent:_name]
295 // -                   relativeToURL:destImapURL];
296   destImapURL = [NSURL URLWithString: _name
297                        relativeToURL: destImapURL];
298   
299   [self logWithFormat:@"TODO: should move collection as '%@' to: %@",
300         [[self imap4URL] absoluteString], 
301         [destImapURL absoluteString]];
302   
303   return [[self imap4Connection] moveMailboxAtURL:[self imap4URL] 
304                                  toURL:destImapURL];
305 }
306
307 - (NSException *) davCopyToTargetObject: (id) _target
308                                 newName: (NSString *) _name
309                               inContext: (id) _ctx
310 {
311   [self logWithFormat:@"TODO: should copy collection as '%@' to: %@",
312         _name, _target];
313   return [NSException exceptionWithHTTPStatus:501 /* Not Implemented */
314                       reason:@"not implemented"];
315 }
316
317 /* folder type */
318 - (NSString *) folderType
319 {
320   return @"Mail";
321 }
322
323 - (NSString *) outlookFolderClass
324 {
325   // TODO: detect Trash/Sent/Drafts folders
326   SOGoMailAccount *account;
327   NSString *n;
328
329   if (folderType != nil)
330     return folderType;
331   
332   account = [self mailAccountFolder];
333   n = nameInContainer;
334
335   if ([n isEqualToString:[account trashFolderNameInContext:nil]])
336     folderType = @"IPF.Trash";
337   else if ([n isEqualToString:[account inboxFolderNameInContext:nil]])
338     folderType = @"IPF.Inbox";
339   else if ([n isEqualToString:[account sentFolderNameInContext:nil]])
340     folderType = @"IPF.Sent";
341   else
342     folderType = @"IPF.Folder";
343   
344   return folderType;
345 }
346
347 /* acls */
348
349 - (NSArray *) _imapAclsToSOGoAcls: (NSString *) imapAcls
350 {
351   unsigned int count, max;
352   NSMutableArray *SOGoAcls;
353
354   SOGoAcls = [NSMutableArray array];
355   max = [imapAcls length];
356   for (count = 0; count < max; count++)
357     {
358       switch ([imapAcls characterAtIndex: count])
359         {
360         case 'l':
361           [SOGoAcls addObjectUniquely: SOGoRole_ObjectViewer];
362           break;
363         case 'r':
364           [SOGoAcls addObjectUniquely: SOGoRole_ObjectReader];
365           break;
366         case 's':
367           [SOGoAcls addObjectUniquely: SOGoMailRole_SeenKeeper];
368           break;
369         case 'w':
370           [SOGoAcls addObjectUniquely: SOGoMailRole_Writer];
371           break;
372         case 'i':
373           [SOGoAcls addObjectUniquely: SOGoRole_ObjectCreator];
374           break;
375         case 'p':
376           [SOGoAcls addObjectUniquely: SOGoMailRole_Poster];
377           break;
378         case 'k':
379           [SOGoAcls addObjectUniquely: SOGoRole_FolderCreator];
380           break;
381         case 'x':
382           [SOGoAcls addObjectUniquely: SOGoRole_FolderEraser];
383           break;
384         case 't':
385           [SOGoAcls addObjectUniquely: SOGoRole_ObjectEraser];
386           break;
387         case 'e':
388           [SOGoAcls addObjectUniquely: SOGoMailRole_Expunger];
389           break;
390         case 'a':
391           [SOGoAcls addObjectUniquely: SOGoMailRole_Administrator];
392           break;
393         }
394     }
395
396   return SOGoAcls;
397 }
398
399 - (NSString *) _sogoAclsToImapAcls: (NSArray *) sogoAcls
400 {
401   NSMutableString *imapAcls;
402   NSEnumerator *acls;
403   NSString *currentAcl;
404   char character;
405
406   imapAcls = [NSMutableString string];
407   acls = [sogoAcls objectEnumerator];
408   currentAcl = [acls nextObject];
409   while (currentAcl)
410     {
411       if ([currentAcl isEqualToString: SOGoRole_ObjectViewer])
412         character = 'l';
413       else if ([currentAcl isEqualToString: SOGoRole_ObjectReader])
414         character = 'r';
415       else if ([currentAcl isEqualToString: SOGoMailRole_SeenKeeper])
416         character = 's';
417       else if ([currentAcl isEqualToString: SOGoMailRole_Writer])
418         character = 'w';
419       else if ([currentAcl isEqualToString: SOGoRole_ObjectCreator])
420         character = 'i';
421       else if ([currentAcl isEqualToString: SOGoMailRole_Poster])
422         character = 'p';
423       else if ([currentAcl isEqualToString: SOGoRole_FolderCreator])
424         character = 'k';
425       else if ([currentAcl isEqualToString: SOGoRole_FolderEraser])
426         character = 'x';
427       else if ([currentAcl isEqualToString: SOGoRole_ObjectEraser])
428         character = 't';
429       else if ([currentAcl isEqualToString: SOGoMailRole_Expunger])
430         character = 'e';
431       else if ([currentAcl isEqualToString: SOGoMailRole_Administrator])
432         character = 'a';
433       else
434         character = 0;
435
436       if (character)
437         [imapAcls appendFormat: @"%c", character];
438
439       currentAcl = [acls nextObject];
440     }
441
442   return imapAcls;
443 }
444
445 - (void) _readMailboxACL
446 {
447   mailboxACL
448     = [[self imap4Connection] aclForMailboxAtURL: [self imap4URL]];
449   [mailboxACL retain];
450 }
451
452 - (NSArray *) aclUsers
453 {
454   NSArray *users;
455
456   if (!mailboxACL)
457     [self _readMailboxACL];
458
459   if ([mailboxACL isKindOfClass: [NSDictionary class]])
460     users = [mailboxACL allKeys];
461   else
462     users = nil;
463
464   return users;
465 }
466
467 - (NSMutableArray *) _sharesACLs
468 {
469   NSMutableArray *acls;
470   SOGoMailAccount *mailAccount;
471   NSString *path, *folder;
472 //   NSArray *names;
473 //   unsigned int count;
474
475   acls = [NSMutableArray array];
476
477   mailAccount = [self mailAccountFolder];
478   path = [[self imap4Connection] imap4FolderNameForURL: [self imap4URL]];
479 //   names = [path componentsSeparatedByString: @"/"];
480 //   count = [names count];
481
482   folder = [mailAccount sharedFolderName];
483   if (folder && [path hasPrefix: folder])
484     [acls addObject: SOGoRole_ObjectViewer];
485   else
486     {
487       folder = [mailAccount otherUsersFolderName];
488       if (folder && [path hasPrefix: folder])
489         [acls addObject: SOGoRole_ObjectViewer];
490       else
491         [acls addObject: SoRole_Owner];
492     }
493
494   return acls;
495 }
496
497 - (NSArray *) aclsForUser: (NSString *) uid
498 {
499   NSMutableArray *acls;
500   NSString *userAcls;
501
502   acls = [self _sharesACLs];
503
504   if (!mailboxACL)
505     [self _readMailboxACL];
506
507   if ([mailboxACL isKindOfClass: [NSDictionary class]])
508     {
509       userAcls = [mailboxACL objectForKey: uid];
510       if (!([userAcls length] || [uid isEqualToString: defaultUserID]))
511         userAcls = [mailboxACL objectForKey: defaultUserID];
512       if ([userAcls length])
513         [acls addObjectsFromArray: [self _imapAclsToSOGoAcls: userAcls]];
514     }
515
516   return acls;
517 }
518
519 - (void) removeAclsForUsers: (NSArray *) users
520 {
521   NSEnumerator *uids;
522   NSString *currentUID;
523   NSString *folderName;
524   NGImap4Client *client;
525
526   folderName = [[self imap4Connection] imap4FolderNameForURL: [self imap4URL]];
527   client = [imap4 client];
528
529   uids = [users objectEnumerator];
530   currentUID = [uids nextObject];
531   while (currentUID)
532     {
533       [client deleteACL: folderName uid: currentUID];
534       currentUID = [uids nextObject];
535     }
536   [mailboxACL release];
537   mailboxACL = nil;
538 }
539
540 - (void) setRoles: (NSArray *) roles
541           forUser: (NSString *) uid
542 {
543   NSString *acls, *folderName;
544
545   acls = [self _sogoAclsToImapAcls: roles];
546   folderName = [[self imap4Connection] imap4FolderNameForURL: [self imap4URL]];
547   [[imap4 client] setACL: folderName rights: acls uid: uid];
548
549   [mailboxACL release];
550   mailboxACL = nil;
551 }
552
553 - (NSString *) defaultUserID
554 {
555   return defaultUserID;
556 }
557
558 - (NSString *) otherUsersPathToFolder
559 {
560   NSString *userPath, *selfPath, *otherUsers, *sharedFolders;
561   SOGoMailAccount *account;
562
563   account = [self mailAccountFolder];
564   otherUsers = [account otherUsersFolderName];
565   sharedFolders = [account sharedFolderName];
566
567   selfPath = [[self imap4URL] path];
568   if ((otherUsers
569        && [selfPath hasPrefix:
570                       [NSString stringWithFormat: @"/%@", otherUsers]])
571       || (sharedFolders
572           && [selfPath hasPrefix:
573                          [NSString stringWithFormat: @"/%@", sharedFolders]]))
574     userPath = selfPath;
575   else
576     {
577       if (otherUsers)
578         userPath = [NSString stringWithFormat: @"/%@/%@%@",
579                              [otherUsers stringByEscapingURL],
580                              owner, selfPath];
581       else
582         userPath = nil;
583     }
584
585   return userPath;
586 }
587
588 - (NSString *) httpURLForAdvisoryToUser: (NSString *) uid;
589 {
590   SOGoUser *user;
591   NSString *otherUsersPath, *url;
592   SOGoMailAccount *thisAccount;
593   NSDictionary *mailAccount;
594
595   user = [SOGoUser userWithLogin: uid roles: nil];
596   otherUsersPath = [self otherUsersPathToFolder];
597   if (otherUsersPath)
598     {
599       thisAccount = [self mailAccountFolder];
600       mailAccount = [[user mailAccounts] objectAtIndex: 0];
601       url = [NSString stringWithFormat: @"%@/%@%@",
602                       [self soURLToBaseContainerForUser: uid],
603                       [mailAccount objectForKey: @"name"],
604                       otherUsersPath];
605     }
606   else
607     url = nil;
608
609   return url;
610 }
611
612 - (NSString *) resourceURLForAdvisoryToUser: (NSString *) uid;
613 {
614   NSURL *selfURL, *userURL;
615
616   selfURL = [self imap4URL];
617   userURL = [[NSURL alloc] initWithScheme: [selfURL scheme]
618                            host: [selfURL host]
619                            path: [self otherUsersPathToFolder]];
620   [userURL autorelease];
621
622   return [userURL absoluteString];
623 }
624
625 @end /* SOGoMailFolder */