Monotone-Parent: 584371c66b4989feaab5012db26f19d8ed7cd7ad

Monotone-Revision: 72a4f60ec4a14476e485da2ce400df860572fa48

Monotone-Author: wsourdeau@inverse.ca
Monotone-Date: 2010-01-05T22:34:35
Monotone-Branch: ca.inverse.sogo
maint-2.0.2
Wolfgang Sourdeau 2010-01-05 22:34:35 +00:00
parent 7c3b61134a
commit aa7e1d2f5a
15 changed files with 987 additions and 241 deletions

View File

@ -1,7 +1,34 @@
2010-01-05 Wolfgang Sourdeau <wsourdeau@inverse.ca> 2010-01-05 Wolfgang Sourdeau <wsourdeau@inverse.ca>
* UI/WebServerResources/MailerUI.js (Mailbox): "this.name" is now
an escaped form of the mailbox name, that can thus be used to
generate folder URLs.
(Mailbox.findMailboxByName): escape the name passed as parameter
prior to doing a search.
* UI/WebServerResources/JavascriptAPIExtensions.js
(asCSSIdentifier): use two arrays rather than an "Object" in order
to improve performance.
* SoObjects/SOGo/NSString+Utilities.m (-fromCSSIdentifier): new
method that converts an escaped string back to a normal one.
(-asCSSIdentifier): no longer use "replaceString:withString:" in
order to avoid parsing the string multiple times. This probably
enhance performances a bit and also avoid double-encoding
problems.
* SoObjects/SOGo/NSArray+Utilities.m (-resultsOfSelector): new
method that applies a selector on the contained objects and
collect the result objects in a new array.
* SoObjects/Mailer/SOGoMailFolder.m
(-lookupName:inContext:acquire:): we must check and create the
current folder even if the looked up key is a subfolder or a web
method.
* SOPE/sope-patchset-r1664.diff: new patchset replacing * SOPE/sope-patchset-r1664.diff: new patchset replacing
-r1660.diff. -r1660.diff.
Added code to handle IMAP namespaces.
2010-01-04 Wolfgang Sourdeau <wsourdeau@inverse.ca> 2010-01-04 Wolfgang Sourdeau <wsourdeau@inverse.ca>

File diff suppressed because it is too large Load Diff

View File

@ -11,12 +11,13 @@ Mailer_OBJC_FILES += \
\ \
SOGoMailManager.m \ SOGoMailManager.m \
\ \
SOGoDraftObject.m \
SOGoMailBaseObject.m \ SOGoMailBaseObject.m \
SOGoMailAccounts.m \ SOGoMailAccounts.m \
SOGoMailAccount.m \ SOGoMailAccount.m \
SOGoMailFolder.m \ SOGoMailFolder.m \
SOGoMailNamespace.m \
SOGoMailObject.m \ SOGoMailObject.m \
SOGoDraftObject.m \
SOGoMailObject+Draft.m \ SOGoMailObject+Draft.m \
SOGoSentFolder.m \ SOGoSentFolder.m \
SOGoDraftsFolder.m \ SOGoDraftsFolder.m \

View File

@ -23,7 +23,7 @@
#ifndef __Mailer_SOGoMailAccount_H__ #ifndef __Mailer_SOGoMailAccount_H__
#define __Mailer_SOGoMailAccount_H__ #define __Mailer_SOGoMailAccount_H__
#import <SoObjects/Mailer/SOGoMailBaseObject.h> #import <Mailer/SOGoMailBaseObject.h>
/* /*
SOGoMailAccount SOGoMailAccount
@ -37,9 +37,10 @@
@class NSArray; @class NSArray;
@class NSString; @class NSString;
#import "SOGoDraftsFolder.h" @class SOGoMailFolder;
#import "SOGoSentFolder.h" @class SOGoDraftsFolder;
#import "SOGoTrashFolder.h" @class SOGoSentFolder;
@class SOGoTrashFolder;
typedef enum { typedef enum {
undefined = -1, undefined = -1,

View File

@ -39,6 +39,7 @@
#import <NGImap4/NGSieveClient.h> #import <NGImap4/NGSieveClient.h>
#import <SOGo/NSArray+Utilities.h> #import <SOGo/NSArray+Utilities.h>
#import <SOGo/NSString+Utilities.h>
#import <SOGo/SOGoDomainDefaults.h> #import <SOGo/SOGoDomainDefaults.h>
#import <SOGo/SOGoUser.h> #import <SOGo/SOGoUser.h>
#import <SOGo/SOGoUserDefaults.h> #import <SOGo/SOGoUserDefaults.h>
@ -47,6 +48,7 @@
#import "SOGoDraftsFolder.h" #import "SOGoDraftsFolder.h"
#import "SOGoMailFolder.h" #import "SOGoMailFolder.h"
#import "SOGoMailManager.h" #import "SOGoMailManager.h"
#import "SOGoMailNamespace.h"
#import "SOGoSentFolder.h" #import "SOGoSentFolder.h"
#import "SOGoTrashFolder.h" #import "SOGoTrashFolder.h"
@ -94,25 +96,56 @@ static NSString *sieveScriptName = @"sogo";
return NO; return NO;
} }
- (void) _appendNamespace: (NSArray *) namespace
toFolders: (NSMutableArray *) folders
{
NSString *newFolder;
NSDictionary *currentPart;
int count, max;
max = [namespace count];
for (count = 0; count < max; count++)
{
currentPart = [namespace objectAtIndex: count];
newFolder
= [[currentPart objectForKey: @"prefix"] substringFromIndex: 1];
if ([newFolder length])
[folders addObject: newFolder];
}
}
- (void) _appendNamespaces: (NSMutableArray *) folders
{
NSDictionary *namespaceDict;
NSArray *namespace;
NGImap4Client *client;
client = [[self imap4Connection] client];
namespaceDict = [client namespace];
namespace = [namespaceDict objectForKey: @"personal"];
if (namespace)
[self _appendNamespace: namespace toFolders: folders];
namespace = [namespaceDict objectForKey: @"other users"];
if (namespace)
[self _appendNamespace: namespace toFolders: folders];
namespace = [namespaceDict objectForKey: @"shared"];
if (namespace)
[self _appendNamespace: namespace toFolders: folders];
}
- (NSArray *) toManyRelationshipKeys - (NSArray *) toManyRelationshipKeys
{ {
NSMutableArray *folders; NSMutableArray *folders;
NSArray *imapFolders, *additionalFolders; NSArray *imapFolders;
folders = [NSMutableArray array];
imapFolders = [[self imap4Connection] subfoldersForURL: [self imap4URL]]; imapFolders = [[self imap4Connection] subfoldersForURL: [self imap4URL]];
additionalFolders folders = [imapFolders mutableCopy];
= [NSArray arrayWithObject: [self draftsFolderNameInContext: nil]]; [folders autorelease];
if ([imapFolders count] > 0) [folders addObjectUniquely: [self draftsFolderNameInContext: nil]];
[folders addObjectsFromArray: imapFolders]; [self _appendNamespaces: folders];
if ([additionalFolders count] > 0)
{ return [[folders stringsWithFormat: @"folder%@"]
[folders removeObjectsInArray: additionalFolders]; resultsOfSelector: @selector (asCSSIdentifier)];
[folders addObjectsFromArray: additionalFolders];
}
return [folders stringsWithFormat: @"folder%@"];
} }
- (SOGoIMAPAclStyle) imapAclStyle - (SOGoIMAPAclStyle) imapAclStyle
@ -300,15 +333,32 @@ static NSString *sieveScriptName = @"sogo";
return self; return self;
} }
- (NSArray *) _allFoldersFromNS: (NSString *) namespace
subscribedOnly: (BOOL) subscribedOnly
{
NSArray *folders;
NSURL *nsURL;
NSString *baseURLString, *urlString;
baseURLString = [[self imap4URL] absoluteString];
urlString = [NSString stringWithFormat: @"%@%@/", baseURLString, [namespace stringByEscapingURL]];
nsURL = [NSURL URLWithString: urlString];
folders = [[self imap4Connection] allFoldersForURL: nsURL
onlySubscribedFolders: subscribedOnly];
return folders;
}
- (NSArray *) allFolderPaths - (NSArray *) allFolderPaths
{ {
NSMutableArray *folderPaths; NSMutableArray *folderPaths, *namespaces;
NSArray *rawFolders, *mainFolders; NSArray *folders, *mainFolders;
SOGoUserDefaults *ud; SOGoUserDefaults *ud;
BOOL subscribedOnly;
int count, max;
ud = [[context activeUser] userDefaults]; ud = [[context activeUser] userDefaults];
rawFolders = [[self imap4Connection] allFoldersForURL: [self imap4URL] subscribedOnly = [ud mailShowSubscribedFoldersOnly];
onlySubscribedFolders: [ud mailShowSubscribedFoldersOnly]];
mainFolders = [[NSArray arrayWithObjects: mainFolders = [[NSArray arrayWithObjects:
[self inboxFolderNameInContext: context], [self inboxFolderNameInContext: context],
@ -316,8 +366,24 @@ static NSString *sieveScriptName = @"sogo";
[self sentFolderNameInContext: context], [self sentFolderNameInContext: context],
[self trashFolderNameInContext: context], [self trashFolderNameInContext: context],
nil] stringsWithFormat: @"/%@"]; nil] stringsWithFormat: @"/%@"];
folderPaths = [NSMutableArray arrayWithArray: rawFolders]; folders = [[self imap4Connection] allFoldersForURL: [self imap4URL]
onlySubscribedFolders: subscribedOnly];
folderPaths = [folders mutableCopy];
[folderPaths autorelease];
[folderPaths removeObjectsInArray: mainFolders]; [folderPaths removeObjectsInArray: mainFolders];
namespaces = [NSMutableArray arrayWithCapacity: 10];
[self _appendNamespaces: namespaces];
max = [namespaces count];
for (count = 0; count < max; count++)
{
folders = [self _allFoldersFromNS: [namespaces objectAtIndex: count]
subscribedOnly: subscribedOnly];
if ([folders count])
{
[folderPaths removeObjectsInArray: folders];
[folderPaths addObjectsFromArray: folders];
}
}
[folderPaths [folderPaths
sortUsingSelector: @selector (localizedCaseInsensitiveCompare:)]; sortUsingSelector: @selector (localizedCaseInsensitiveCompare:)];
[folderPaths replaceObjectsInRange: NSMakeRange (0, 0) [folderPaths replaceObjectsInRange: NSMakeRange (0, 0)
@ -417,14 +483,22 @@ static NSString *sieveScriptName = @"sogo";
acquire: (BOOL) _flag acquire: (BOOL) _flag
{ {
NSString *folderName; NSString *folderName;
NSMutableArray *namespaces;
Class klazz; Class klazz;
id obj; id obj;
[[[self imap4Connection] client] namespace];
if ([_key hasPrefix: @"folder"]) if ([_key hasPrefix: @"folder"])
{ {
folderName = [_key substringFromIndex: 6]; folderName = [[_key substringFromIndex: 6] fromCSSIdentifier];
if ([folderName
isEqualToString: [self sentFolderNameInContext: _ctx]]) namespaces = [NSMutableArray array];
[self _appendNamespaces: namespaces];
if ([namespaces containsObject: folderName])
klazz = [SOGoMailNamespace class];
else if ([folderName
isEqualToString: [self sentFolderNameInContext: _ctx]])
klazz = [SOGoSentFolder class]; klazz = [SOGoSentFolder class];
else if ([folderName else if ([folderName
isEqualToString: [self draftsFolderNameInContext: _ctx]]) isEqualToString: [self draftsFolderNameInContext: _ctx]])

View File

@ -44,6 +44,7 @@
#import <SOGo/DOMNode+SOGo.h> #import <SOGo/DOMNode+SOGo.h>
#import <SOGo/NSArray+Utilities.h> #import <SOGo/NSArray+Utilities.h>
#import <SOGo/NSString+Utilities.h>
#import <SOGo/NSString+DAV.h> #import <SOGo/NSString+DAV.h>
#import <SOGo/NSArray+DAV.h> #import <SOGo/NSArray+DAV.h>
#import <SOGo/NSObject+DAV.h> #import <SOGo/NSObject+DAV.h>
@ -59,6 +60,7 @@
#import "SOGoMailAccount.h" #import "SOGoMailAccount.h"
#import "SOGoMailManager.h" #import "SOGoMailManager.h"
#import "SOGoMailFolder.h" #import "SOGoMailFolder.h"
#import "SOGoTrashFolder.h"
#define XMLNS_INVERSEDAV @"urn:inverse:params:xml:ns:inverse-dav" #define XMLNS_INVERSEDAV @"urn:inverse:params:xml:ns:inverse-dav"
@ -113,7 +115,7 @@ static NSString *defaultUserID = @"anyone";
- (void) dealloc - (void) dealloc
{ {
[filenames release]; [filenames release];
[folderType release]; [folderType release];
[mailboxACL release]; [mailboxACL release];
[super dealloc]; [super dealloc];
@ -123,7 +125,7 @@ static NSString *defaultUserID = @"anyone";
- (NSString *) relativeImap4Name - (NSString *) relativeImap4Name
{ {
return [nameInContainer substringFromIndex: 6]; return [[nameInContainer substringFromIndex: 6] fromCSSIdentifier];
} }
- (NSString *) absoluteImap4Name - (NSString *) absoluteImap4Name
@ -151,7 +153,11 @@ static NSString *defaultUserID = @"anyone";
- (NSArray *) toManyRelationshipKeys - (NSArray *) toManyRelationshipKeys
{ {
return [self subfolders]; NSArray *subfolders;
subfolders = [[self subfolders] stringsWithFormat: @"folder%@"];
return [subfolders resultsOfSelector: @selector (asCSSIdentifier)];
} }
- (NSArray *) subfolders - (NSArray *) subfolders
@ -251,7 +257,6 @@ static NSString *defaultUserID = @"anyone";
id result; id result;
BOOL b; BOOL b;
trashFolder = [[self mailAccountFolder] trashFolderInContext: localContext]; trashFolder = [[self mailAccountFolder] trashFolderInContext: localContext];
if ([trashFolder isNotNull]) if ([trashFolder isNotNull])
{ {
@ -392,35 +397,35 @@ static NSString *defaultUserID = @"anyone";
toFolder: (NSString *) destinationFolder toFolder: (NSString *) destinationFolder
inContext: (id) localContext inContext: (id) localContext
{ {
NSEnumerator *folders; NSArray *folders;
NSString *currentFolderName; NSString *currentFolderName;
NSMutableString *imapDestinationFolder; NSMutableString *imapDestinationFolder;
NGImap4Client *client; NGImap4Client *client;
id result; id result;
int count, max;
#warning this code will fail on implementation using something else than '/' as delimiter
imapDestinationFolder = [NSMutableString string]; imapDestinationFolder = [NSMutableString string];
folders = [[destinationFolder componentsSeparatedByString: @"/"] objectEnumerator]; folders = [[destinationFolder componentsSeparatedByString: @"/"]
currentFolderName = [folders nextObject]; resultsOfSelector: @selector (fromCSSIdentifier)];
while (currentFolderName) max = [folders count];
{ for (count = 2; count < max; count++)
if ([currentFolderName hasPrefix: @"folder"])
{ {
[imapDestinationFolder appendString: @"/"]; currentFolderName
[imapDestinationFolder appendString: [currentFolderName substringFromIndex: 6]]; = [[folders objectAtIndex: count] substringFromIndex: 6];
[imapDestinationFolder appendFormat: @"/%@", currentFolderName];
} }
currentFolderName = [folders nextObject];
}
client = [[self imap4Connection] client]; client = [[self imap4Connection] client];
[imap4 selectFolder: [self imap4URL]]; [imap4 selectFolder: [self imap4URL]];
// We make sure the destination IMAP folder exist, if not, we create it. // We make sure the destination IMAP folder exist, if not, we create it.
result = [[client status: imapDestinationFolder flags: [NSArray arrayWithObject: @"UIDVALIDITY"]] result = [[client status: imapDestinationFolder
flags: [NSArray arrayWithObject: @"UIDVALIDITY"]]
objectForKey: @"result"]; objectForKey: @"result"];
if (![result boolValue]) if (![result boolValue])
result = [[self imap4Connection] createMailbox: imapDestinationFolder atURL: [[self mailAccountFolder] imap4URL]]; result = [[self imap4Connection] createMailbox: imapDestinationFolder
atURL: [[self mailAccountFolder] imap4URL]];
if (!result || [result boolValue]) if (!result || [result boolValue])
result = [client copyUids: uids toFolder: imapDestinationFolder]; result = [client copyUids: uids toFolder: imapDestinationFolder];
@ -442,11 +447,10 @@ static NSString *defaultUserID = @"anyone";
client = [[self imap4Connection] client]; client = [[self imap4Connection] client];
result = [self copyUIDs: uids toFolder: destinationFolder inContext: localContext]; result = [self copyUIDs: uids toFolder: destinationFolder inContext: localContext];
if (![result isNotNull])
if ( ![result isNotNull] )
{ {
result = [client storeFlags: [NSArray arrayWithObject: @"Deleted"] result = [client storeFlags: [NSArray arrayWithObject: @"Deleted"]
forUIDs: uids addOrRemove: YES]; forUIDs: uids addOrRemove: YES];
if ([[result valueForKey: @"result"] boolValue]) if ([[result valueForKey: @"result"] boolValue])
{ {
[self markForExpunge]; [self markForExpunge];
@ -555,51 +559,51 @@ static NSString *defaultUserID = @"anyone";
inContext: (id)_ctx inContext: (id)_ctx
acquire: (BOOL) _acquire acquire: (BOOL) _acquire
{ {
NSString *folderName, *className; NSString *folderName, *fullFolderName, *className;
SOGoMailAccount *mailAccount; SOGoMailAccount *mailAccount;
id obj; id obj;
if ([_key hasPrefix: @"folder"]) // We automatically create mailboxes that don't exist but that we're
// trying to open. This shouldn't happen unless a mailbox has been
// deleted "behind our back" or if we're trying to open a special
// mailbox that doesn't yet exist.
if ([[self imap4Connection] doesMailboxExistAtURL: [self imap4URL]]
|| ![[self imap4Connection] createMailbox: [self relativeImap4Name]
atURL: [[self mailAccountFolder] imap4URL]])
{ {
mailAccount = [self mailAccountFolder]; obj = [super lookupName: _key inContext: _ctx acquire: NO];
folderName = [NSString stringWithFormat: @"%@/%@", if (!obj)
[self traversalFromMailAccount], {
[_key substringFromIndex: 6]]; if ([_key hasPrefix: @"folder"])
if ([folderName {
isEqualToString: [mailAccount sentFolderNameInContext: _ctx]]) mailAccount = [self mailAccountFolder];
className = @"SOGoSentFolder"; folderName = [[_key substringFromIndex: 6] fromCSSIdentifier];
else if ([folderName isEqualToString: fullFolderName = [NSString stringWithFormat: @"%@/%@",
[mailAccount draftsFolderNameInContext: _ctx]]) [self traversalFromMailAccount], folderName];
className = @"SOGoDraftsFolder"; if ([fullFolderName
else if ([folderName isEqualToString: isEqualToString: [mailAccount sentFolderNameInContext: _ctx]])
[mailAccount trashFolderNameInContext: _ctx]]) className = @"SOGoSentFolder";
className = @"SOGoTrashFolder"; else if ([fullFolderName isEqualToString:
/* else if ([folderName isEqualToString: [mailAccount draftsFolderNameInContext: _ctx]])
[mailAccount sieveFolderNameInContext: _ctx]]) className = @"SOGoDraftsFolder";
obj = [self lookupFiltersFolder: _key inContext: _ctx]; */ else if ([fullFolderName isEqualToString:
else [mailAccount trashFolderNameInContext: _ctx]])
className = @"SOGoMailFolder"; className = @"SOGoTrashFolder";
/* else if ([folderName isEqualToString:
[mailAccount sieveFolderNameInContext: _ctx]])
obj = [self lookupFiltersFolder: _key inContext: _ctx]; */
else
className = @"SOGoMailFolder";
obj = [NSClassFromString (className) obj = [NSClassFromString (className) objectWithName: _key
objectWithName: _key inContainer: self]; inContainer: self];
}
else if (isdigit ([_key characterAtIndex: 0]))
obj = [SOGoMailObject objectWithName: _key inContainer: self];
}
} }
else else
{ obj = nil;
// We automatically create mailboxes that don't exist but that we're
// trying to open. This shouldn't happen unless a mailbox has been
// deleted "behind our back" or if we're trying to open a special
// mailbox that doesn't yet exist.
if ([[self imap4Connection] doesMailboxExistAtURL: [self imap4URL]] ||
![[self imap4Connection] createMailbox: [self relativeImap4Name] atURL: [[self mailAccountFolder] imap4URL]])
{
if (isdigit ([_key characterAtIndex: 0]))
obj = [SOGoMailObject objectWithName: _key inContainer: self];
else
obj = [super lookupName: _key inContext: _ctx acquire: NO];
}
else
obj = nil;
}
if (!obj && _acquire) if (!obj && _acquire)
obj = [NSException exceptionWithHTTPStatus: 404 /* Not Found */]; obj = [NSException exceptionWithHTTPStatus: 404 /* Not Found */];

View File

@ -0,0 +1,32 @@
/* SOGoMailNamespace.h - this file is part of SOGo
*
* Copyright (C) 2010 Wolfgang Sourdeau
*
* Author: Wolfgang Sourdeau <wsourdeau@inverse.ca>
*
* 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)
* any later version.
*
* This file 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; see the file COPYING. If not, write to
* the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
* Boston, MA 02111-1307, USA.
*/
#ifndef SOGOMAILNAMESPACE_H
#define SOGOMAILNAMESPACE_H
#import "SOGoMailFolder.h"
@interface SOGoMailNamespace : SOGoMailFolder
@end
#endif /* SOGOMAILNAMESPACE_H */

View File

@ -0,0 +1,32 @@
/* SOGoMailNamespace.m - this file is part of SOGo
*
* Copyright (C) 2010 Wolfgang Sourdeau
*
* Author: Wolfgang Sourdeau <wsourdeau@inverse.ca>
*
* 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)
* any later version.
*
* This file 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; see the file COPYING. If not, write to
* the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
* Boston, MA 02111-1307, USA.
*/
#import "SOGoMailNamespace.h"
@implementation SOGoMailNamespace
- (NSArray *) toOneRelationshipKeys
{
return nil;
}
@end

View File

@ -51,6 +51,8 @@
withObject: (id) object1 withObject: (id) object1
withObject: (id) object2; withObject: (id) object2;
#endif #endif
/* foreach ... */
- (NSArray *) resultsOfSelector: (SEL) operation;
@end @end

View File

@ -36,7 +36,7 @@
unsigned int max; unsigned int max;
max = [self count]; max = [self count];
pointers = NSZoneMalloc (NULL, sizeof(id) * (max + 1)); pointers = NSZoneMalloc (NULL, sizeof (id) * (max + 1));
[self getObjects: pointers]; [self getObjects: pointers];
*(pointers + max) = nil; *(pointers + max) = nil;
@ -154,6 +154,12 @@
return newArray; return newArray;
} }
- (NSArray *) trimmedComponents
{
return [self resultsOfSelector: @selector (stringByTrimmingSpaces)];
}
#ifdef GNUSTEP_BASE_LIBRARY
- (void) makeObjectsPerform: (SEL) selector - (void) makeObjectsPerform: (SEL) selector
withObject: (id) object1 withObject: (id) object1
withObject: (id) object2 withObject: (id) object2
@ -166,6 +172,24 @@
withObject: object1 withObject: object1
withObject: object2]; withObject: object2];
} }
#endif
- (NSArray *) resultsOfSelector: (SEL) operation
{
NSMutableArray *results;
int count, max;
id result;
max = [self count];
results = [NSMutableArray arrayWithCapacity: max];
for (count = 0; count < max; count++)
{
result = [[self objectAtIndex: count] performSelector: operation];
[results addObject: result];
}
return results;
}
- (NSString *) jsonRepresentation - (NSString *) jsonRepresentation
{ {
@ -204,23 +228,6 @@
return response; return response;
} }
- (NSArray *) trimmedComponents
{
NSMutableArray *newComponents;
NSString *currentString;
unsigned int count, max;
max = [self count];
newComponents = [NSMutableArray arrayWithCapacity: max];
for (count = 0; count < max; count++)
{
currentString = [[self objectAtIndex: count] stringByTrimmingSpaces];
[newComponents addObject: currentString];
}
return newComponents;
}
@end @end
@implementation NSMutableArray (SOGoArrayUtilities) @implementation NSMutableArray (SOGoArrayUtilities)

View File

@ -47,7 +47,7 @@
- (NSString *) jsonRepresentation; - (NSString *) jsonRepresentation;
- (NSString *) asCSSIdentifier; - (NSString *) asCSSIdentifier;
- (NSString *) fromCSSIdentifier;
/* bare email addresses */ /* bare email addresses */
- (NSString *) pureEMailAddress; - (NSString *) pureEMailAddress;

View File

@ -27,6 +27,7 @@
#import <EOControl/EOQualifier.h> #import <EOControl/EOQualifier.h>
#import <NGExtensions/NSDictionary+misc.h>
#import <NGExtensions/NGQuotedPrintableCoding.h> #import <NGExtensions/NGQuotedPrintableCoding.h>
#import "NSArray+Utilities.h" #import "NSArray+Utilities.h"
@ -39,6 +40,10 @@ static NSMutableCharacterSet *urlNonEndingChars = nil;
static NSMutableCharacterSet *urlAfterEndingChars = nil; static NSMutableCharacterSet *urlAfterEndingChars = nil;
static NSMutableCharacterSet *urlStartChars = nil; static NSMutableCharacterSet *urlStartChars = nil;
static NSString **cssEscapingStrings = NULL;
static unichar *cssEscapingCharacters = NULL;
static int cssEscapingCount = 0;
@implementation NSString (SOGoURLExtension) @implementation NSString (SOGoURLExtension)
- (NSString *) composeURLWithAction: (NSString *) action - (NSString *) composeURLWithAction: (NSString *) action
@ -305,23 +310,109 @@ static NSMutableCharacterSet *urlStartChars = nil;
return [self doubleQuotedString]; return [self doubleQuotedString];
} }
- (void) _setupCSSEscaping
{
NSArray *strings, *characters;
int count;
strings = [NSArray arrayWithObjects: @"_U_", @"_D_", @"_H_", @"_A_", @"_S_",
@"_C_", @"_CO_", @"_SP_", nil];
cssEscapingStrings = [strings asPointersOfObjects];
characters = [NSArray arrayWithObjects: @"_", @".", @"#", @"@", @"*", @":",
@",", @" ", nil];
cssEscapingCharacters
= NSZoneMalloc (NULL, sizeof ((cssEscapingCount + 1) * sizeof (unichar)));
cssEscapingCount = [strings count];
for (count = 0; count < cssEscapingCount; count++)
*(cssEscapingCharacters + count)
= [[characters objectAtIndex: count] characterAtIndex: 0];
*(cssEscapingCharacters + cssEscapingCount) = 0;
}
- (int) _cssCharacterIndex: (unichar) character
{
int idx, count;
idx = -1;
for (count = 0; idx == -1 && count < cssEscapingCount; count++)
if (*(cssEscapingCharacters + count) == character)
idx = count;
return idx;
}
- (NSString *) asCSSIdentifier - (NSString *) asCSSIdentifier
{ {
NSMutableString *cssIdentifier; NSMutableString *cssIdentifier;
unichar currentChar;
int count, max, idx;
cssIdentifier = [NSMutableString stringWithString: self]; if (!cssEscapingStrings)
[cssIdentifier replaceString: @"_" withString: @"_U_"]; [self _setupCSSEscaping];
[cssIdentifier replaceString: @"." withString: @"_D_"];
[cssIdentifier replaceString: @"#" withString: @"_H_"]; cssIdentifier = [NSMutableString string];
[cssIdentifier replaceString: @"@" withString: @"_A_"]; max = [self length];
[cssIdentifier replaceString: @"*" withString: @"_S_"]; for (count = 0; count < max; count++)
[cssIdentifier replaceString: @":" withString: @"_C_"]; {
[cssIdentifier replaceString: @"," withString: @"_CO_"]; currentChar = [self characterAtIndex: count];
[cssIdentifier replaceString: @" " withString: @"_SP_"]; idx = [self _cssCharacterIndex: currentChar];
if (idx > -1)
[cssIdentifier appendString: cssEscapingStrings[idx]];
else
[cssIdentifier appendFormat: @"%lc", currentChar];
}
return cssIdentifier; return cssIdentifier;
} }
- (int) _cssStringIndex: (NSString *) string
{
int idx, count;
idx = -1;
for (count = 0; idx == -1 && count < cssEscapingCount; count++)
if ([string hasPrefix: *(cssEscapingStrings + count)])
idx = count;
return idx;
}
- (NSString *) fromCSSIdentifier
{
NSMutableString *newString;
NSString *currentString;
int count, length, max, idx;
if (!cssEscapingStrings)
[self _setupCSSEscaping];
newString = [NSMutableString string];
max = [self length];
for (count = 0; count < max - 2; count++)
{
/* The difficulty here is that most escaping strings are 3 chars long
except one. Therefore we must juggle a little bit with the lengths in
order to avoid an overflow exception. */
length = 4;
if (count + length > max)
length = max - count;
currentString = [self substringFromRange: NSMakeRange (count, length)];
idx = [self _cssStringIndex: currentString];
if (idx > -1)
{
[newString appendFormat: @"%lc", cssEscapingCharacters[idx]];
count += [cssEscapingStrings[idx] length] - 1;
}
else
[newString appendFormat: @"%lc", [self characterAtIndex: count]];
}
currentString = [self substringFromRange: NSMakeRange (count, max - count)];
[newString appendString: currentString];
return newString;
}
- (NSString *) pureEMailAddress - (NSString *) pureEMailAddress
{ {
NSString *pureAddress; NSString *pureAddress;

View File

@ -59,21 +59,14 @@ String.prototype.asDate = function () {
return newDate; return newDate;
}; };
String.prototype.asCSSIdentifier = function () { String.prototype.asCSSIdentifier = function() {
var substitutions = { '_': '_U_', var characters = [ '_' , '\\.', '#' , '@' , '\\*', ':' , ',' , ' ' ];
'\\.': '_D_', var escapeds = [ '_U_', '_D_', '_H_', '_A_', '_S_', '_C_', '_CO_', '_SP_' ];
'#': '_H_',
'@': '_A_',
'\\*': '_S_',
':': '_C_',
',': '_CO_',
' ': '_SP_' };
var newString = this;
var re;
for (var key in substitutions) { var newString = this;
re = new RegExp(key, 'g'); for (var i = 0; i < characters.length; i++) {
newString = newString.replace(re, substitutions[key]); var re = new RegExp(characters[i], 'g');
newString = newString.replace(re, escapeds[i]);
} }
return newString; return newString;

View File

@ -42,7 +42,7 @@ var messageCheckTimer;
/* We need to override this method since it is adapted to GCS-based folder /* We need to override this method since it is adapted to GCS-based folder
references, which we do not use here */ references, which we do not use here */
function URLForFolderID(folderID) { function URLForFolderID(folderID) {
var url = ApplicationBaseURL + encodeURI(folderID); var url = ApplicationBaseURL + encodeURI(folderID.substr(1));
if (url[url.length-1] == '/') if (url[url.length-1] == '/')
url = url.substr(0, url.length-1); url = url.substr(0, url.length-1);
@ -296,7 +296,6 @@ function ml_lowlight(sender) {
sender.className = "tableview"; sender.className = "tableview";
} }
function onUnload(event) { function onUnload(event) {
var url = ApplicationBaseURL + encodeURI(Mailer.currentMailbox) + "/expunge"; var url = ApplicationBaseURL + encodeURI(Mailer.currentMailbox) + "/expunge";
@ -533,17 +532,17 @@ function onMailboxMenuMove(event) {
} }
function onMailboxMenuCopy(event) { function onMailboxMenuCopy(event) {
var targetMailbox;
var messageList = $("messageList").down("TBODY"); var messageList = $("messageList").down("TBODY");
var rows = messageList.getSelectedNodes(); var rows = messageList.getSelectedNodes();
var uids = new Array(); // message IDs var uids = new Array(); // message IDs
var paths = new Array(); // row IDs var paths = new Array(); // row IDs
var targetMailbox;
if (this.tagName == 'LI') // from contextual menu if (this.tagName == 'LI') // from contextual menu
targetMailbox = this.mailbox.fullName(); targetMailbox = this.mailbox.fullName();
else // from DnD else // from DnD
targetMailbox = this.readAttribute("dataname"); targetMailbox = this.readAttribute("dataname");
for (var i = 0; i < rows.length; i++) { for (var i = 0; i < rows.length; i++) {
var uid = rows[i].readAttribute("id").substr(4); var uid = rows[i].readAttribute("id").substr(4);
var path = Mailer.currentMailbox + "/" + uid; var path = Mailer.currentMailbox + "/" + uid;
@ -1748,7 +1747,7 @@ function updateMailboxTreeInPage() {
} }
} }
function mailboxMenuNode(type, name) { function mailboxMenuNode(type, displayName) {
var newNode = document.createElement("li"); var newNode = document.createElement("li");
var icon = MailerUIdTreeExtension.folderIcons[type]; var icon = MailerUIdTreeExtension.folderIcons[type];
if (!icon) if (!icon)
@ -1756,9 +1755,9 @@ function mailboxMenuNode(type, name) {
var image = document.createElement("img"); var image = document.createElement("img");
image.src = ResourcesURL + "/" + icon; image.src = ResourcesURL + "/" + icon;
newNode.appendChild(image); newNode.appendChild(image);
var displayName = MailerUIdTreeExtension.folderNames[type]; var dnOverride = MailerUIdTreeExtension.folderNames[type];
if (!displayName) if (dnOverride)
displayName = name; displayName = dnOverride;
newNode.appendChild(document.createTextNode(" " + displayName)); newNode.appendChild(document.createTextNode(" " + displayName));
return newNode; return newNode;
@ -1804,7 +1803,7 @@ function generateMenuForMailbox(mailbox, prefix, callback) {
var submenuCount = 0; var submenuCount = 0;
var newNode; var newNode;
for (var i = 0; i < mailbox.children.length; i++) { for (var i = 0; i < mailbox.children.length; i++) {
if ( menu.offsetHeight > windowHeight-offset ) { if (menu.offsetHeight > windowHeight-offset) {
var menuWidth = parseInt(menu.offsetWidth) + 15 var menuWidth = parseInt(menu.offsetWidth) + 15
menuWidth = menuWidth + "px"; menuWidth = menuWidth + "px";
menu.style.width = menuWidth; menu.style.width = menuWidth;
@ -1814,7 +1813,7 @@ function generateMenuForMailbox(mailbox, prefix, callback) {
menuDIV.appendChild(menu); menuDIV.appendChild(menu);
} }
var child = mailbox.children[i]; var child = mailbox.children[i];
newNode = mailboxMenuNode(child.type, child.name); newNode = mailboxMenuNode(child.type, child.displayName);
newNode.style.width = "auto"; newNode.style.width = "auto";
menu.appendChild(newNode); menu.appendChild(newNode);
if (child.children.length > 0) { if (child.children.length > 0) {
@ -1831,8 +1830,7 @@ function generateMenuForMailbox(mailbox, prefix, callback) {
var menuWidth = parseInt(menu.offsetWidth) + 15 var menuWidth = parseInt(menu.offsetWidth) + 15
menuWidth = menuWidth + "px"; menuWidth = menuWidth + "px";
menu.style.width = menuWidth; menu.style.width = menuWidth;
initMenu(menuDIV, callbacks); initMenu(menuDIV, callbacks);
return menuDIV.getAttribute("id"); return menuDIV.getAttribute("id");
@ -1912,7 +1910,7 @@ function onLoadMailboxesCallback(http) {
} }
function buildMailboxes(accountKeys, encoded) { function buildMailboxes(accountKeys, encoded) {
var account = new Mailbox("account", accountKeys[0], var account = new Mailbox("account", accountKeys[1],
undefined, //necessary, null will cause issues undefined, //necessary, null will cause issues
accountKeys[1]); accountKeys[1]);
var data = encoded.evalJSON(true); var data = encoded.evalJSON(true);
@ -1926,9 +1924,10 @@ function buildMailboxes(accountKeys, encoded) {
var currentNode = account; var currentNode = account;
var names = mailboxes[i].path.split("/"); var names = mailboxes[i].path.split("/");
for (var j = 1; j < (names.length - 1); j++) { for (var j = 1; j < (names.length - 1); j++) {
var node = currentNode.findMailboxByName(names[j]); var name = names[j];
var node = currentNode.findMailboxByName(name);
if (!node) { if (!node) {
node = new Mailbox("additional", names[j]); node = new Mailbox("additional", name);
currentNode.addMailbox(node); currentNode.addMailbox(node);
} }
currentNode = node; currentNode = node;
@ -2327,11 +2326,12 @@ document.observe("dom:loaded", initMailer);
function Mailbox(type, name, unseen, displayName) { function Mailbox(type, name, unseen, displayName) {
this.type = type; this.type = type;
this.name = name;
if (displayName) if (displayName)
this.displayName = displayName; this.displayName = displayName;
else else
this.displayName = name; this.displayName = name;
// log("name: " + name + "; dn: " + displayName);
this.name = name.asCSSIdentifier();
this.unseen = unseen; this.unseen = unseen;
this.parentFolder = null; this.parentFolder = null;
this.children = new Array(); this.children = new Array();
@ -2361,9 +2361,11 @@ Mailbox.prototype = {
findMailboxByName: function(name) { findMailboxByName: function(name) {
var mailbox = null; var mailbox = null;
var searchName = name.asCSSIdentifier();
var i = 0; var i = 0;
while (!mailbox && i < this.children.length) while (!mailbox && i < this.children.length)
if (this.children[i].name == name if (this.children[i].name == searchName
|| this.children[i].displayName == name) || this.children[i].displayName == name)
mailbox = this.children[i]; mailbox = this.children[i];
else else

View File

@ -26,7 +26,7 @@ var MailerUIdTreeExtension = {
displayName += "<span id=\"unseenCount\"> (<span>" + parseInt(unseen) + "</span>)</span>"; displayName += "<span id=\"unseenCount\"> (<span>" + parseInt(unseen) + "</span>)</span>";
} }
this.add(this.elementCounter, parent, displayName, 1, '#', fullName, this.add(this.elementCounter, parent, displayName, 1, '#', fullName,
type, '', '', icon, icon, hasUnseen); type, '', '', icon, icon, hasUnseen);
this.elementCounter++; this.elementCounter++;
}, },
_addFolder: function (parent, folder) { _addFolder: function (parent, folder) {