diff --git a/SoObjects/Mailer/GNUmakefile b/SoObjects/Mailer/GNUmakefile index d96f672af..92ca7d45b 100644 --- a/SoObjects/Mailer/GNUmakefile +++ b/SoObjects/Mailer/GNUmakefile @@ -92,6 +92,10 @@ Mailer_RESOURCE_FILES += \ SOGoMailWelshReply.wo +Mailer_LANGUAGES = English French + +Mailer_LOCALIZED_RESOURCE_FILES = Localizable.strings + ADDITIONAL_INCLUDE_DIRS += -I../../SOPE/ ADDITIONAL_INCLUDE_DIRS += $(shell xml2-config --cflags) ADDITIONAL_LIB_DIRS += -L../../SOPE/GDLContentStore/obj/ diff --git a/SoObjects/Mailer/SOGoMailAccount.h b/SoObjects/Mailer/SOGoMailAccount.h index 011a7620e..cec85d61e 100644 --- a/SoObjects/Mailer/SOGoMailAccount.h +++ b/SoObjects/Mailer/SOGoMailAccount.h @@ -80,10 +80,11 @@ typedef enum { - (NSArray *) toManyRelationshipKeysWithNamespaces: (BOOL) withNSs; - (NSArray *) allFolderPaths; +- (NSArray *) allFoldersMetadata; + - (BOOL) isInDraftsFolder; /* special folders */ - - (NSString *) inboxFolderNameInContext: (id)_ctx; - (NSString *) draftsFolderNameInContext: (id)_ctx; - (NSString *) sentFolderNameInContext: (id)_ctx; diff --git a/SoObjects/Mailer/SOGoMailAccount.m b/SoObjects/Mailer/SOGoMailAccount.m index c396b04d1..276bc208a 100644 --- a/SoObjects/Mailer/SOGoMailAccount.m +++ b/SoObjects/Mailer/SOGoMailAccount.m @@ -21,6 +21,7 @@ */ #import +#import #import #import #import @@ -47,6 +48,7 @@ #import #import #import +#import #import #import "SOGoDraftsFolder.h" @@ -339,6 +341,9 @@ static NSString *inboxFolderName = @"INBOX"; return folders; } +// +// +// - (NSArray *) allFolderPaths { NSMutableArray *folderPaths, *namespaces; @@ -382,8 +387,128 @@ static NSString *inboxFolderName = @"INBOX"; return folderPaths; } -/* IMAP4 */ +// +// +// +- (NSString *) _folderType: (NSString *) folderName +{ + NSString *folderType; + if ([folderName isEqualToString: [NSString stringWithFormat: @"/%@", inboxFolderName]]) + folderType = @"inbox"; + else if ([folderName isEqualToString: [NSString stringWithFormat: @"/%@", [self draftsFolderNameInContext: context]]]) + folderType = @"draft"; + else if ([folderName isEqualToString: [NSString stringWithFormat: @"/%@", [self sentFolderNameInContext: context]]]) + folderType = @"sent"; + else if ([folderName isEqualToString: [NSString stringWithFormat: @"/%@", [self trashFolderNameInContext: context]]]) + folderType = @"trash"; + else + folderType = @"folder"; + + return folderType; +} + +- (NSString *) _parentForFolder: (NSString *) folderName + foldersList: (NSArray *) theFolders +{ + NSArray *pathComponents; + NSString *s; + int i; + + pathComponents = [folderName pathComponents]; + s = [[[pathComponents subarrayWithRange: NSMakeRange(0,[pathComponents count]-1)] componentsJoinedByString: @"/"] substringFromIndex: 1]; + + for (i = 0; i < [theFolders count]; i++) + { + if ([s isEqualToString: [theFolders objectAtIndex: i]]) + return s; + } + + return nil; +} + +// +// +// +- (NSArray *) allFoldersMetadata +{ + NSString *currentFolder, *currentDecodedFolder, *currentDisplayName, *currentFolderType, *login, *fullName, *parent; + NSMutableArray *pathComponents, *folders; + SOGoUserManager *userManager; + NSEnumerator *rawFolders; + NSDictionary *folderData; + NSAutoreleasePool *pool; + NSArray *allFolderPaths; + + allFolderPaths = [self allFolderPaths]; + rawFolders = [allFolderPaths objectEnumerator]; + + folders = [NSMutableArray array]; + while ((currentFolder = [rawFolders nextObject])) + { + // Using a local pool to avoid using too many file descriptors. This could + // happen with tons of mailboxes under "Other Users" as LDAP connections + // are never reused and "autoreleased" at the end. This loop would consume + // lots of LDAP connections during its execution. + pool = [[NSAutoreleasePool alloc] init]; + + currentDecodedFolder = [currentFolder stringByDecodingImap4FolderName]; + currentFolderType = [self _folderType: currentFolder]; + + // We translate the "Other Users" and "Shared Folders" namespaces. + // While we're at it, we also translate the user's mailbox names + // to the full name of the person. + if (otherUsersFolderName && [currentDecodedFolder hasPrefix: [NSString stringWithFormat: @"/%@", otherUsersFolderName]]) + { + // We have a string like /Other Users/lmarcotte/... under Cyrus, but we could + // also have something like /shared under Dovecot. So we swap the username only + // if we have one, of course. + pathComponents = [NSMutableArray arrayWithArray: [currentDecodedFolder pathComponents]]; + + if ([pathComponents count] > 2) + { + login = [pathComponents objectAtIndex: 2]; + userManager = [SOGoUserManager sharedUserManager]; + fullName = [userManager getCNForUID: login]; + [pathComponents removeObjectsInRange: NSMakeRange(0,3)]; + + currentDisplayName = [NSString stringWithFormat: @"/%@/%@/%@", + [self labelForKey: @"OtherUsersFolderName"], + (fullName != nil ? fullName : login), + [pathComponents componentsJoinedByString: @"/"]]; + + } + else + { + currentDisplayName = [NSString stringWithFormat: @"/%@%@", + [self labelForKey: @"OtherUsersFolderName"], + [currentDecodedFolder substringFromIndex: + [otherUsersFolderName length]+1]]; + } + } + else if (sharedFoldersName && [currentDecodedFolder hasPrefix: [NSString stringWithFormat: @"/%@", sharedFoldersName]]) + currentDisplayName = [NSString stringWithFormat: @"/%@%@", [self labelForKey: @"SharedFoldersName"], + [currentDecodedFolder substringFromIndex: [sharedFoldersName length]+1]]; + else + currentDisplayName = currentDecodedFolder; + + parent = [self _parentForFolder: currentFolder foldersList: allFolderPaths]; + + folderData = [NSDictionary dictionaryWithObjectsAndKeys: + currentFolder, @"path", + currentFolderType, @"type", + currentDisplayName, @"displayName", + parent, @"parent", + nil]; + [folders addObject: folderData]; + [pool release]; + } + + return folders; +} + + +/* IMAP4 */ - (NSDictionary *) _mailAccount { NSDictionary *mailAccount; diff --git a/SoObjects/Mailer/SOGoMailFolder.h b/SoObjects/Mailer/SOGoMailFolder.h index f80702ce5..2c9855bbe 100644 --- a/SoObjects/Mailer/SOGoMailFolder.h +++ b/SoObjects/Mailer/SOGoMailFolder.h @@ -94,6 +94,8 @@ - (NSCalendarDate *) mostRecentMessageDate; +- (NSString *) davCollectionTag; + /* flags */ - (NSException *) addFlagsToAllMessages: (id) _f; diff --git a/SoObjects/Mailer/SOGoMailFolder.m b/SoObjects/Mailer/SOGoMailFolder.m index 925ec4ade..76fb70073 100644 --- a/SoObjects/Mailer/SOGoMailFolder.m +++ b/SoObjects/Mailer/SOGoMailFolder.m @@ -24,6 +24,7 @@ #import #import #import +#import #import #import @@ -1924,6 +1925,145 @@ static NSString *defaultUserID = @"anyone"; return date; } +- (NSString *) davCollectionTag +{ + NSString *tag; + + tag = @"-1"; + + if ([self imap4Connection]) + { + NSString *folderName; + NSDictionary *result; + + folderName = [imap4 imap4FolderNameForURL: [self imap4URL]]; + + [[imap4 client] unselect]; + + result = [[imap4 client] select: folderName]; + + tag = [NSString stringWithFormat: @"%@-%@", [result objectForKey: @"uidnext"], [result objectForKey: @"highestmodseq"]]; + } + + return tag; +} + +// +// FIXME - see below for code refactoring with MAPIStoreMailFolder. +// +- (EOQualifier *) _nonDeletedQualifier +{ + static EOQualifier *nonDeletedQualifier = nil; + EOQualifier *deletedQualifier; + + if (!nonDeletedQualifier) + { + deletedQualifier + = [[EOKeyValueQualifier alloc] + initWithKey: @"FLAGS" + operatorSelector: EOQualifierOperatorContains + value: [NSArray arrayWithObject: @"Deleted"]]; + nonDeletedQualifier = [[EONotQualifier alloc] + initWithQualifier: deletedQualifier]; + [deletedQualifier release]; + } + + return nonDeletedQualifier; +} + + + +// +// Check updated items +// +// +// +// . uid fetch 1:* (FLAGS) (changedsince 171) +// +// Deleted: "UID FETCH 1:* (UID) (CHANGEDSINCE 171 VANISHED)" + +// fetchUIDsOfVanishedItems .. +// +// . uid fetch 1:* (FLAGS) (changedsince 176 vanished) +// * VANISHED (EARLIER) 36 +// +// +// FIXME: refactor MAPIStoreMailFolder.m - synchroniseCache to use this method +// +- (NSArray *) syncTokenFieldsWithProperties: (NSArray *) theProperties + matchingSyncToken: (NSString *) theSyncToken +{ + EOQualifier *searchQualifier; + NSMutableArray *allTokens; + NSArray *a, *uids; + NSDictionary *d; + + int uidnext, highestmodseq, i; + + allTokens = [NSMutableArray array]; + a = [theSyncToken componentsSeparatedByString: @"-"]; + uidnext = [[a objectAtIndex: 0] intValue]; + highestmodseq = [[a objectAtIndex: 1] intValue]; + + // We first make sure QRESYNC is enabled + [[self imap4Connection] enableExtensions: [NSArray arrayWithObject: @"QRESYNC"]]; + + + // We fetch new messages and modified messages + if (highestmodseq) + { + EOKeyValueQualifier *kvQualifier; + NSNumber *nextModseq; + + nextModseq = [NSNumber numberWithUnsignedLongLong: highestmodseq + 1]; + kvQualifier = [[EOKeyValueQualifier alloc] + initWithKey: @"modseq" + operatorSelector: EOQualifierOperatorGreaterThanOrEqualTo + value: nextModseq]; + searchQualifier = [[EOAndQualifier alloc] + initWithQualifiers: + kvQualifier, [self _nonDeletedQualifier], nil]; + [kvQualifier release]; + [searchQualifier autorelease]; + } + else + { + searchQualifier = [self _nonDeletedQualifier]; + } + + // we fetch modified or added uids + uids = [self fetchUIDsMatchingQualifier: searchQualifier + sortOrdering: nil]; + + for (i = 0; i < [uids count]; i++) + { + // New messages + if ([[uids objectAtIndex: i] intValue] >= uidnext) + { + d = [NSDictionary dictionaryWithObject: @"added" forKey: [uids objectAtIndex: i]]; + } + // Changed messages + else + { + d = [NSDictionary dictionaryWithObject: @"changed" forKey: [uids objectAtIndex: i]]; + } + + [allTokens addObject: d]; + } + + + // We fetch deleted ones + uids = [self fetchUIDsOfVanishedItems: highestmodseq]; + + for (i = 0; i < [uids count]; i++) + { + d = [NSDictionary dictionaryWithObject: @"deleted" forKey: [uids objectAtIndex: i]]; + [allTokens addObject: d]; + } + + return allTokens; +} + @end /* SOGoMailFolder */ @implementation SOGoSpecialMailFolder diff --git a/SoObjects/Mailer/SOGoMailObject.h b/SoObjects/Mailer/SOGoMailObject.h index 182efeea0..46c1d7c14 100644 --- a/SoObjects/Mailer/SOGoMailObject.h +++ b/SoObjects/Mailer/SOGoMailObject.h @@ -81,6 +81,8 @@ NSArray *SOGoMailCoreInfoKeys; - (id) bodyStructure; - (id) lookupInfoForBodyPart:(id)_path; +- (id) lookupImap4BodyPartKey: (NSString *) _key + inContext: (id) _ctx; /* content */ diff --git a/UI/MailerUI/UIxMailAccountActions.h b/UI/MailerUI/UIxMailAccountActions.h index 08874fcf3..286b543c5 100644 --- a/UI/MailerUI/UIxMailAccountActions.h +++ b/UI/MailerUI/UIxMailAccountActions.h @@ -1,8 +1,6 @@ /* UIxMailAccountActions.h - this file is part of SOGo * - * Copyright (C) 2007-2011 Inverse inc. - * - * Author: Wolfgang Sourdeau + * Copyright (C) 2007-2013 Inverse inc. * * This file is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -29,12 +27,6 @@ @interface UIxMailAccountActions : WODirectAction { - NSString *inboxFolderName; - NSString *draftsFolderName; - NSString *sentFolderName; - NSString *trashFolderName; - NSString *otherUsersFolderName; - NSString *sharedFoldersName; } - (WOResponse *) listMailboxesAction; diff --git a/UI/MailerUI/UIxMailAccountActions.m b/UI/MailerUI/UIxMailAccountActions.m index a06411294..e331ab4ab 100644 --- a/UI/MailerUI/UIxMailAccountActions.m +++ b/UI/MailerUI/UIxMailAccountActions.m @@ -2,9 +2,6 @@ * * Copyright (C) 2007-2013 Inverse inc. * - * Author: Wolfgang Sourdeau - * Ludovic Marcotte - * * This file is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2, or (at your option) @@ -51,159 +48,16 @@ @implementation UIxMailAccountActions -- (id) init -{ - if ((self = [super init])) - { - inboxFolderName = nil; - draftsFolderName = nil; - sentFolderName = nil; - trashFolderName = nil; - otherUsersFolderName = nil; - sharedFoldersName = nil; - } - - return self; -} - -- (void) dealloc -{ - [inboxFolderName release]; - [draftsFolderName release]; - [sentFolderName release]; - [trashFolderName release]; - [otherUsersFolderName release]; - [sharedFoldersName release]; - [super dealloc]; -} - -- (NSString *) _folderType: (NSString *) folderName -{ - NSString *folderType; - SOGoMailAccount *co; - NSArray *specialFolders; - - if (!inboxFolderName) - { - co = [self clientObject]; - specialFolders = [[NSArray arrayWithObjects: - [co inboxFolderNameInContext: context], - [co draftsFolderNameInContext: context], - [co sentFolderNameInContext: context], - [co trashFolderNameInContext: context], - [co otherUsersFolderNameInContext: context], - [co sharedFoldersNameInContext: context], - nil] stringsWithFormat: @"/%@"]; - ASSIGN(inboxFolderName, [specialFolders objectAtIndex: 0]); - ASSIGN(draftsFolderName, [specialFolders objectAtIndex: 1]); - ASSIGN(sentFolderName, [specialFolders objectAtIndex: 2]); - ASSIGN(trashFolderName, [specialFolders objectAtIndex: 3]); - if ([specialFolders count] > 4) - ASSIGN(otherUsersFolderName, [specialFolders objectAtIndex: 4]); - if ([specialFolders count] > 5) - ASSIGN(sharedFoldersName, [specialFolders objectAtIndex: 5]); - } - - if ([folderName isEqualToString: inboxFolderName]) - folderType = @"inbox"; - else if ([folderName isEqualToString: draftsFolderName]) - folderType = @"draft"; - else if ([folderName isEqualToString: sentFolderName]) - folderType = @"sent"; - else if ([folderName isEqualToString: trashFolderName]) - folderType = @"trash"; - else if ([folderName hasPrefix: [NSString stringWithFormat: @"%@/", draftsFolderName]]) - folderType = @"draft/folder"; - else if ([folderName hasPrefix: [NSString stringWithFormat: @"%@/", sentFolderName]]) - folderType = @"sent/folder"; - else - folderType = @"folder"; - - return folderType; -} - -- (NSArray *) _jsonFolders: (NSEnumerator *) rawFolders -{ - NSString *currentFolder, *currentDecodedFolder, *currentDisplayName, *currentFolderType, *login, *fullName; - NSMutableArray *pathComponents; - SOGoUserManager *userManager; - NSDictionary *folderData; - NSMutableArray *folders; - NSAutoreleasePool *pool; - - folders = [NSMutableArray array]; - while ((currentFolder = [rawFolders nextObject])) - { - // Using a local pool to avoid using too many file descriptors. This could - // happen with tons of mailboxes under "Other Users" as LDAP connections - // are never reused and "autoreleased" at the end. This loop would consume - // lots of LDAP connections during its execution. - pool = [[NSAutoreleasePool alloc] init]; - - currentDecodedFolder = [currentFolder stringByDecodingImap4FolderName]; - currentFolderType = [self _folderType: currentFolder]; - - // We translate the "Other Users" and "Shared Folders" namespaces. - // While we're at it, we also translate the user's mailbox names - // to the full name of the person. - if (otherUsersFolderName && [currentDecodedFolder hasPrefix: otherUsersFolderName]) - { - // We have a string like /Other Users/lmarcotte/... under Cyrus, but we could - // also have something like /shared under Dovecot. So we swap the username only - // if we have one, of course. - pathComponents = [NSMutableArray arrayWithArray: [currentDecodedFolder pathComponents]]; - - if ([pathComponents count] > 2) - { - login = [pathComponents objectAtIndex: 2]; - userManager = [SOGoUserManager sharedUserManager]; - fullName = [userManager getCNForUID: login]; - [pathComponents removeObjectsInRange: NSMakeRange(0,3)]; - - currentDisplayName = [NSString stringWithFormat: @"/%@/%@/%@", - [self labelForKey: @"OtherUsersFolderName"], - (fullName != nil ? fullName : login), - [pathComponents componentsJoinedByString: @"/"]]; - - } - else - { - currentDisplayName = [NSString stringWithFormat: @"/%@%@", - [self labelForKey: @"OtherUsersFolderName"], - [currentDecodedFolder substringFromIndex: - [otherUsersFolderName length]]]; - } - } - else if (sharedFoldersName && [currentDecodedFolder hasPrefix: sharedFoldersName]) - currentDisplayName = [NSString stringWithFormat: @"/%@%@", [self labelForKey: @"SharedFoldersName"], - [currentDecodedFolder substringFromIndex: [sharedFoldersName length]]]; - else - currentDisplayName = currentDecodedFolder; - - folderData = [NSDictionary dictionaryWithObjectsAndKeys: - currentFolder, @"path", - currentFolderType, @"type", - currentDisplayName, @"displayName", - nil]; - [folders addObject: folderData]; - [pool release]; - } - - return folders; -} - - (WOResponse *) listMailboxesAction { SOGoMailAccount *co; - NSEnumerator *rawFolders; NSArray *folders; NSDictionary *data; WOResponse *response; co = [self clientObject]; - rawFolders = [[co allFolderPaths] objectEnumerator]; - folders = [self _jsonFolders: rawFolders]; + folders = [co allFoldersMetadata]; data = [NSDictionary dictionaryWithObjectsAndKeys: folders, @"mailboxes", nil]; response = [self responseWithStatus: 200 diff --git a/UI/MailerUI/UIxMailFolderActions.m b/UI/MailerUI/UIxMailFolderActions.m index 41d4afc5e..6e3c4e4d5 100644 --- a/UI/MailerUI/UIxMailFolderActions.m +++ b/UI/MailerUI/UIxMailFolderActions.m @@ -1,9 +1,6 @@ /* UIxMailFolderActions.m - this file is part of SOGo * - * Copyright (C) 2007-2011 Inverse inc. - * - * Author: Wolfgang Sourdeau - * Francis Lachapelle + * Copyright (C) 2007-2013 Inverse inc. * * This file is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by