2 Copyright (C) 2004-2005 SKYRIX Software AG
4 This file is part of OpenGroupware.org.
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
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.
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
22 #import <Foundation/NSDictionary.h>
23 #import <Foundation/NSURL.h>
24 #import <Foundation/NSUserDefaults.h>
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>
33 #import <NGImap4/NGImap4Connection.h>
34 #import <NGImap4/NGImap4Client.h>
36 #import <SoObjects/SOGo/SOGoPermissions.h>
37 #import <SoObjects/SOGo/SOGoUser.h>
38 #import <SoObjects/SOGo/NSArray+Utilities.h>
40 #import "SOGoMailObject.h"
41 #import "SOGoMailAccount.h"
42 #import "SOGoMailManager.h"
43 #import "SOGoMailFolder.h"
45 static NSString *defaultUserID = @"anyone";
47 @implementation SOGoMailFolder
51 SOGoMailAccount *mailAccount;
52 NSString *path, *folder;
55 mailAccount = [self mailAccountFolder];
56 path = [[self imap4Connection] imap4FolderNameForURL: [self imap4URL]];
58 folder = [mailAccount sharedFolderName];
59 if (folder && [path hasPrefix: folder])
60 [self setOwner: @"nobody"];
63 folder = [mailAccount otherUsersFolderName];
64 if (folder && [path hasPrefix: folder])
66 names = [path componentsSeparatedByString: @"/"];
67 if ([names count] > 1)
68 [self setOwner: [names objectAtIndex: 1]];
70 [self setOwner: @"nobody"];
75 - (id) initWithName: (NSString *) newName
76 inContainer: (id) newContainer
78 if ((self = [super initWithName: newName
79 inContainer: newContainer]))
98 - (NSString *) relativeImap4Name
100 return [nameInContainer substringFromIndex: 6];
103 - (NSString *) absoluteImap4Name
107 name = [[self imap4URL] path];
108 if (![name hasSuffix: @"/"])
109 name = [name stringByAppendingString: @"/"];
114 - (NSMutableString *) imap4URLString
116 NSMutableString *urlString;
118 urlString = [super imap4URLString];
119 [urlString appendString: @"/"];
124 /* listing the available folders */
126 - (NSArray *) toManyRelationshipKeys
128 return [self subfolders];
131 - (NSArray *) subfolders
133 return [[self imap4Connection] subfoldersForURL: [self imap4URL]];
136 - (NSArray *) allFolderPaths
138 NSMutableArray *deepSubfolders;
139 NSEnumerator *folderNames;
141 NSString *currentFolderName, *prefix;
143 deepSubfolders = [NSMutableArray new];
144 [deepSubfolders autorelease];
146 prefix = [self absoluteImap4Name];
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:)];
155 return deepSubfolders;
158 - (NSArray *) allFolderURLs
160 NSURL *selfURL, *currentURL;
161 NSMutableArray *subfoldersURL;
162 NSEnumerator *subfolders;
163 NSString *currentFolder;
165 subfoldersURL = [NSMutableArray array];
166 selfURL = [self imap4URL];
167 subfolders = [[self allFolderPaths] objectEnumerator];
168 currentFolder = [subfolders nextObject];
169 while (currentFolder)
171 currentURL = [[NSURL alloc]
172 initWithScheme: [selfURL scheme]
174 path: currentFolder];
175 [currentURL autorelease];
176 [subfoldersURL addObject: currentURL];
177 currentFolder = [subfolders nextObject];
180 return subfoldersURL;
183 - (NSString *) davContentType
185 return @"httpd/unix-directory";
188 - (NSArray *) toOneRelationshipKeys
191 unsigned int count, max;
196 filenames = [NSMutableArray new];
197 uids = [self fetchUIDsMatchingQualifier: nil sortOrdering: @"DATE"];
198 if (![uids isKindOfClass: [NSException class]])
201 for (count = 0; count < max; count++)
203 filename = [NSString stringWithFormat: @"%@.mail",
204 [uids objectAtIndex: count]];
205 [filenames addObject: filename];
215 - (NSArray *) fetchUIDsMatchingQualifier: (id) _q
216 sortOrdering: (id) _so
218 /* seems to return an NSArray of NSNumber's */
219 return [[self imap4Connection] fetchUIDsInURL: [self imap4URL]
220 qualifier: _q sortOrdering: _so];
223 - (NSArray *) fetchUIDs: (NSArray *) _uids
224 parts: (NSArray *) _parts
226 return [[self imap4Connection] fetchUIDs: _uids inURL: [self imap4URL]
230 - (NSException *) postData: (NSData *) _data
233 return [[self imap4Connection] postData: _data flags: _flags
234 toFolderURL: [self imap4URL]];
237 - (NSException *) expunge
239 return [[self imap4Connection] expungeAtURL: [self imap4URL]];
242 - (void) markForExpunge
245 NSMutableDictionary *mailSettings;
247 ud = [[context activeUser] userSettings];
248 mailSettings = [ud objectForKey: @"Mail"];
251 mailSettings = [NSMutableDictionary dictionaryWithCapacity: 1];
252 [ud setObject: mailSettings forKey: @"Mail"];
255 [mailSettings setObject: [self imap4URLString] forKey: @"folderForExpunge"];
259 - (void) expungeLastMarkedFolder
262 NSMutableDictionary *mailSettings;
263 NSString *expungeURL;
266 ud = [[context activeUser] userSettings];
267 mailSettings = [ud objectForKey: @"Mail"];
270 expungeURL = [mailSettings objectForKey: @"folderForExpunge"];
273 folderURL = [NSURL URLWithString: expungeURL];
274 if (![[self imap4Connection] expungeAtURL: folderURL])
276 [mailSettings removeObjectForKey: @"folderForExpunge"];
285 - (NSException *) addFlagsToAllMessages: (id) _f
287 return [[self imap4Connection] addFlags:_f
288 toAllMessagesInURL: [self imap4URL]];
293 - (id) lookupName: (NSString *) _key
295 acquire: (BOOL) _acquire
299 if ([_key hasPrefix: @"folder"])
300 obj = [SOGoMailFolder objectWithName: _key inContainer: self];
303 if ([[self imap4Connection] doesMailboxExistAtURL: [self imap4URL]])
305 if (isdigit ([_key characterAtIndex: 0]))
306 obj = [SOGoMailObject objectWithName: _key inContainer: self];
308 obj = [super lookupName: _key inContext: _ctx acquire: NO];
314 if (!obj && _acquire)
315 obj = [NSException exceptionWithHTTPStatus: 404 /* Not Found */];
322 - (BOOL) davIsCollection
327 - (NSException *) davCreateCollection: (NSString *) _name
330 return [[self imap4Connection] createMailbox:_name atURL:[self imap4URL]];
333 - (NSException *) delete
335 /* Note: overrides SOGoObject -delete */
336 return [[self imap4Connection] deleteMailboxAtURL:[self imap4URL]];
339 - (NSException *) davMoveToTargetObject: (id) _target
340 newName: (NSString *) _name
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"];
350 if (![_target respondsToSelector:@selector(imap4URL)]) {
351 return [NSException exceptionWithHTTPStatus:502 /* Bad Gateway */
352 reason:@"target is not an IMAP4 folder"];
355 /* build IMAP4 URL for target */
357 destImapURL = [_target imap4URL];
358 // - destImapURL = [NSURL URLWithString:[[destImapURL path]
359 // - stringByAppendingPathComponent:_name]
360 // - relativeToURL:destImapURL];
361 destImapURL = [NSURL URLWithString: _name
362 relativeToURL: destImapURL];
364 [self logWithFormat:@"TODO: should move collection as '%@' to: %@",
365 [[self imap4URL] absoluteString],
366 [destImapURL absoluteString]];
368 return [[self imap4Connection] moveMailboxAtURL:[self imap4URL]
372 - (NSException *) davCopyToTargetObject: (id) _target
373 newName: (NSString *) _name
376 [self logWithFormat:@"TODO: should copy collection as '%@' to: %@",
378 return [NSException exceptionWithHTTPStatus:501 /* Not Implemented */
379 reason:@"not implemented"];
383 - (NSString *) folderType
388 - (NSString *) outlookFolderClass
390 // TODO: detect Trash/Sent/Drafts folders
391 SOGoMailAccount *account;
394 if (folderType != nil)
397 account = [self mailAccountFolder];
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";
407 folderType = @"IPF.Folder";
414 - (NSArray *) _imapAclsToSOGoAcls: (NSString *) imapAcls
416 unsigned int count, max;
417 NSMutableArray *SOGoAcls;
419 SOGoAcls = [NSMutableArray array];
420 max = [imapAcls length];
421 for (count = 0; count < max; count++)
423 switch ([imapAcls characterAtIndex: count])
426 [SOGoAcls addObjectUniquely: SOGoRole_ObjectViewer];
429 [SOGoAcls addObjectUniquely: SOGoRole_ObjectReader];
432 [SOGoAcls addObjectUniquely: SOGoMailRole_SeenKeeper];
435 [SOGoAcls addObjectUniquely: SOGoMailRole_Writer];
438 [SOGoAcls addObjectUniquely: SOGoRole_ObjectCreator];
441 [SOGoAcls addObjectUniquely: SOGoMailRole_Poster];
444 [SOGoAcls addObjectUniquely: SOGoRole_FolderCreator];
447 [SOGoAcls addObjectUniquely: SOGoRole_FolderEraser];
450 [SOGoAcls addObjectUniquely: SOGoRole_ObjectEraser];
453 [SOGoAcls addObjectUniquely: SOGoMailRole_Expunger];
456 [SOGoAcls addObjectUniquely: SOGoMailRole_Administrator];
464 - (NSString *) _sogoAclsToImapAcls: (NSArray *) sogoAcls
466 NSMutableString *imapAcls;
468 NSString *currentAcl;
471 imapAcls = [NSMutableString string];
472 acls = [sogoAcls objectEnumerator];
473 currentAcl = [acls nextObject];
476 if ([currentAcl isEqualToString: SOGoRole_ObjectViewer])
478 else if ([currentAcl isEqualToString: SOGoRole_ObjectReader])
480 else if ([currentAcl isEqualToString: SOGoMailRole_SeenKeeper])
482 else if ([currentAcl isEqualToString: SOGoMailRole_Writer])
484 else if ([currentAcl isEqualToString: SOGoRole_ObjectCreator])
486 else if ([currentAcl isEqualToString: SOGoMailRole_Poster])
488 else if ([currentAcl isEqualToString: SOGoRole_FolderCreator])
490 else if ([currentAcl isEqualToString: SOGoRole_FolderEraser])
492 else if ([currentAcl isEqualToString: SOGoRole_ObjectEraser])
494 else if ([currentAcl isEqualToString: SOGoMailRole_Expunger])
496 else if ([currentAcl isEqualToString: SOGoMailRole_Administrator])
502 [imapAcls appendFormat: @"%c", character];
504 currentAcl = [acls nextObject];
510 - (void) _readMailboxACL
513 = [[self imap4Connection] aclForMailboxAtURL: [self imap4URL]];
517 - (NSArray *) aclUsers
522 [self _readMailboxACL];
524 if ([mailboxACL isKindOfClass: [NSDictionary class]])
525 users = [mailboxACL allKeys];
532 - (NSMutableArray *) _sharesACLs
534 NSMutableArray *acls;
535 SOGoMailAccount *mailAccount;
536 NSString *path, *folder;
538 // unsigned int count;
540 acls = [NSMutableArray array];
542 mailAccount = [self mailAccountFolder];
543 path = [[self imap4Connection] imap4FolderNameForURL: [self imap4URL]];
544 // names = [path componentsSeparatedByString: @"/"];
545 // count = [names count];
547 folder = [mailAccount sharedFolderName];
548 if (folder && [path hasPrefix: folder])
549 [acls addObject: SOGoRole_ObjectViewer];
552 folder = [mailAccount otherUsersFolderName];
553 if (folder && [path hasPrefix: folder])
554 [acls addObject: SOGoRole_ObjectViewer];
556 [acls addObject: SoRole_Owner];
562 - (NSArray *) aclsForUser: (NSString *) uid
564 NSMutableArray *acls;
567 acls = [self _sharesACLs];
570 [self _readMailboxACL];
572 if ([mailboxACL isKindOfClass: [NSDictionary class]])
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]];
584 - (void) removeAclsForUsers: (NSArray *) users
587 NSString *currentUID;
588 NSString *folderName;
589 NGImap4Client *client;
591 folderName = [[self imap4Connection] imap4FolderNameForURL: [self imap4URL]];
592 client = [imap4 client];
594 uids = [users objectEnumerator];
595 currentUID = [uids nextObject];
598 [client deleteACL: folderName uid: currentUID];
599 currentUID = [uids nextObject];
601 [mailboxACL release];
605 - (void) setRoles: (NSArray *) roles
606 forUser: (NSString *) uid
608 NSString *acls, *folderName;
610 acls = [self _sogoAclsToImapAcls: roles];
611 folderName = [[self imap4Connection] imap4FolderNameForURL: [self imap4URL]];
612 [[imap4 client] setACL: folderName rights: acls uid: uid];
614 [mailboxACL release];
618 - (NSString *) defaultUserID
620 return defaultUserID;
623 - (NSString *) otherUsersPathToFolder
625 NSString *userPath, *selfPath, *otherUsers, *sharedFolders;
626 SOGoMailAccount *account;
628 account = [self mailAccountFolder];
629 otherUsers = [account otherUsersFolderName];
630 sharedFolders = [account sharedFolderName];
632 selfPath = [[self imap4URL] path];
634 && [selfPath hasPrefix:
635 [NSString stringWithFormat: @"/%@", otherUsers]])
637 && [selfPath hasPrefix:
638 [NSString stringWithFormat: @"/%@", sharedFolders]]))
643 userPath = [NSString stringWithFormat: @"/%@/%@%@",
644 [otherUsers stringByEscapingURL],
653 - (NSString *) httpURLForAdvisoryToUser: (NSString *) uid
656 NSString *otherUsersPath, *url;
657 SOGoMailAccount *thisAccount;
658 NSDictionary *mailAccount;
660 user = [SOGoUser userWithLogin: uid roles: nil];
661 otherUsersPath = [self otherUsersPathToFolder];
664 thisAccount = [self mailAccountFolder];
665 mailAccount = [[user mailAccounts] objectAtIndex: 0];
666 url = [NSString stringWithFormat: @"%@/%@%@",
667 [self soURLToBaseContainerForUser: uid],
668 [mailAccount objectForKey: @"name"],
677 - (NSString *) resourceURLForAdvisoryToUser: (NSString *) uid
679 NSURL *selfURL, *userURL;
681 selfURL = [self imap4URL];
682 userURL = [[NSURL alloc] initWithScheme: [selfURL scheme]
684 path: [self otherUsersPathToFolder]];
685 [userURL autorelease];
687 return [userURL absoluteString];
690 @end /* SOGoMailFolder */