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