/* Copyright (C) 2004-2005 SKYRIX Software AG This file is part of OpenGroupware.org. OGo is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2, or (at your option) any later version. OGo is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with OGo; see the file COPYING. If not, write to the Free Software Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #include "SOGoMailAccount.h" #include "SOGoMailFolder.h" #include "SOGoMailManager.h" #include "SOGoDraftsFolder.h" #include "SOGoUser+Mail.h" #include #include "common.h" @implementation SOGoMailAccount static NSArray *rootFolderNames = nil; static NSString *inboxFolderName = @"INBOX"; static NSString *draftsFolderName = @"Drafts"; static NSString *sieveFolderName = @"Filters"; static NSString *sharedFolderName = @""; // TODO: add English default static NSString *otherUsersFolderName = @""; // TODO: add English default static BOOL useAltNamespace = NO; + (int)version { return [super version] + 0 /* v1 */; } + (void)initialize { NSUserDefaults *ud = [NSUserDefaults standardUserDefaults]; NSString *cfgDraftsFolderName; NSAssert2([super version] == 1, @"invalid superclass (%@) version %i !", NSStringFromClass([self superclass]), [super version]); useAltNamespace = [ud boolForKey:@"SOGoSpecialFoldersInRoot"]; sharedFolderName = [ud stringForKey:@"SOGoSharedFolderName"]; otherUsersFolderName = [ud stringForKey:@"SOGoOtherUsersFolderName"]; cfgDraftsFolderName = [ud stringForKey:@"SOGoDraftsFolderName"]; if ([cfgDraftsFolderName length] > 0) { ASSIGN (draftsFolderName, cfgDraftsFolderName); NSLog(@"Note: using drafts folder named: '%@'", draftsFolderName); } NSLog(@"Note: using shared-folders name: '%@'", sharedFolderName); NSLog(@"Note: using other-users-folders name: '%@'", otherUsersFolderName); if ([ud boolForKey:@"SOGoEnableSieveFolder"]) { rootFolderNames = [[NSArray alloc] initWithObjects: draftsFolderName, sieveFolderName, nil]; } else { rootFolderNames = [[NSArray alloc] initWithObjects: draftsFolderName, nil]; } } /* shared accounts */ - (BOOL)isSharedAccount { NSString *s; NSRange r; s = [self nameInContainer]; r = [s rangeOfString:@"@"]; if (r.length == 0) /* regular HTTP logins are never a shared mailbox */ return NO; s = [s substringToIndex:r.location]; return [s rangeOfString:@".-."].length > 0 ? YES : NO; } - (NSString *)sharedAccountName { return nil; } /* listing the available folders */ - (NSArray *)additionalRootFolderNames { return rootFolderNames; } - (NSArray *)toManyRelationshipKeys { NSArray *a, *b; a = [self additionalRootFolderNames]; b = [[self imap4Connection] subfoldersForURL:[self imap4URL]]; return [b count] > 0 ? [b arrayByAddingObjectsFromArray:a] : a; } /* identity */ - (SOGoMailIdentity *)preferredIdentity { WOContext *ctx; if ((ctx = [[WOApplication application] context]) == nil) { [self logWithFormat:@"ERROR(%s): cannot procede without context!", __PRETTY_FUNCTION__]; return nil; } return [[ctx activeUser] primaryMailIdentityForAccount: [self nameInContainer]]; } /* hierarchy */ - (SOGoMailAccount *)mailAccountFolder { return self; } - (NSArray *)allFolderPathes { NSArray *pathes; pathes = [[self imap4Connection] allFoldersForURL:[self imap4URL] ]; pathes = [pathes sortedArrayUsingSelector:@selector(compare:)]; return pathes; } /* IMAP4 */ - (BOOL)useSSL { return NO; } - (NSString *)imap4LoginFromHTTP { WORequest *rq; NSString *s; NSArray *creds; rq = [[(WOApplication *)[WOApplication application] context] request]; s = [rq headerForKey:@"x-webobjects-remote-user"]; if ([s length] > 0) return s; if ((s = [rq headerForKey:@"authorization"]) == nil) { /* no basic auth */ return nil; } creds = [SoHTTPAuthenticator parseCredentials:s]; if ([creds count] < 2) /* somehow invalid */ return nil; return [creds objectAtIndex:0]; /* the user */ } - (NSString *)imap4URLString { /* private, overridden by SOGoSharedMailAccount */ NSString *s; NSRange r; s = [self nameInContainer]; r = [s rangeOfString:@"@"]; if (r.length == 0) { NSString *u; u = [self imap4LoginFromHTTP]; if ([u length] == 0) { [self errorWithFormat:@"missing login in account folder name: %@", s]; return nil; } s = [[u stringByAppendingString:@"@"] stringByAppendingString:s]; } if ([s hasSuffix:@":80"]) { // HACK [self logWithFormat:@"WARNING: incorrect value for IMAP4 URL: '%@'", s]; s = [s substringToIndex:([s length] - 3)]; } s = [([self useSSL] ? @"imaps://" : @"imap://") stringByAppendingString:s]; s = [s stringByAppendingString:@"/"]; return s; } - (NSURL *)imap4URL { /* imap://agenortest@mail.opengroupware.org/ */ NSString *s; if (self->imap4URL != nil) return self->imap4URL; if ((s = [self imap4URLString]) == nil) return nil; self->imap4URL = [[NSURL alloc] initWithString:s]; return self->imap4URL; } - (NSString *)imap4Login { return [[self imap4URL] user]; } /* name lookup */ - (id)lookupFolder:(NSString *)_key ofClassNamed:(NSString *)_cn inContext:(id)_cx { Class clazz; if ((clazz = NSClassFromString(_cn)) == Nil) { [self logWithFormat:@"ERROR: did not find class '%@' for key: '%@'", _cn, _key]; return [NSException exceptionWithHTTPStatus:500 /* server error */ reason:@"did not find mail folder class!"]; } return [[[clazz alloc] initWithName:_key inContainer:self] autorelease]; } - (id)lookupImap4Folder:(NSString *)_key inContext:(id)_cx { NSString *s; s = [_key isEqualToString:[self trashFolderNameInContext:_cx]] ? @"SOGoTrashFolder" : @"SOGoMailFolder"; return [self lookupFolder:_key ofClassNamed:s inContext:_cx]; } - (id)lookupDraftsFolder:(NSString *)_key inContext:(id)_ctx { return [self lookupFolder:_key ofClassNamed:@"SOGoDraftsFolder" inContext:_ctx]; } - (id)lookupFiltersFolder:(NSString *)_key inContext:(id)_ctx { return [self lookupFolder:_key ofClassNamed:@"SOGoSieveScriptsFolder" inContext:_ctx]; } - (id)lookupName:(NSString *)_key inContext:(id)_ctx acquire:(BOOL)_flag { id obj; /* first check attributes directly bound to the application */ if ((obj = [super lookupName:_key inContext:_ctx acquire:NO]) != nil) return obj; // TODO: those should be product.plist bindings? (can't be class bindings // though because they are 'per-account') if ([_key isEqualToString: draftsFolderName]) { if ((obj = [self lookupDraftsFolder:_key inContext:_ctx]) != nil) return obj; } if ([_key isEqualToString: sieveFolderName]) { if ((obj = [self lookupFiltersFolder:_key inContext:_ctx]) != nil) return obj; } if ((obj = [self lookupImap4Folder:_key inContext:_ctx]) != nil) return obj; /* return 404 to stop acquisition */ return [NSException exceptionWithHTTPStatus:404 /* Not Found */]; } /* special folders */ - (NSString *)inboxFolderNameInContext:(id)_ctx { return inboxFolderName; /* cannot be changed in Cyrus ? */ } - (NSString *)draftsFolderNameInContext:(id)_ctx { return draftsFolderName; /* SOGo managed folder */ } - (NSString *)sieveFolderNameInContext:(id)_ctx { return sieveFolderName; /* SOGo managed folder */ } - (NSString *)sentFolderNameInContext:(id)_ctx { /* OGo issue #1225 */ static NSString *s = nil; if (s == nil) { NSUserDefaults *ud; ud = [NSUserDefaults standardUserDefaults]; s = [[ud stringForKey:@"SOGoSentFolderName"] copy]; if ([s length] == 0) s = @"Sent"; [self logWithFormat:@"Note: using SOGoSentFolderName: '%@'", s]; } return s; } - (NSString *)trashFolderNameInContext:(id)_ctx { /* OGo issue #1225 */ static NSString *s = nil; if (s == nil) { NSUserDefaults *ud; ud = [NSUserDefaults standardUserDefaults]; s = [[ud stringForKey:@"SOGoTrashFolderName"] copy]; if ([s length] == 0) s = @"Trash"; NSLog(@"Note: using SOGoTrashFolderName: '%@'", s); } return s; } - (SOGoMailFolder *)inboxFolderInContext:(id)_ctx { // TODO: use some profile to determine real location, use a -traverse lookup SOGoMailFolder *folder; if (self->inboxFolder != nil) return self->inboxFolder; folder = [self lookupName:[self inboxFolderNameInContext:_ctx] inContext:_ctx acquire:NO]; if ([folder isKindOfClass:[NSException class]]) return folder; return ((self->inboxFolder = [folder retain])); } - (SOGoMailFolder *)sentFolderInContext:(id)_ctx { // TODO: use some profile to determine real location, use a -traverse lookup SOGoMailFolder *folder; if (self->sentFolder != nil) return self->sentFolder; folder = useAltNamespace ? (id)self : [self inboxFolderInContext:_ctx]; if ([folder isKindOfClass:[NSException class]]) return folder; folder = [folder lookupName:[self sentFolderNameInContext:_ctx] inContext:_ctx acquire:NO]; if ([folder isKindOfClass:[NSException class]]) return folder; if (![folder isNotNull]) { return [NSException exceptionWithHTTPStatus:404 /* not found */ reason:@"did not find Sent folder!"]; } return ((self->sentFolder = [folder retain])); } - (SOGoMailFolder *)trashFolderInContext:(id)_ctx { // TODO: use some profile to determine real location SOGoMailFolder *folder; if (self->trashFolder != nil) return self->trashFolder; folder = useAltNamespace ? (id)self : [self inboxFolderInContext:_ctx]; if ([folder isKindOfClass:[NSException class]]) return folder; folder = [folder lookupName:[self trashFolderNameInContext:_ctx] inContext:_ctx acquire:NO]; if ([folder isKindOfClass:[NSException class]]) return folder; if (![folder isNotNull]) { return [NSException exceptionWithHTTPStatus:404 /* not found */ reason:@"did not find Trash folder!"]; } return ((self->trashFolder = [folder retain])); } /* WebDAV */ - (BOOL)davIsCollection { return YES; } - (NSException *)davCreateCollection:(NSString *)_name inContext:(id)_ctx { return [[self imap4Connection] createMailbox:_name atURL:[self imap4URL]]; } - (NSString *)shortTitle { NSString *s, *login, *host; NSRange r; s = [self nameInContainer]; r = [s rangeOfString:@"@"]; if (r.length > 0) { login = [s substringToIndex:r.location]; host = [s substringFromIndex:(r.location + r.length)]; } else { login = nil; host = s; } r = [host rangeOfString:@"."]; if (r.length > 0) host = [host substringToIndex:r.location]; if ([login length] == 0) return host; r = [login rangeOfString:@"."]; if (r.length > 0) login = [login substringToIndex:r.location]; return [NSString stringWithFormat:@"%@@%@", login, host]; } - (NSString *)davDisplayName { return [self shortTitle]; } @end /* SOGoMailAccount */