2 Copyright (C) 2000-2005 SKYRIX Software AG
4 This file is part of SOPE.
6 SOPE 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 SOPE 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 SOPE; see the file COPYING. If not, write to the
18 Free Software Foundation, 59 Temple Place - Suite 330, Boston, MA
22 #include "NGImap4Context.h"
23 #include "NGImap4Client.h"
24 #include "NGImap4Folder.h"
25 #include "NGImap4ServerRoot.h"
26 #include "NGImap4Support.h"
27 #include "NGImap4Functions.h"
30 @interface NGImap4Context(Private)
31 - (void)initializeSentFolder;
32 - (void)initializeTrashFolder;
33 - (void)initializeDraftsFolder;
34 - (void)initializeInboxFolder;
35 - (void)initializeServerRoot;
37 - (void)_setSortEncoding:(NSString *)_str;
38 - (void)_setSubscribeFolderFailed:(BOOL)_b;
39 - (void)_setShowOnlySubscribedInRoot:(BOOL)_b;
40 - (void)_setShowOnlySubscribedInSubFolders:(BOOL)_b;
43 @implementation NGImap4Context
45 static id DefaultForSortEncoding = nil;
46 static id DefaultForSubscribeFailed = nil;
47 static id DefaultForShowOnlySubscribedInRoot = nil;
48 static id DefaultForShowOnlySubscribedInSubFolders = nil;
49 static int ImapLogEnabled = -1;
53 static BOOL didInit = NO;
57 ud = [NSUserDefaults standardUserDefaults];
59 DefaultForSortEncoding = [[ud stringForKey:@"ImapSortEncoding"] copy];
60 DefaultForSubscribeFailed =
61 [[ud stringForKey:@"ImapSubscribedCouldFailed"] copy];
62 DefaultForShowOnlySubscribedInSubFolders =
63 [[ud stringForKey:@"ShowOnlySubscribedInSubFolders"] copy];
64 DefaultForShowOnlySubscribedInRoot =
65 [[ud stringForKey:@"ShowOnlySubscribedInRoot"] copy];
67 ImapLogEnabled = [ud boolForKey:@"ImapLogEnabled"]?1:0;
70 + (id)imap4ContextWithURL:(id)_url {
73 if (![_url isKindOfClass:[NSURL class]])
74 _url = [NSURL URLWithString:[_url stringValue]];
76 return [[(NGImap4Context *)[self alloc] initWithURL:_url] autorelease];
78 + (id)imap4ContextWithConnectionDictionary:(NSDictionary *)_connection {
79 return [[[self alloc] initWithConnectionDictionary:_connection] autorelease];
82 - (id)initWithConnectionDictionary:(NSDictionary *)_connection {
83 if ((self = [super init])) {
84 self->connectionDictionary = [_connection copy];
85 self->folderForRefresh = [[NSMutableArray alloc] initWithCapacity:512];
88 self->subscribeFolderFailed = (DefaultForSubscribeFailed)
89 ? [DefaultForSubscribeFailed boolValue]?1:0
92 self->showOnlySubscribedInRoot = (DefaultForShowOnlySubscribedInRoot)
93 ? [DefaultForShowOnlySubscribedInRoot boolValue]?1:0
96 self->showOnlySubscribedInSubFolders =
97 (DefaultForShowOnlySubscribedInSubFolders)
98 ? [DefaultForShowOnlySubscribedInSubFolders boolValue]?1:0
101 self->sortEncoding = (DefaultForSortEncoding)
102 ? [DefaultForSortEncoding retain]
108 - (id)initWithNSURL:(NSURL *)_url {
109 NSMutableDictionary *md;
117 md = [NSMutableDictionary dictionaryWithCapacity:4];
118 if ((tmp = [_url host])) [md setObject:tmp forKey:@"host"];
119 if ((tmp = [_url port])) [md setObject:tmp forKey:@"port"];
120 if ((tmp = [_url user])) [md setObject:tmp forKey:@"login"];
121 if ((tmp = [_url password])) [md setObject:tmp forKey:@"passwd"];
123 if ([[_url scheme] isEqualToString:@"imaps"])
124 [md setObject:[NSNumber numberWithBool:YES] forKey:@"SSL"];
126 return [self initWithConnectionDictionary:md];
128 - (id)initWithURL:(id)_url {
129 if ((_url != nil) && ![_url isKindOfClass:[NSURL class]])
130 _url = [NSURL URLWithString:[_url stringValue]];
132 return [self initWithNSURL:_url];
137 [self->client removeFromResponseNotification:self];
138 [self->connectionDictionary release];
139 [self->client release];
140 [self->folderForRefresh release];
141 [self->serverName release];
142 [self->serverKind release];
143 [self->serverVersion release];
144 [self->serverSubVersion release];
145 [self->serverTag release];
146 [self->lastException release];
148 self->selectedFolder = nil; /* not retained */
149 self->trashFolder = nil; /* not retained */
150 self->draftsFolder = nil; /* not retained */
151 self->sentFolder = nil; /* not retained */
152 self->inboxFolder = nil; /* not retained */
153 self->serverRoot = nil; /* not retained */
155 [self->capability release];
156 [self->sortEncoding release];
163 - (NSException *)lastException {
164 return self->lastException;
167 - (void)setLastException:(NSException *)_exception {
168 ASSIGN(self->lastException, _exception);
171 - (void)resetLastException {
172 [self->lastException release]; self->lastException = nil;
175 - (NGImap4Client *)client {
176 if (self->client == nil)
177 [self openConnection];
181 - (EOGlobalID *)serverGlobalID {
182 if (self->client == nil) {
183 // TODO: construct global-id using connectionDictionary!
184 [self logWithFormat:@"WARNING: could not construct GID (client not set!)"];
187 return [self->client serverGlobalID];
196 scheme = [[self->connectionDictionary objectForKey:@"SSL"] boolValue]
197 ? @"imaps" : @"imap";
199 self->url = [[NSURL alloc]
200 initWithScheme:scheme
201 host:[self->connectionDictionary objectForKey:@"host"]
206 /* folder tracking */
208 - (BOOL)isSelectedFolder:(NGImap4Folder *)_folder {
209 return [self->selectedFolder isEqual:_folder];
212 - (void)setSelectedFolder:(NGImap4Folder *)_folder {
213 self->selectedFolder = _folder;
216 - (BOOL)registerAsSelectedFolder:(NGImap4Folder *)_folder {
219 if (self->selectedFolder == _folder)
222 if ([_folder noselect])
225 tmp = self->selectedFolder;
226 self->selectedFolder = _folder;
227 if (![_folder selectImmediately:YES]) {
228 self->selectedFolder = tmp;
235 - (BOOL)removeSelectedFolder:(NGImap4Folder *)_folder {
236 if (self->selectedFolder == _folder)
237 self->selectedFolder = nil;
241 - (BOOL)openConnection {
246 login = [self->connectionDictionary objectForKey:@"login"];
247 passwd = [self->connectionDictionary objectForKey:@"passwd"];
248 host = [self->connectionDictionary objectForKey:@"host"];
250 [self resetSpecialFolders];
252 if ((login == nil) || (passwd == nil) || (host == nil)) {
255 exc = [[NGImap4ConnectionException alloc]
257 @"missing login, passwd or host in connection-dictionary <%@>",
258 self->connectionDictionary];
259 ASSIGN(self->lastException, exc);
263 if (self->client == nil) {
264 self->client = [[NGImap4Client alloc] initWithHost:host];
265 [self->client registerForResponseNotification:self];
266 [self->client setContext:self];
268 if (![[self->client isConnected] boolValue]) {
271 [self resetLastException];
273 res = [self->client openConnection];
274 if (!_checkResult(self, res, __PRETTY_FUNCTION__))
277 [self->serverName release]; self->serverName = nil;
278 [self->serverKind release]; self->serverKind = nil;
279 [self->serverVersion release]; self->serverVersion = nil;
280 [self->serverSubVersion release]; self->serverSubVersion = nil;
281 [self->serverTag release]; self->serverTag = nil;
283 self->serverName = [[res objectForKey:@"server"] retain];
284 self->serverKind = [[res objectForKey:@"serverKind"] retain];
286 if (ImapLogEnabled) {
287 [self logWithFormat:@"Server greeting: <%@> parse serverkind: <%@>",
288 self->serverName, self->serverKind];
291 // TODO: move capability to specialized object!
292 // Note: capability of the IMAP4 server should be always configurable
293 // using defaults because the identification might be broken for
296 if (self->serverKind != nil) {
297 self->serverVersion = [[res objectForKey:@"version"] retain];
298 self->serverSubVersion = [[res objectForKey:@"subversion"] retain];
299 self->serverTag = [[res objectForKey:@"tag"] retain];
301 if ([self->serverKind isEqual:@"courier"]) {
302 [self _setSortEncoding:@"US-ASCII"];
303 [self _setSubscribeFolderFailed:NO];
304 [self _setShowOnlySubscribedInRoot:0];
305 [self _setShowOnlySubscribedInSubFolders:0];
307 else if ([self->serverKind isEqual:@"washington"]) {
308 [self _setSortEncoding:@"UTF-8"];
309 [self _setSubscribeFolderFailed:YES];
310 [self _setShowOnlySubscribedInRoot:1];
311 [self _setShowOnlySubscribedInSubFolders:1];
313 else if ([self->serverKind isEqual:@"cyrus"]) {
314 [self _setSortEncoding:@"UTF-8"];
315 [self _setSubscribeFolderFailed:YES];
316 [self _setShowOnlySubscribedInRoot:0];
317 [self _setShowOnlySubscribedInSubFolders:0];
320 [self logWithFormat:@"sortEncoding %@ subscribeFolderFailed %@ "
321 @"showOnlySubscribedInSubFolders %@ showOnlySubscribedInRoot %@",
323 [self subscribeFolderFailed] ? @"YES" : @"NO",
324 [self showOnlySubscribedInSubFolders]? @"YES" : @"NO",
325 [self showOnlySubscribedInRoot] ? @"YES" : @"NO"];
327 [self resetLastException];
328 res = [self->client login:login password:passwd];
329 if (!_checkResult(self, res, __PRETTY_FUNCTION__))
335 [self->capability release]; self->capability = nil;
336 res = [self->client capability];
338 if (!_checkResult(self, res, __PRETTY_FUNCTION__))
341 self->capability = [[res objectForKey:@"capability"] retain];
350 - (void)_setSortEncoding:(NSString *)_str {
351 if (!DefaultForSortEncoding)
352 ASSIGN(self->sortEncoding, _str);
355 - (void)_setSubscribeFolderFailed:(BOOL)_b {
356 if (!DefaultForSubscribeFailed)
357 self->subscribeFolderFailed = _b?1:0;
360 - (void)_setShowOnlySubscribedInRoot:(BOOL)_b {
361 if (!DefaultForShowOnlySubscribedInRoot)
362 self->showOnlySubscribedInRoot = _b?1:0;
365 - (void)_setShowOnlySubscribedInSubFolders:(BOOL)_b {
366 if (!DefaultForShowOnlySubscribedInSubFolders)
367 self->showOnlySubscribedInSubFolders = _b?1:0;
370 - (void)setSortEncoding:(NSString *)_str {
371 ASSIGN(self->sortEncoding, _str);
374 - (void)setSubscribeFolderFailed:(BOOL)_b {
375 self->subscribeFolderFailed = _b?1:0;
378 - (void)setShowOnlySubscribedInRoot:(BOOL)_b {
379 self->showOnlySubscribedInRoot = _b?1:0;
382 - (void)setShowOnlySubscribedInSubFolders:(BOOL)_b {
383 self->showOnlySubscribedInSubFolders = _b?1:0;
386 - (BOOL)closeConnection {
387 [self->client closeConnection];
388 [self resetSpecialFolders];
389 [self->capability release]; self->capability = nil;
398 ** NGImap4ResponseReceiver protocol
399 ** If the NGImap4Context receive a response-notification it
400 ** updates the selected folder
403 - (void)responseNotificationFrom:(NGImap4Client *)_client
404 response:(NSDictionary *)_dict
406 if (![[_dict objectForKey:@"result"] boolValue]) {
410 if ((str = [_dict objectForKey:@"reason"]) == nil)
411 str = @"Response failed";
413 exc = [[NGImap4ResponseException alloc]
414 initWithName:@"NGImap4ResponseException" reason:str userInfo:_dict];
415 ASSIGN(self->lastException, exc);
419 if (self->selectedFolder)
420 [self->selectedFolder processResponse:_dict];
424 if (self->trashFolder == nil)
425 [self initializeTrashFolder];
426 return self->trashFolder;
428 - (void)setTrashFolder:(NGImap4Folder *)_folder {
429 self->trashFolder = _folder;
433 if (self->sentFolder == nil)
434 [self initializeSentFolder];
435 return self->sentFolder;
437 - (void)setSentFolder:(NGImap4Folder *)_folder {
438 self->sentFolder = _folder;
442 if (self->draftsFolder == nil)
443 [self initializeDraftsFolder];
444 return self->draftsFolder;
446 - (void)setDraftsFolder:(NGImap4Folder *)_folder {
447 self->draftsFolder = _folder;
451 if (self->inboxFolder == nil)
452 [self initializeInboxFolder];
453 return self->inboxFolder;
457 if (self->serverRoot == nil)
458 [self initializeServerRoot];
459 return self->serverRoot;
462 - (void)initializeServerRoot {
463 if (self->serverRoot == nil) {
465 Note: serverRoot is not retained by NGImap4Context to avoid a
466 retain cycle. This is why the object is autoreleased immediatly
467 after creation (the usercode uses -serverRoot to access the result).
469 [self resetSpecialFolders];
470 self->serverRoot = [[NGImap4ServerRoot alloc]
471 initServerRootWithContext:self];
472 self->serverRoot = [self->serverRoot autorelease];
476 - (void)_checkFolder:(id)_folder folderName:(NSString *)_name
481 [self resetLastException];
482 res = [self->client list:@"" pattern:_name];
484 if (![[res objectForKey:@"result"] boolValue])
487 list = [res objectForKey:@"list"];
489 if ([list count]) { /* folder exist but is not subscribed */
490 [self->client subscribe:_name];
492 else { /* try to create folder */
493 [_folder createSubFolderWithName:[_name lastPathComponent]];
495 [_folder resetSubFolders];
496 [self->lastException release]; self->lastException = nil;
499 - (NGImap4Folder *)_getFolderWithName:(NSString *)_name {
500 NSEnumerator *enumerator;
501 NGImap4Folder *folder;
503 if (self->serverRoot == nil)
504 [self initializeServerRoot];
506 enumerator = [[self->serverRoot subFolders] objectEnumerator];
507 while ((folder = [enumerator nextObject])) {
510 name = [[folder name] lowercaseString];
512 if ([name isEqualToString:[_name lowercaseString]]) {
516 if ([[_name lowercaseString] isEqual:@"inbox"]) {
517 [self resetLastException];
518 [self->client subscribe:_name];
519 if (self->lastException != nil) {
520 [self->serverRoot createSubFolderWithName:_name];
521 [self->lastException release]; self->lastException = nil;
523 [self resetSpecialFolders];
526 if ([[self inboxFolder] noinferiors]) {
527 /* try to create Sent/Trash/Drafts in root */
528 [self _checkFolder:self->serverRoot folderName:_name];
531 /* take a look in inbox */
533 NSString *absoluteName;
535 f = [self inboxFolder];
536 folder = [f subFolderWithName:[_name lowercaseString]
537 caseInsensitive:YES];
541 absoluteName = [[f absoluteName] stringByAppendingPathComponent:_name];
543 [self _checkFolder:f folderName:absoluteName];
549 - (NSString *)sentFolderName {
550 static NSString *SentFolderName = nil;
552 if (SentFolderName == nil) {
553 SentFolderName = [[[NSUserDefaults standardUserDefaults]
554 stringForKey:@"ImapSentFolderName"]
558 SentFolderName = @"Sent";
560 return SentFolderName;
563 - (NSString *)trashFolderName {
564 static NSString *TrashFolderName = nil;
566 if (TrashFolderName == nil) {
567 TrashFolderName = [[[NSUserDefaults standardUserDefaults]
568 stringForKey:@"ImapTrashFolderName"]
571 if (!TrashFolderName)
572 TrashFolderName = @"Trash";
574 return TrashFolderName;
577 - (NSString *)draftsFolderName {
578 static NSString *DraftsFolderName = nil;
580 if (DraftsFolderName == nil) {
581 DraftsFolderName = [[[NSUserDefaults standardUserDefaults]
582 stringForKey:@"ImapDraftsFolderName"]
585 if (!DraftsFolderName)
586 DraftsFolderName = @"Drafts";
588 return DraftsFolderName;
591 - (void)initializeSentFolder {
592 if ((self->sentFolder = [self _getFolderWithName:
593 [self sentFolderName]]) == nil)
594 self->sentFolder = [self _getFolderWithName:
595 [self sentFolderName]];
596 if (self->sentFolder == nil)
597 NSLog(@"WARNING[%s]: Couldn't find/create sentFolder", __PRETTY_FUNCTION__);
600 - (void)initializeTrashFolder {
601 if ((self->trashFolder = [self _getFolderWithName:
602 [self trashFolderName]]) == nil)
603 self->trashFolder = [self _getFolderWithName:[self trashFolderName]];
604 if (self->trashFolder == nil)
605 NSLog(@"WARNING[%s]: Couldn't find/create trashFolder", __PRETTY_FUNCTION__);
608 - (void)initializeDraftsFolder {
609 if ((self->draftsFolder = [self _getFolderWithName:
610 [self draftsFolderName]]) == nil)
611 self->draftsFolder = [self _getFolderWithName:
612 [self draftsFolderName]];
613 if (self->draftsFolder == nil)
614 NSLog(@"WARNING[%s]: Couldn't find/create draftsFolder", __PRETTY_FUNCTION__);
617 - (void)initializeInboxFolder {
618 if ((self->inboxFolder = [self _getFolderWithName:@"Inbox"]) == nil)
619 self->inboxFolder = [self _getFolderWithName:@"Inbox"];
621 if (self->inboxFolder == nil)
622 NSLog(@"WARNING[%s]: Couldn't find/create inbox", __PRETTY_FUNCTION__);
625 - (NGImap4Folder *)folderWithName:(NSString *)_name {
626 return [self folderWithName:_name caseInsensitive:NO];
629 - (NGImap4Folder *)folderWithName:(NSString *)_name
630 caseInsensitive:(BOOL)_caseIn
632 NSEnumerator *enumerator;
636 [self resetLastException];
637 enumerator = [[_name componentsSeparatedByString:@"/"] objectEnumerator];
638 f = [self serverRoot];
640 while ((obj = [enumerator nextObject])) {
641 if ([obj length] > 0)
642 f = [f subFolderWithName:obj caseInsensitive:_caseIn];
644 return self->lastException ? nil : f;
647 - (BOOL)createFolderWithPath:(NSString *)_name {
648 NSEnumerator *enumerator;
649 id<NGImap4Folder> f1, f2;
652 [self resetLastException];
654 enumerator = [[_name componentsSeparatedByString:@"/"] objectEnumerator];
655 f1 = [self serverRoot];
657 while ((name = [enumerator nextObject])) {
658 if ((f2 = [f1 subFolderWithName:name caseInsensitive:YES]) == nil)
664 if (![f1 createSubFolderWithName:name])
666 f1 = [f1 subFolderWithName:name caseInsensitive:YES];
667 } while ((name = [enumerator nextObject]));
669 return self->lastException ? NO : YES;
672 - (void)resetSpecialFolders {
673 self->sentFolder = nil;
674 self->trashFolder = nil;
675 self->draftsFolder = nil;
676 self->inboxFolder = nil;
677 self->serverRoot = nil;
680 - (NSArray *)newMessages {
681 NSEnumerator *enumerator;
683 NSMutableArray *result;
686 [self resetLastException];
688 qual = [EOQualifier qualifierWithQualifierFormat:@"flags = \"recent\""];
689 result = [NSMutableArray array];
691 [self->inboxFolder status];
692 if ([self->inboxFolder hasNewMessagesSearchRecursiv:NO]) {
695 array = [self->inboxFolder messagesForQualifier:qual];
697 [result addObjectsFromArray:array];
699 enumerator = [self->folderForRefresh objectEnumerator];
700 while ((f = [enumerator nextObject])) {
702 if ([f hasNewMessagesSearchRecursiv:NO]) {
705 array = [self->inboxFolder messagesForQualifier:qual];
707 [result addObjectsFromArray:array];
710 return self->lastException ? nil : result;
713 - (BOOL)hasNewMessages {
714 NSEnumerator *enumerator;
718 [self resetLastException];
720 [self->inboxFolder status];
721 if ([self->inboxFolder hasNewMessagesSearchRecursiv:NO])
725 enumerator = [self->folderForRefresh objectEnumerator];
727 while ((f = [enumerator nextObject])) {
729 if ([f hasNewMessagesSearchRecursiv:NO]) {
734 return self->lastException ? NO : result;
738 return [self->connectionDictionary objectForKey:@"host"];
740 - (NSString *)login {
741 return [self->connectionDictionary objectForKey:@"login"];
744 - (BOOL)registerForRefresh:(NGImap4Folder *)_folder {
745 [self->folderForRefresh addObject:_folder];
749 - (BOOL)removeFromRefresh:(NGImap4Folder *)_folder {
750 [self->folderForRefresh removeObject:_folder];
754 - (BOOL)removeAllFromRefresh {
755 [self->folderForRefresh removeAllObjects];
759 - (BOOL)refreshFolder {
761 // this runs status on each folder and status triggers notifications?
762 NSEnumerator *enumerator;
764 BOOL refreshInbox = NO;
766 if ([self lastException] != nil)
769 enumerator = [self->folderForRefresh objectEnumerator];
771 [self resetLastException];
773 while ((f = [enumerator nextObject])) {
774 if ([f isEqual:self->inboxFolder])
781 [self->inboxFolder status];
783 return self->lastException ? NO : YES;
787 return self->serverName;
790 return self->serverKind;
792 - (id)serverVersion {
793 return self->serverVersion;
795 - (id)serverSubVersion {
796 return self->serverSubVersion;
799 return self->serverTag;
806 [self->serverRoot resetSync];
808 [self logWithFormat:@"WARNING: resetSync has no effect if syncMode == NO"];
811 - (BOOL)isInSyncMode {
812 return self->syncMode;
815 - (void)enterSyncMode {
816 self->syncMode = YES;
820 - (void)leaveSyncMode {
824 - (BOOL)showOnlySubscribedInRoot {
825 if (self->showOnlySubscribedInRoot == -1)
828 return (self->showOnlySubscribedInRoot == 1) ? YES : NO;
831 - (BOOL)showOnlySubscribedInSubFolders {
832 if (self->showOnlySubscribedInSubFolders == -1)
835 return (self->showOnlySubscribedInSubFolders == 1) ? YES : NO;
838 - (BOOL)subscribeFolderFailed {
839 if (self->subscribeFolderFailed == -1)
842 return (self->subscribeFolderFailed == 1) ? YES : NO;
845 - (NSString *)sortEncoding {
846 if (self->sortEncoding == nil)
847 self->sortEncoding = @"UTF-8";
849 return self->sortEncoding;
855 if (self->capability == nil) {
856 if (![self openConnection])
859 if (self->canSort == -1) {
861 ([self->capability containsObject:@"sort"])? 1 : 0;
863 return self->canSort;
867 if (self->capability == nil) {
868 if (![self openConnection])
871 if (self->canQuota == -1) {
873 ([self->capability containsObject:@"quota"])? 1 : 0;
875 return self->canQuota;
878 /* URL based factory */
880 + (id)messageWithURL:(id)_url {
885 if (![_url isKindOfClass:[NSURL class]])
886 _url = [NSURL URLWithString:[_url stringValue]];
888 if ((ctx = [self imap4ContextWithURL:_url]) == nil) {
889 NSLog(@"WARNING(%s): got no IMAP4 context for URL: %@",
890 __PRETTY_FUNCTION__, _url);
893 return [ctx messageWithURL:_url];
895 - (id)folderWithURL:(id)_url {
896 NSString *path, *folderPath;
898 if (_url != nil && ![_url isKindOfClass:[NSURL class]])
899 _url = [NSURL URLWithString:[_url stringValue]];
904 folderPath = [path stringByDeletingLastPathComponent];
906 return [self folderWithName:folderPath];
908 - (id)messageWithURL:(id)_url {
909 NSString *path, *folderPath;
913 if (_url != nil && ![_url isKindOfClass:[NSURL class]])
914 _url = [NSURL URLWithString:[_url stringValue]];
919 folderPath = [path stringByDeletingLastPathComponent];
920 messageID = [[path lastPathComponent] intValue];
922 if ((f = [self folderWithName:folderPath]) == nil) {
923 [self logWithFormat:@"WARNING(%s): missing folder for URL: '%@'",
924 __PRETTY_FUNCTION__, _url];
927 return [f messageWithUid:messageID];
932 - (NSString *)description {
936 ms = [NSMutableString stringWithCapacity:64];
938 [ms appendFormat:@"<0x%08X[%@]:", self, NSStringFromClass([self class])];
940 if ((tmp = [self host]))
941 [ms appendFormat:@" host=%@", tmp];
942 if ((tmp = [self login]))
943 [ms appendFormat:@" login=%@", tmp];
945 [ms appendFormat:@" server='%@'/%@/%@.%@/%@",
948 [self serverVersion],
949 [self serverSubVersion],
953 [ms appendString:@" syncmode"];
955 [ms appendString:@">"];
960 @end /* NGImap4Context(Capability) */