2 Copyright (C) 2000-2004 SKYRIX Software AG
4 This file is part of OGo
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
23 #include "NGImap4FileManager.h"
24 #include <NGImap4/NGImap4Folder.h>
25 #include <NGImap4/NGImap4Context.h>
26 #include <NGImap4/NGImap4Message.h>
27 #include <NGExtensions/NGFileFolderInfoDataSource.h>
29 #include <NGImap4/NGImap4DataSource.h>
31 @interface NGImap4FileManager(Privates)
33 - (BOOL)loginWithUser:(NSString *)_user
34 password:(NSString *)_pwd
35 host:(NSString *)_host;
37 - (NGImap4Folder *)_lookupFolderAtPath:(NSArray *)_paths;
39 - (NGImap4Folder *)_lookupFolderAtPathString:(NSString *)_path;
41 - (EOQualifier *)_qualifierForFileName:(NSString *)_filename;
44 @implementation NGImap4FileManager
47 return [super version] + 0 /* v0 */;
50 NSAssert2([super version] == 0,
51 @"invalid superclass (%@) version %i !",
52 NSStringFromClass([self superclass]), [super version]);
55 - (id)initWithUser:(NSString *)_user
56 password:(NSString *)_pwd
57 host:(NSString *)_host
59 if ((self = [super init])) {
60 if (![self loginWithUser:_user password:_pwd host:_host]) {
61 [self logWithFormat:@"could not login user '%@' host '%@'.",
70 return [self initWithUser:nil password:nil host:nil];
72 - (id)initWithURL:(NSURL *)_url {
78 if ((self = [super init])) {
79 self->imapContext = [NGImap4Context alloc]; /* keep gcc happy */
80 if ((self->imapContext = [self->imapContext initWithURL:_url]) == nil){
81 [self logWithFormat:@"ERROR: got no IMAP4 context for url %@ ...",_url];
86 [self->imapContext enterSyncMode];
88 if (![self->imapContext openConnection]) {
89 [self logWithFormat:@"ERROR: could not open IMAP4 connection ..."];
94 if ((self->rootFolder = [[self->imapContext serverRoot] retain]) == nil) {
95 [self logWithFormat:@"ERROR: did not find root folder ..."];
99 if ((self->currentFolder=[[self->imapContext inboxFolder] retain])==nil) {
100 [self logWithFormat:@"ERROR: did not find inbox folder ..."];
105 if (![[_url path] isEqualToString:@"/"]) {
106 if (![self changeCurrentDirectoryPath:[_url path]]) {
107 [self logWithFormat:@"ERROR: couldn't change to URL path: %@", _url];
117 return self->imapContext;
121 [self->currentFolder release];
122 [self->imapContext release];
123 [self->rootFolder release];
129 - (BOOL)loginWithUser:(NSString *)_user
130 password:(NSString *)_pwd
131 host:(NSString *)_host
133 NSException *loginException;
134 NSDictionary *conDict;
136 [self->imapContext release]; self->imapContext = nil;
137 [self->rootFolder release]; self->rootFolder = nil;
138 [self->currentFolder release]; self->currentFolder = nil;
140 conDict = [NSDictionary dictionaryWithObjectsAndKeys:
141 _user ? _user : @"anonymous", @"login",
142 _pwd ? _pwd : @"", @"passwd",
143 _host ? _host : @"localhost", @"host",
146 loginException = nil;
149 [[NGImap4Context alloc] initWithConnectionDictionary:conDict];
150 [self->imapContext enterSyncMode];
152 if (![self->imapContext openConnection])
155 if ((self->rootFolder = [[self->imapContext serverRoot] retain]) == nil)
157 if ((self->currentFolder = [[self->imapContext inboxFolder] retain]) == nil)
165 - (id<NGImap4Folder>)_lookupFolderAtPath:(NSArray *)_paths {
168 id<NGImap4Folder> folder;
170 folder = self->currentFolder;
172 e = [_paths objectEnumerator];
173 while ((path = [e nextObject]) && (folder != nil)) {
174 if ([path isEqualToString:@"."])
176 if ([path isEqualToString:@""])
178 if ([path isEqualToString:@".."]) {
179 folder = [folder parentFolder];
182 if ([path isEqualToString:@"/"]) {
183 folder = self->rootFolder;
187 folder = [folder subFolderWithName:path caseInsensitive:NO];
192 - (NGImap4Folder *)_lookupFolderAtPathString:(NSString *)_path {
193 return [self _lookupFolderAtPath:[_path pathComponents]];
196 - (EOQualifier *)_qualifierForFileName:(NSString *)_filename {
197 return [EOQualifier qualifierWithQualifierFormat:@"uid=%@", _filename];
202 - (BOOL)createDirectoryAtPath:(NSString *)_path
203 attributes:(NSDictionary *)_attributes
205 NGImap4Folder *folder;
208 if (![_path isAbsolutePath])
209 _path = [[self currentDirectoryPath] stringByAppendingPathComponent:_path];
211 filename = [_path lastPathComponent];
212 _path = [_path stringByDeletingLastPathComponent];
214 if ((folder = [self _lookupFolderAtPathString:_path]) == nil)
217 return [folder createSubFolderWithName:filename];
220 - (BOOL)changeCurrentDirectoryPath:(NSString *)_path {
221 NGImap4Folder *folder;
223 if ([_path length] == 0)
226 if (![_path isAbsolutePath]) {
227 _path = [[self currentDirectoryPath] stringByAppendingPathComponent:_path];
230 if ((folder = [self _lookupFolderAtPathString:_path]) == nil)
233 ASSIGN(self->currentFolder, folder);
238 - (NSString *)currentDirectoryPath {
239 if ([self->currentFolder isEqual:[self->currentFolder parentFolder]] ||
240 [self->currentFolder parentFolder] == nil)
242 else return [self->currentFolder absoluteName];
245 - (NGImap4Folder *)currentFolder {
246 return self->currentFolder;
251 - (NSArray *)directoryContentsAtPath:(NSString *)_path {
252 return [self directoryContentsAtPath:_path directories:YES files:YES];
255 - (NSArray *)directoriesAtPath:(NSString *)_path {
256 return [self directoryContentsAtPath:_path directories:YES files:NO];
259 - (NSArray *)filesAtPath:(NSString *)_path {
260 return [self directoryContentsAtPath:_path directories:NO files:YES];
263 - (NSArray *)directoryContentsAtPath:(NSString *)_path
264 directories:(BOOL)_dirs
267 NGImap4Folder *folder;
268 NSMutableArray *results;
273 if (![_path isAbsolutePath])
274 _path = [[self currentDirectoryPath] stringByAppendingPathComponent:_path];
276 if ((folder = [self _lookupFolderAtPath:[_path pathComponents]]) == nil)
277 /* folder does not exist */
280 results = [NSMutableArray arrayWithCapacity:64];
284 e = [[folder subFolders] objectEnumerator];
285 while ((tmp = [e nextObject]))
286 [results addObject:[tmp name]];
291 e = [[folder messages] objectEnumerator];
292 while ((msg = [e nextObject]))
293 [results addObject:[NSString stringWithFormat:@"%d", [msg uid]]];
299 - (NGImap4Message *)messageAtPath:(NSString *)_path {
300 NGImap4Folder *folder;
306 if (![_path isAbsolutePath])
307 _path = [[self currentDirectoryPath] stringByAppendingPathComponent:_path];
309 filename = [_path lastPathComponent];
310 _path = [_path stringByDeletingLastPathComponent];
312 if ((folder = [self _lookupFolderAtPath:[_path pathComponents]]) == nil)
315 q = [self _qualifierForFileName:filename];
316 //NSLog(@"qualifier: %@", q);
318 msgs = [folder messagesForQualifier:q maxCount:2];
319 if ([msgs count] == 0) {
320 /* no such message .. */
323 if ([msgs count] > 1) {
324 NSLog(@"multiple messages for uid %@", filename);
327 msg = [msgs objectAtIndex:0];
331 - (NSData *)contentsAtPath:(NSString *)_path {
332 return [self contentsAtPath:_path part:@""];
335 - (NSData *)contentsAtPath:(NSString *)_path part:(NSString *)_part {
337 NGImap4Folder *folder;
339 if (![_path isAbsolutePath])
340 _path = [[self currentDirectoryPath] stringByAppendingPathComponent:_path];
342 fileName = [_path lastPathComponent];
343 _path = [_path stringByDeletingLastPathComponent];
345 if ((folder = [self _lookupFolderAtPath:[_path pathComponents]]) == nil)
348 return [folder blobForUid:[fileName unsignedIntValue] part:_part];
351 - (BOOL)fileExistsAtPath:(NSString *)_path {
353 return [self fileExistsAtPath:_path isDirectory:&isDir];
355 - (BOOL)fileExistsAtPath:(NSString *)_path isDirectory:(BOOL *)_isDir {
358 NGImap4Folder *folder;
360 if (![_path isAbsolutePath])
361 _path = [[self currentDirectoryPath] stringByAppendingPathComponent:_path];
363 fileName = [_path lastPathComponent];
364 _path = [_path stringByDeletingLastPathComponent];
365 paths = [_path pathComponents];
367 folder = [self _lookupFolderAtPath:paths];
369 // NSLog(@"paths: %@, file %@, folder %@", paths, fileName, folder);
374 if ([fileName isEqualToString:@"."]) {
378 if ([fileName isEqualToString:@".."]) {
383 if ([folder subFolderWithName:fileName caseInsensitive:NO]) {
390 /* check for message 'file' */
395 q = [self _qualifierForFileName:fileName];
396 msgs = [folder messagesForQualifier:q maxCount:2];
398 if ([msgs count] > 0)
405 - (BOOL)isReadableFileAtPath:(NSString *)_path {
406 return [self fileExistsAtPath:_path];
408 - (BOOL)isWritableFileAtPath:(NSString *)_path {
409 return [self fileExistsAtPath:_path];
411 - (BOOL)isExecutableFileAtPath:(NSString *)_path {
414 - (BOOL)isDeletableFileAtPath:(NSString *)_path {
415 return [self fileExistsAtPath:_path];
420 - (NSDictionary *)_fileAttributesOfFolder:(id<NGImap4Folder>)_folder {
421 NSMutableDictionary *attrs;
424 attrs = [NSMutableDictionary dictionaryWithCapacity:12];
426 if ((tmp = [_folder absoluteName]))
427 [attrs setObject:tmp forKey:NSFilePath];
428 if ((tmp = [_folder name]))
429 [attrs setObject:tmp forKey:NSFileName];
430 if ((tmp = [[_folder parentFolder] absoluteName]))
431 [attrs setObject:tmp forKey:NSParentPath];
433 [attrs setObject:[self->imapContext login] forKey:NSFileOwnerAccountName];
434 [attrs setObject:NSFileTypeDirectory forKey:NSFileType];
439 - (NSDictionary *)_fileAttributesOfMessage:(NGImap4Message *)_msg
440 inFolder:(NGImap4Folder *)_folder
442 NSMutableDictionary *attrs;
443 NSString *fileName, *filePath;
444 NSDictionary *headers;
447 static NGMimeHeaderNames *Fields = NULL;
450 Fields = (NGMimeHeaderNames *)[NGMimePartParser headerFieldNames];
453 headers = (id)[_msg headers];
454 //NSLog(@"headers: %@", headers);
456 fileName = [NSString stringWithFormat:@"%i", [_msg uid]];
457 filePath = [[_folder absoluteName] stringByAppendingPathComponent:fileName];
458 attrs = [NSMutableDictionary dictionaryWithCapacity:12];
460 if (filePath) [attrs setObject:filePath forKey:NSFilePath];
461 if (fileName) [attrs setObject:fileName forKey:NSFileName];
463 if ((tmp = [_folder absoluteName]))
464 [attrs setObject:tmp forKey:NSParentPath];
466 if ((tmp = [headers objectForKey:@"date"])) {
467 /* should parse date ? */
468 NSCalendarDate *date;
470 if ([tmp isKindOfClass:[NSDate class]])
473 NGMimeRFC822DateHeaderFieldParser *parser;
474 parser = [[NGMimeRFC822DateHeaderFieldParser alloc] init];
475 date = [parser parseValue:
476 [tmp dataUsingEncoding:[NSString defaultCStringEncoding]]
477 ofHeaderField:@"date"];
482 NSLog(@"couldn't parse date: %@", tmp);
484 [attrs setObject:date ? date : tmp forKey:NSFileModificationDate];
487 if ((tmp = [headers objectForKey:Fields->from]))
488 [attrs setObject:tmp forKey:@"NGImapFrom"];
489 if ((tmp = [headers objectForKey:Fields->xMailer]))
490 [attrs setObject:tmp forKey:@"NGImapMailer"];
491 if ((tmp = [headers objectForKey:Fields->organization]))
492 [attrs setObject:tmp forKey:@"NGImapOrganization"];
493 if ((tmp = [headers objectForKey:Fields->to]))
494 [attrs setObject:tmp forKey:@"NGImapReceiver"];
495 if ((tmp = [headers objectForKey:Fields->subject]))
496 [attrs setObject:tmp forKey:@"NGImapSubject"];
497 if ((tmp = [headers objectForKey:Fields->contentType]))
498 [attrs setObject:tmp forKey:@"NGImapContentType"];
500 [attrs setObject:[self->imapContext login] forKey:NSFileOwnerAccountName];
501 [attrs setObject:[NSNumber numberWithInt:[_msg size]] forKey:NSFileSize];
503 if ((tmp = [headers objectForKey:Fields->messageID]))
504 [attrs setObject:tmp forKey:@"NSFileIdentifier"];
506 [attrs setObject:[NSNumber numberWithInt:[_msg uid]]
507 forKey:@"NSFileIdentifier"];
510 [attrs setObject:NSFileTypeRegular forKey:NSFileType];
515 - (NSDictionary *)fileAttributesAtPath:(NSString *)_path
516 traverseLink:(BOOL)flag
519 NGImap4Folder *folder, *sfolder;
521 if (![_path isAbsolutePath])
522 _path = [[self currentDirectoryPath] stringByAppendingPathComponent:_path];
524 fileName = [_path lastPathComponent];
525 _path = [_path stringByDeletingLastPathComponent];
527 if ((folder = [self _lookupFolderAtPath:[_path pathComponents]]) == nil)
530 /* check for folder */
532 if ([fileName isEqualToString:@"."])
533 return [self _fileAttributesOfFolder:folder];
534 if ([fileName isEqualToString:@".."])
535 return [self _fileAttributesOfFolder:[folder parentFolder]];
537 if ((sfolder = [folder subFolderWithName:fileName caseInsensitive:NO]))
538 return [self _fileAttributesOfFolder:sfolder];
540 /* check for messages */
545 q = [self _qualifierForFileName:fileName];
546 msgs = [folder messagesForQualifier:q maxCount:2];
548 if ([msgs count] == 0) {
549 /* msg does not exist */
550 //NSLog(@"did not find msg for qualifier %@ in folder %@", q, folder);
554 return [self _fileAttributesOfMessage:[msgs objectAtIndex:0]
559 - (NSDictionary *)fileSystemAttributesAtPath:(NSString *)_path {
560 NSMutableDictionary *dict;
563 dict = [NSMutableDictionary dictionaryWithCapacity:12];
565 if ((tmp = [self->imapContext host]))
566 [dict setObject:tmp forKey:@"host"];
567 if ((tmp = [self->imapContext login]))
568 [dict setObject:tmp forKey:@"login"];
569 if ((tmp = [self->imapContext serverName]))
570 [dict setObject:tmp forKey:@"serverName"];
571 if ((tmp = [self->imapContext serverKind]))
572 [dict setObject:tmp forKey:@"serverKind"];
573 if ((tmp = [self->imapContext serverVersion]))
574 [dict setObject:tmp forKey:@"serverVersion"];
575 if ((tmp = [self->imapContext serverTag]))
576 [dict setObject:tmp forKey:@"serverTag"];
578 if ((tmp = [[self->imapContext trashFolder] absoluteName]))
579 [dict setObject:tmp forKey:@"trashFolderPath"];
580 if ((tmp = [[self->imapContext sentFolder] absoluteName]))
581 [dict setObject:tmp forKey:@"sentFolderPath"];
582 if ((tmp = [[self->imapContext draftsFolder] absoluteName]))
583 [dict setObject:tmp forKey:@"draftsFolderPath"];
584 if ((tmp = [[self->imapContext inboxFolder] absoluteName]))
585 [dict setObject:tmp forKey:@"inboxFolderPath"];
586 if ((tmp = [[self->imapContext serverRoot] absoluteName]))
587 [dict setObject:tmp forKey:@"rootFolderPath"];
594 - (NSString *)description {
597 ms = [NSMutableString stringWithCapacity:64];
599 [ms appendFormat:@"<0x%08X[%@]:", self, NSStringFromClass([self class])];
600 [ms appendFormat:@" ctx=%@", self->imapContext];
601 [ms appendFormat:@" root=%@", self->rootFolder];
602 [ms appendFormat:@" wd=%@", self->currentFolder];
603 [ms appendString:@">"];
608 - (EODataSource *)dataSourceAtPath:(NSString *)_path {
611 if ((f = [self _lookupFolderAtPath:[_path pathComponents]]) == nil)
614 return [[[NGImap4DataSource alloc] initWithFolder:f] autorelease];
618 return [self->imapContext isInSyncMode];
621 - (void)setSyncMode:(BOOL)_bool {
623 [self->imapContext enterSyncMode];
625 [self->imapContext leaveSyncMode];
628 @end /* NGImap4FileManager */