diff --git a/ChangeLog b/ChangeLog index 223e5b6e0..3be02f72c 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,26 +1,78 @@ 2010-08-12 Wolfgang Sourdeau - * UI/WebServerResources/generic.js: (onAjaxRequestStateChange): - when using CAS authentication and when the return code is 0 and - when the request status is 4, chances are that the initial ajax - request failed due to the expiration of the CAS ticket entry in - memcached. In FF 3.5 and above, a bug prevents the initial cookie - from being given back to the reauthentication redirect from an - AJAX request. Therefore we open a window that will do this for us - and will close one the authentication has succeeded again. + * UI/WebServerResources/MailerUIdTree.js: (getMailboxNode): fixed + method to return unfolded nodes too. - * UI/MainUI/SOGoRootPage.m (-revoverAction): new fake action that - perform the CAS redirection and then trigger the respawn of the - initial request. + * UI/WebServerResources/MailerUI.js: (updateUnseenCount): renamed + from "updateStatusFolders" and improved to use DOM methods. - * UI/Common/UIxPageFrame.m (-usesCASAuthentication): new accessor - used for recovery of redirected requests. + * UI/WebServerResources/dtree.js: (Node): removed the "hasUnseen" + parameter. -2010-08-11 Francis Lachapelle + * UI/MailerUI/UIxMailAccountActions.m (-listMailboxes): make use + of the "application/json" content type in the response. - * UI/WebServerResources/MailerUI.js (refreshMessage): if the - current mailbox is the sent folder, refresh the mailbox instead of - only the message. + * UI/MailerUI/UIxMailFolderActions.m: (-unseenCount): renamed and + moved implementation of the -[UIxMailAccountActions statusFolders] + method, splitted thereof since we now execute the method on all + mail folders. + + * UI/MailerUI/UIxMailMainFrame.m (-defaultColumnsOrder): removed + accessor, made obsolete by the new propagation mechanism of user + defaults. + (-getUnseenCountForAllFolders): new accessor that returns the + value below as a string value. + + * SoObjects/SOGo/SOGoDomainDefaults.m (-mailCheckAllUnseenCounts): + new method returning whether the unseen count of all mailboxes + should be checked. + + * SoObjects/Mailer/SOGoMailAccount.m (-updateFilters): when the + first login attempt fails, we must request a new password to the + authenticator (as intended originally). + + * UI/WebServerResources/generic.js: (onAjaxRequestStateChange): + when using CAS authentication and when the return code is 0 and + when the request status is 4, chances are that the initial ajax + request failed due to the expiration of the CAS ticket entry in + memcached. In FF 3.5 and above, a bug prevents the initial cookie + from being given back to the reauthentication redirect from an + AJAX request. Therefore we open a window that will do this for us + and will close one the authentication has succeeded again. + + * UI/MainUI/SOGoRootPage.m (-revoverAction): new fake action that + perform the CAS redirection and then trigger the respawn of the + initial request. + + * UI/Common/UIxPageFrame.m (-usesCASAuthentication): new accessor + used for recovery of redirected requests. + + * SoObjects/Contacts/SOGoFolder+CardDAV.m: + (_appendObject:withBaseURL:toREPORTResponse:): moved method here + from SOGOContact{GCS,Source}Folder since its implementation was + mostly the same in the two classes and never invoked anywhere + else. + Modified to return both "address-data" and "addressbook-data" + (draft rev < 4) unless we parse the requested props appropriately. + + * SoObjects/Contacts/SOGoContactGCSFolder.m + (-davAddressbookMultiget): new method that responds the + CardDAV addressbook-multiget report. + (-davSQLFieldsTable): new overriden method that adds support for + the CardDAV address-data property. + + * SoObjects/Appointments/SOGoAppointmentFolder.m + (-davCalendarMultiget:): make use of the new method below. + + * SoObjects/SOGo/SOGoGCSFolder.m + (-performMultigetInContext:inNamespace:): collection-type + independent form of -[SOGoAppointmentFolder davCalendarMultiget:], + moved along with all its private methods. + + * SoObjects/Contacts/SOGoContactFolders.m + (-toManyRelationshipKeys): new overriden method to return only the + personal addressbook when the request is performed by + AddressBook.app. 2010-08-11 Ludovic Marcotte @@ -41,6 +93,274 @@ messages and also when opening mailboxes. This fixes #716. +2010-08-11 Francis Lachapelle + + * UI/WebServerResources/MailerUI.js (refreshMessage): if the + current mailbox is the sent folder, refresh the mailbox instead of + only the message. + +2010-08-11 Wolfgang Sourdeau + + * UI/WebServerResources/SchedulerUI.js: (initCalendars): avoid + setting "showCompletedTasks" when in a popup, since this needs + access to a UserDefaults dictionary which is not present nor event + required. + + * Main/SOGo+DAV.m (-davCurrentUserPrincipal): new method + implementing the "{DAV:}current-user-principal" property. + + * SoObjects/Contacts/SOGoContactGCSEntry.m (-davAddressData): new + DAV property accessor returning the card content. + + * SoObjects/SOGo/WORequest+SOGo.m (-isAddressBookApp): new + (hackish) test method to determine whether the client is Apple's + AddressBook.app. + + * UI/Common/UIxPageFrame.m (_dictionaryWithKeys:fromSource:): + set an NSNull as value for keys which return no results, in order + to avoid an NSInvalidArgumentException. + +2010-08-10 Francis Lachapelle + + * UI/WebServerResources/generic.js (showAlertDialog): new function + to replace window.alert(). + +2010-08-09 Wolfgang Sourdeau + + * UI/WebServerResources/MailerUI.js (openMailbox): removed the + "updateStatus" parameter, which is never used. + + * UI/WebServerResources/generic.js (log): fixed handling of + messages ending with "\n". + + * UI/Common/UIxPageFrame.m (-setUserDefaultsKeys:) + (-hasUserDefaultsKeys, -setUserSettingsKeys:) + (-hasUserSettingsKeys): new accessor for determining explicitly + which user defaults/settings keys are returned. The new + corresponding ivars are "udKeys" and "usKeys" respectively. + (-userDefaults, -userSettings): rewrote methods to create a + dictionary containing only the requested keys (see above). Since + the source are now explicitly queried, the values can now be + inherited from the domain and system defaults. + + * UI/WebServerResources/UIxFilterEditor.js (loadMailboxes): the + first account is now always identitied with "0", therefore we no + longer need the "firstMailAccount" variable. + + * UI/PreferencesUI/UIxFilterEditor.m (-firstMailAccount): removed + osbolete accessor. + + * UI/WebServerResources/UIxPreferences.js: (onMailAccountAdd) + (onMailAccountDelete): set "hasChanged" to 1 when triggered. + (onColorPickerChoice): don't invoke "onChoiceChanged" as it is an + event callback. + +2010-08-06 Wolfgang Sourdeau + + * UI/WebServerResources/generic.js (reloadPreferences): dropped + useless method. + + * UI/PreferencesUI/UIxPreferences.m (_extractAuxiliaryAccounts:): + since we no longer transfer the user password to the client, the + old password has to be fetched from the old account unless a new + password has been set. + (-mailAccounts): we now strip the passwords from the returned mail + accounts, to avoid transferring them uselessly with a risk of + keeping them in cache. + + * UI/WebServerResources/MailerUI.js: (initMailboxTree): make use + of the new class below and drop all code related to keeping track + of active requests. + + * UI/WebServerResources/generic.js: (AjaxRequestsChain): new class + that implements the chaining of ajax requests. + + * UI/WebServerResources/MailerUI.js: (dropAction): ensure that the + destination mailbox is in the same mail account as the message + being dragged. + (getMenus): arranged menu descriptions as as single statement. + Added "prepareVisibility" callbacks for the "messageListMenu", + "messagesListMenu" and "messageContentMenu" (move/copy ops) and + "inboxIconMenu", "trashIconMenu", "mailboxIconMenu" menus (folder + sharing). + (onMessageListMenuPrepareVisibility): new vis. callback designed + to attach the right folder submenu to the copy and move ops. + (onFolderMenuPrepareVisibility): new vis. cb for disabling the + share of folders for auxiliary accounts. + (updateMailboxMenus): simplified method by removing the first + level of submenus, where the account names used to be. + (openInbox): "openMailbox" only takes 3 parameters. Using "null" + instead of "false" was also pretty lame. + (updateStatusFolders): take the mailbox name as parameter and + arranged for multiple mail accounts. In particular, "unseenCount" + is now a class identifier rather than an element id. + (markMailInWindow): make use of + MailerUIdTreeExtension.getMailboxNode. + + * UI/WebServerResources/ContactsUI.js + (onToolbarDeleteSelectedContacts): use translated labels. + + * UI/WebServerResources/MailerUIdTree.js: + (MailerUIdTreeExtension._addFolderNode): no longer set a SPAN + arroung the unseen count. + (MailerUIdTreeExtension.getMailboxNode): new method returning the + DIV.dTreeNode corresponding to the mailbox name passed as + parameter. + + * UI/WebServerResources/UIxPreferences.js: (_setupEvents): no + longer take any parameter, which simplifies the method, as + removing the "change" observers on input field is not useful. + (initMailAccounts): new method that parses the mail accounts JSON + dictionary, populates the mail account UL and setup related + events. + (onMailAccountInfoChange, onMailIdentityInfoChange) + (onMailIdentitySignatureClick, onMailIdentitySignatureOK) + (createMailAccountLI, onMailAccountEntryClick, displayMailAccount) + (displayAccountSignature, createMailAccount) + (onMailAccountAdd, onMailAccountDelete, saveMailAccounts) + (compactMailAccounts): new methods completing the above. + + * UI/PreferencesUI/UIxPreferences.m (-identitiesList) + (-itemIdentityText, -signature, -setSignature): removed obsolete + methods. + (-defaultAction): when updating filters, we now only need to query + the "0" account since accounts are no longer identified by "name". + (-mailAuxiliaryUserAccountsEnabled): new bool method. + (-setMailAccounts): new accessor that decodes the new mail + accounts dictionary in JSON format and validates it prior to save + it in the user defaults. + (-mailAccounts): new accessor. + + * UI/MailerUI/UIxMailFolderActions.m (_setFolderPurpose:): we now + use the corresponding methods on the SOGoUserDefaults instance + rather than in the user settings (old bug) when setting folders + for the main account. Additionally, we now handle the entries from + auxiliary accounts. + + * UI/MailerUI/UIxMailEditor.m (-fromEmails): the list of + identities is now taken from the corresponding mail account dict. + + * SoObjects/Mailer/SOGoMailForward.m (-signature): same as below. + + * UI/MailerUI/UIxMailAccountActions.m (-composeAction): same as below. + + * SoObjects/Mailer/SOGoDraftObject.m (-fetchMailForForwarding): + the signature is now taken from the original mail's corresponding + account. + + * SoObjects/Mailer/SOGoMailFolder.m + (-copyUIDs:toFolder:inContext:): we now ensure that the operation + is not attempted on two separate accounts. + (-httpURLForAdvisoryToUser): the returned mail account is now + always "0". + + * SoObjects/Mailer/SOGoMailBaseObject.m (-imap4URL): we now add + the "tls=YES" url parameter if the corresponding account + encryption is set to "tls". + (-imap4Login): removed useless method. + (-imap4PasswordRenewed:): now a proxy to the same method on the + instance account folder. + + * SoObjects/Mailer/SOGoMailAccount.m (-identities, -signature) + (encryption): new helper methods that return the corresponding key + in the appropriate mail account dictionary. + (-_migrateFolderWithPurpose:): moved method in SOGoMailAccount.m. + (-_userFolderNameWithPurpose): rewrote method to make use of the + mail account dictionaries. Fallback on the main account of the + auxiliary ones do not have the required entry. + (-imap4PasswordRenewed:): moved method from SOGoMailBaseObject.m + and use the "password" entry in auxiliary account dicts depending + on "nameInContainer". + (-imap4LoginFromHTTP, -imap4Login): removed useless methods. + + * SoObjects/SOGo/SOGoUser.m (-mailAccounts): we now conditionnally + append the array of auxiliary user accounts, if enabled in the + domain defaults. + (-_appendSystemMailAccount): the user main signature and the + sent, drafts, trash folder location are now stored in the account + dictionaries. Added migration code from us to ud from + SOGoMailAccount.m. + + * SoObjects/SOGo/SOGoDomainDefaults.m + (-mailAuxiliaryUserAccountsEnabled): new method returning a BOOL + describing whether the domain users can access auxiliary mail + accounts. + + * SoObjects/SOGo/SOGoUserDefaults.m (-setAuxiliaryMailAccounts:) + (auxiliaryMailAccounts): new methods that receives and returns an + NSArray of NSDictionary describing the user's auxiliary mail + accounts. + + * UI/WebServerResources/HTMLElement.js: (deselectAll): if the + container has a "selectedElements" attribute, then it's no longer + required to wander through the "selectedIds" attribute. This fixes + a strange bug on IE when modifying the selection on LI lists. + +2010-08-05 Francis Lachapelle + + * UI/WebServerResources/SOGoResizableTable.js + (computeColumnsWidths): was previous part of saveColumnsState. + Used when the user hasn't modify the columns widths but the local + "ratios" hash still need to be built. + +2010-08-04 Francis Lachapelle + + * UI/WebServerResources/generic.js (createDialog): when the + position class is set to "none", the dialog will be modal (with a + dimmed background). + + * UI/WebServerResources/ContactsUI.js: replaced window.confirm by + a CSS modal dialog. + +2010-08-04 Wolfgang Sourdeau + + * UI/WebServerResources/RowEditionController.js: + (RowEditionController.startEditing): avoid setting the whole + element class. Make use of addClassName/removeClassName instead, + so that classes added/removed via other evt handlers are not + ignored. + +2010-08-03 Wolfgang Sourdeau + + * SoObjects/SOGo/SOGoUser.m (-_appendSystemMailAccount): new + private method with the mailbox code split from -[mailAccounts]. + + * UI/WebServerResources/MailerUI.js (composeNewMessage): the first + account is always identified as "0". + + * UI/MailerUI/UIxMailMainFrame.m (-mailAccounts): we now return + tje JSON rep. of a simple and flat array containing the account + display names, since the account are represented with an integer + index. + (-inboxData): we directly fetch the first mail account by using + the "0" key. + (-composeAction): same as above. + + * SoObjects/Mailer/SOGoMailAccount.m (-imap4URLString): the + "nameInContainer" now represents the index of the current account + in the array of user mail accounts, which simplifies this method. + We also handle the "encryption" key, albeit currently unused, and + we now add the port to the url as needed. + (-shortTitle): removed method. + (-davDisplayName): we only need to return the "name" key of the + current account. + + * SoObjects/Mailer/SOGoMailAccounts.m (-mailAccounts): new utility + method that returns the mail account dictionaries corresponding to + the owner. + (-toManyRelationshipKeys): we now identify mail accounts via their + index in the array of accounts, since the order will never vary, + this simplifies the code. + (-lookupName:inContext:acquire:): same as the above. + + * UI/WebServerResources/RowEditionController.js: + RowEditionController.startEditing: keep the "_selected" class + when starting edition, if exists. + RowEditionController.acceptEdition: revert the changes if the new + value is an empty string (might be wrong, though). + RowEditionController.onInputKeyDown: we now handle the tab key + too. + 2010-08-03 Ludovic Marcotte * Applied patch for bug #690 @@ -49,6 +369,30 @@ * Updated Ukrainian translation - patch from Oleksa Stasevych +2010-08-02 Wolfgang Sourdeau + + * UI/WebServerResources/RowEditionController.js: new class module + that implements the handling of editable list items. + + * SoObjects/SOGo/NSNumber+Utilities.m (-jsonRepresentation): + returns "true" or "false" when the initial return value is "YES" + or "NO". + +2010-07-30 Francis Lachapelle + + * UI/WebServerResources/SchedulerUI.js (deleteEvent): improved + the function to delete all selected events cells. + (onSelectAll): improved the function to select all events cells. + (onDocumentKeydown): select all elements upon ctrl-A. + (onCalendarSelectEvent): allow multiple selections. + + * UI/WebServerResources/HTMLElement.js (selectAll): new method to + select all entries of a list or a table. + + * UI/WebServerResources/scriptaculous/dragdrop.js (initDrag): + avoid stopping the event propagation so the blur event is properly + fired on the search input field when it isfocused. + 2010-07-28 Francis Lachapelle * UI/WebServerResources/MailerUI.js (deleteCachedMailboxByType, diff --git a/Documentation/SOGo Installation Guide.odt b/Documentation/SOGo Installation Guide.odt index 0271fffd7..3b580f4f2 100644 Binary files a/Documentation/SOGo Installation Guide.odt and b/Documentation/SOGo Installation Guide.odt differ diff --git a/Main/SOGo+DAV.m b/Main/SOGo+DAV.m index 6369e6855..131f70dec 100644 --- a/Main/SOGo+DAV.m +++ b/Main/SOGo+DAV.m @@ -504,4 +504,30 @@ return r; } +- (SOGoWebDAVValue *) davCurrentUserPrincipal +{ + NSDictionary *userHREF; + NSString *usersUrl, *login; + SOGoUser *activeUser; + SOGoWebDAVValue *davCurrentUserPrincipal; + + activeUser = [[self context] activeUser]; + login = [activeUser login]; + if ([login isEqualToString: @"anonymous"]) + davCurrentUserPrincipal = nil; + else + { + usersUrl = [NSString stringWithFormat: @"%@%@/", + [self davURLAsString], login]; + userHREF = davElementWithContent (@"href", XMLNS_WEBDAV, usersUrl); + davCurrentUserPrincipal + = [davElementWithContent (@"current-user-principal", + XMLNS_WEBDAV, + userHREF) + asWebDAVValue]; + } + + return davCurrentUserPrincipal; +} + @end diff --git a/SOPE/NGCards/ChangeLog b/SOPE/NGCards/ChangeLog index e03b9a050..91fb55d8a 100644 --- a/SOPE/NGCards/ChangeLog +++ b/SOPE/NGCards/ChangeLog @@ -1,3 +1,8 @@ +2010-08-11 Wolfgang Sourdeau + + * NGVCardPhoto.m (-type): returns @"JPEG" if the type is + unspecified. + 2010-07-21 Wolfgang Sourdeau * iCalXMLRenderer.m (_appendPaddingValues:withTag:intoString:): diff --git a/SOPE/NGCards/NGVCardPhoto.m b/SOPE/NGCards/NGVCardPhoto.m index ce00238ec..17cf02040 100644 --- a/SOPE/NGCards/NGVCardPhoto.m +++ b/SOPE/NGCards/NGVCardPhoto.m @@ -37,7 +37,13 @@ - (NSString *) type { - return [[self value: 0 ofAttribute: @"type"] uppercaseString]; + NSString *type; + + type = [[self value: 0 ofAttribute: @"type"] uppercaseString]; + if (!type) + type = @"JPEG"; + + return type; } - (NSData *) decodedContent diff --git a/SOPE/NGCards/versitCardsSaxDriver/ChangeLog b/SOPE/NGCards/versitCardsSaxDriver/ChangeLog index 5afb1fde4..fe4545dcd 100644 --- a/SOPE/NGCards/versitCardsSaxDriver/ChangeLog +++ b/SOPE/NGCards/versitCardsSaxDriver/ChangeLog @@ -1,3 +1,9 @@ +2010-08-11 Wolfgang Sourdeau + + * VSSaxDriver.m (_parseAttr:forTag:intoAttr:intoValue:): when no + attribute tag is not specified and the value is "BASE64" or "B", the + attribute tag is set to "ENCODING". + 2010-05-05 Wolfgang Sourdeau * VSSaxDriver.m (_endComponent:value:): avoid a crash occurring @@ -38,7 +44,7 @@ * VSSaxDriver.m: improved error reporting in case no data could be retrieved from a URL (v4.5.22) - + 2005-12-05 Helge Hess * v4.5.21 diff --git a/SOPE/NGCards/versitCardsSaxDriver/VSSaxDriver.m b/SOPE/NGCards/versitCardsSaxDriver/VSSaxDriver.m index 4b6763941..3686c9c54 100644 --- a/SOPE/NGCards/versitCardsSaxDriver/VSSaxDriver.m +++ b/SOPE/NGCards/versitCardsSaxDriver/VSSaxDriver.m @@ -325,7 +325,7 @@ static NSCharacterSet *whitespaceCharSet = nil; intoValue: (NSString **) value_ { NSRange r; - NSString *attrName, *attrValue; + NSString *attrName, *attrValue, *upperAttr; r = [_attr rangeOfCharacterFromSet: equalSignCharSet]; if (r.length > 0) @@ -360,13 +360,16 @@ static NSCharacterSet *whitespaceCharSet = nil; } else { - if ([[_attr uppercaseString] isEqualToString: @"QUOTED-PRINTABLE"]) + upperAttr = [_attr uppercaseString]; + if ([upperAttr isEqualToString: @"QUOTED-PRINTABLE"] + || [upperAttr isEqualToString: @"BASE64"] + || [upperAttr isEqualToString: @"B"]) attrName = @"ENCODING"; else attrName = @"TYPE"; attrValue = _attr; } - + #if 0 // ZNeK: what's this for? r = [attrValue rangeOfCharacterFromSet: commaCharSet]; diff --git a/SoObjects/Appointments/SOGoAppointmentFolder.m b/SoObjects/Appointments/SOGoAppointmentFolder.m index 50808a32b..5f65cde0f 100644 --- a/SoObjects/Appointments/SOGoAppointmentFolder.m +++ b/SoObjects/Appointments/SOGoAppointmentFolder.m @@ -81,6 +81,16 @@ #define defaultColor @"#AAAAAA" +@interface SOGoGCSFolder (SOGoPrivate) + +- (void) appendObject: (NSDictionary *) object + properties: (NSString **) properties + count: (unsigned int) propertiesCount + withBaseURL: (NSString *) baseURL + toBuffer: (NSMutableString *) r; + +@end + @implementation SOGoAppointmentFolder static NSNumber *sharedYes = nil; @@ -1065,27 +1075,10 @@ firstInstanceCalendarDateRange: (NGCalendarDateRange *) fir return ma; } -- (void) _appendPropstat: (NSDictionary *) propstat - toBuffer: (NSMutableString *) r -{ - NSArray *properties; - unsigned int count, max; - - [r appendString: @""]; - properties = [propstat objectForKey: @"properties"]; - max = [properties count]; - for (count = 0; count < max; count++) - [r appendString: [properties objectAtIndex: count]]; - [r appendString: @""]; - [r appendString: [propstat objectForKey: @"status"]]; - [r appendString: @""]; -} - #warning we should use the EOFetchSpecification for that!!! (see doPROPFIND:) #warning components in calendar-data query are ignored -#warning the two following methods should be replaced with the new dav rendering mechanism - (NSString *) _nodeTagForProperty: (NSString *) property { NSString *namespace, *nodeName, *nsRep; @@ -1103,158 +1096,6 @@ firstInstanceCalendarDateRange: (NGCalendarDateRange *) fir return [NSString stringWithFormat: @"%@:%@", nsRep, nodeName]; } -- (NSString *) _nodeTag: (NSString *) property -{ - static NSMutableDictionary *tags = nil; - NSString *nodeTag; - - if (!tags) - tags = [NSMutableDictionary new]; - nodeTag = [tags objectForKey: property]; - if (!nodeTag) - { - nodeTag = [self _nodeTagForProperty: property]; - [tags setObject: nodeTag forKey: property]; - } - - return nodeTag; -} - -- (NSString **) _properties: (NSString **) properties - count: (unsigned int) propertiesCount - ofObject: (NSDictionary *) object -{ - SOGoCalendarComponent *sogoObject; - NSString **currentProperty; - NSString **values, **currentValue; - SEL methodSel; - -// NSLog (@"_properties:ofObject:: %@", [NSDate date]); - - values = NSZoneMalloc (NULL, - (propertiesCount + 1) * sizeof (NSString *)); - *(values + propertiesCount) = nil; - - //c = [self objectClassForComponentName: [object objectForKey: @"c_component"]]; - -#warning TODO: determine why this commented invocation takes so long... - // sogoObject = [self createChildComponentWithRecord: object]; - - sogoObject = [SOGoCalendarComponent objectWithRecord: object - inContainer: self]; - [sogoObject setComponentTag: [object objectForKey: @"c_component"]]; - - currentProperty = properties; - currentValue = values; - while (*currentProperty) - { - methodSel = SOGoSelectorForPropertyGetter (*currentProperty); - if (methodSel && [sogoObject respondsToSelector: methodSel]) - *currentValue = [[sogoObject performSelector: methodSel] - stringByEscapingXMLString]; - currentProperty++; - currentValue++; - } - -// NSLog (@"/_properties:ofObject:: %@", [NSDate date]); - - return values; -} - -- (NSArray *) _propstats: (NSString **) properties - count: (unsigned int) propertiesCount - ofObject: (NSDictionary *) object -{ - NSMutableArray *propstats, *properties200, *properties404, *propDict; - NSString **property, **values, **currentValue; - NSString *propertyValue, *nodeTag; - -// NSLog (@"_propstats:ofObject:: %@", [NSDate date]); - - propstats = [NSMutableArray array]; - - properties200 = [NSMutableArray array]; - properties404 = [NSMutableArray array]; - - values = [self _properties: properties count: propertiesCount - ofObject: object]; - currentValue = values; - - property = properties; - while (*property) - { - nodeTag = [self _nodeTag: *property]; - if (*currentValue) - { - propertyValue = [NSString stringWithFormat: @"<%@>%@", - nodeTag, *currentValue, nodeTag]; - propDict = properties200; - } - else - { - propertyValue = [NSString stringWithFormat: @"<%@/>", nodeTag]; - propDict = properties404; - } - [propDict addObject: propertyValue]; - property++; - currentValue++; - } - free (values); - - if ([properties200 count]) - [propstats addObject: [NSDictionary dictionaryWithObjectsAndKeys: - properties200, @"properties", - @"HTTP/1.1 200 OK", @"status", - nil]]; - if ([properties404 count]) - [propstats addObject: [NSDictionary dictionaryWithObjectsAndKeys: - properties404, @"properties", - @"HTTP/1.1 404 Not Found", @"status", - nil]]; -// NSLog (@"/_propstats:ofObject:: %@", [NSDate date]); - - return propstats; -} - -#warning We need to use the new DAV utilities here... -#warning this is baddddd because we return a single-valued dictionary containing \ - a cname which may not event exist... the logic behind appendObject:... should be \ - rethought, especially since we may start using SQL views - -- (void) appendObject: (NSDictionary *) object - properties: (NSString **) properties - count: (unsigned int) propertiesCount - withBaseURL: (NSString *) baseURL - toBuffer: (NSMutableString *) r -{ - NSArray *propstats; - unsigned int count, max; - - [r appendFormat: @""]; - [r appendString: baseURL]; - [r appendString: [object objectForKey: @"c_name"]]; - [r appendString: @""]; - -// NSLog (@"(appendPropstats...): %@", [NSDate date]); - propstats = [self _propstats: properties count: propertiesCount - ofObject: object]; - max = [propstats count]; - for (count = 0; count < max; count++) - [self _appendPropstat: [propstats objectAtIndex: count] - toBuffer: r]; -// NSLog (@"/(appendPropstats...): %@", [NSDate date]); - - [r appendString: @""]; -} - -- (void) appendMissingObjectRef: (NSString *) href - toBuffer: (NSMutableString *) r -{ - [r appendString: @""]; - [r appendString: href]; - [r appendString: @"HTTP/1.1 404 Not Found"]; -} - - (NSCalendarDate *) _getMaxStartDate { NSCalendarDate *now, *rc; @@ -1688,211 +1529,10 @@ firstInstanceCalendarDateRange: (NGCalendarDateRange *) fir return r; } -- (NSDictionary *) _deduceObjectNamesFromURLs: (NSArray *) urls +- (WOResponse *) davCalendarMultiget: (WOContext *) queryContext { - unsigned int count, max; - NSString *url, *componentURLPath, *cName, *baseURLString; - NSMutableDictionary *cNames; - NSURL *componentURL, *baseURL; - NSArray *urlComponents; - - max = [urls count]; - cNames = [NSMutableDictionary dictionaryWithCapacity: max]; - baseURL = [self davURL]; - baseURLString = [self davURLAsString]; - - for (count = 0; count < max; count++) - { - url = [NSString stringWithFormat: @"%@/%@", - [[urls objectAtIndex: count] stringByDeletingLastPathComponent], - [[[urls objectAtIndex: count] lastPathComponent] stringByEscapingURL]]; - componentURL = [[NSURL URLWithString: url relativeToURL: baseURL] - standardizedURL]; - componentURLPath = [componentURL absoluteString]; - if ([componentURLPath rangeOfString: baseURLString].location - != NSNotFound) - { - urlComponents = [componentURLPath componentsSeparatedByString: @"/"]; - cName = [[urls objectAtIndex: count] lastPathComponent]; - [cNames setObject: [urls objectAtIndex: count] forKey: cName]; - } - } - - return cNames; -} - -- (NSArray *) _fetchComponentsWithNames: (NSArray *) cNames - fields: (NSArray *) fields -{ - NSMutableString *filterString; - NSArray *records; - -// NSLog (@"fetchComponentsWithNames"); - filterString = [NSMutableString string]; - [filterString appendFormat: @"c_name='%@'", - [cNames componentsJoinedByString: @"' OR c_name='"]]; -// NSLog (@"fetchComponentsWithNames: query"); - records = [self bareFetchFields: fields - from: nil to: nil - title: nil - component: nil - additionalFilters: filterString]; -// NSLog (@"/fetchComponentsWithNames"); - - return records; -} - -#define maxQuerySize 2500 -#define baseQuerySize 160 -#define idQueryOverhead 13 - -- (NSArray *) _fetchComponentsMatchingObjectNames: (NSArray *) cNames - fields: (NSArray *) fields -{ - NSMutableArray *components; - NSArray *records; - NSMutableArray *currentNames; - unsigned int count, max, currentSize, queryNameLength; - NSString *currentName; - -// NSLog (@"fetching components matching names"); - - currentNames = [NSMutableArray array]; - currentSize = baseQuerySize; - - max = [cNames count]; - components = [NSMutableArray arrayWithCapacity: max]; - for (count = 0; count < max; count++) - { - currentName = [cNames objectAtIndex: count]; - queryNameLength = idQueryOverhead + [currentName length]; - if ((currentSize + queryNameLength) - > maxQuerySize) - { - records = [self _fetchComponentsWithNames: currentNames fields: fields]; - [components addObjectsFromArray: records]; - [currentNames removeAllObjects]; - currentSize = baseQuerySize; - } - [currentNames addObject: currentName]; - currentSize += queryNameLength; - } - - records = [self _fetchComponentsWithNames: currentNames fields: fields]; - [components addObjectsFromArray: records]; - -// NSLog (@"/fetching components matching names"); - - return components; -} - -- (NSDictionary *) _fetchComponentsMatchingURLs: (NSArray *) urls - fields: (NSArray *) fields -{ - NSMutableDictionary *components; - NSDictionary *cnames, *record; - NSString *recordURL; - NSArray *records; - unsigned int count, max; - - components = [NSMutableDictionary dictionary]; - - cnames = [self _deduceObjectNamesFromURLs: urls]; - records = [self _fetchComponentsMatchingObjectNames: [cnames allKeys] - fields: fields]; - max = [records count]; - for (count = 0; count < max; count++) - { - record = [records objectAtIndex: count]; - recordURL = [cnames objectForKey: [record objectForKey: @"c_name"]]; - if (recordURL) - [components setObject: record forKey: recordURL]; - } - - return components; -} - -- (void) _appendComponentProperties: (NSDictionary *) properties - matchingURLs: (id ) refs - toResponse: (WOResponse *) response -{ - NSObject *element; - NSDictionary *currentComponent, *components; - NSString *currentURL, *baseURL, *currentField; - NSString **propertiesArray; - NSMutableArray *urls, *fields; - NSMutableString *buffer; - unsigned int count, max, propertiesCount; - NSEnumerator *addFields; - - baseURL = [self davURLAsString]; -#warning review this when fixing http://www.scalableogo.org/bugs/view.php?id=276 - if (![baseURL hasSuffix: @"/"]) - baseURL = [NSString stringWithFormat: @"%@/", baseURL]; - - urls = [NSMutableArray array]; - max = [refs length]; - for (count = 0; count < max; count++) - { - element = [refs objectAtIndex: count]; - currentURL = [[element firstChild] nodeValue]; - [urls addObject: currentURL]; - } - - propertiesArray = [[properties allKeys] asPointersOfObjects]; - propertiesCount = [properties count]; - - fields = [NSMutableArray arrayWithObjects: @"c_name", @"c_component", nil]; - addFields = [[properties allValues] objectEnumerator]; - while ((currentField = [addFields nextObject])) - if ([currentField length]) - [fields addObjectUniquely: currentField]; - - components = [self _fetchComponentsMatchingURLs: urls fields: fields]; - max = [urls count]; -// NSLog (@"adding properties with url"); - buffer = [NSMutableString stringWithCapacity: max*512]; - for (count = 0; count < max; count++) - { - currentComponent = [components objectForKey: [urls objectAtIndex: count]]; - if (currentComponent) - [self appendObject: currentComponent - properties: propertiesArray - count: propertiesCount - withBaseURL: baseURL - toBuffer: buffer]; - else - [self appendMissingObjectRef: currentURL - toBuffer: buffer]; - } - [response appendContentString: buffer]; -// NSLog (@"/adding properties with url"); - - NSZoneFree (NULL, propertiesArray); -} - -- (id) davCalendarMultiget: (id) queryContext -{ - WOResponse *r; - id document; - DOMElement *documentElement, *propElement; - - r = [context response]; - [r prepareDAVResponse]; - [r appendContentString: @""]; - - document = [[context request] contentAsDOMDocument]; - documentElement = (DOMElement *) [document documentElement]; - propElement = [documentElement firstElementWithTag: @"prop" - inNamespace: @"DAV:"]; - - [self _appendComponentProperties: [self parseDAVRequestedProperties: propElement] - matchingURLs: [documentElement getElementsByTagName: @"href"] - toResponse: r]; - [r appendContentString:@""]; - - return r; + return [self performMultigetInContext: queryContext + inNamespace: @"urn:ietf:params:xml:ns:caldav"]; } - (NSString *) additionalWebdavSyncFilters diff --git a/SoObjects/Contacts/SOGoContactFolder.h b/SoObjects/Contacts/SOGoContactFolder.h index dfcb4287f..00a52456f 100644 --- a/SoObjects/Contacts/SOGoContactFolder.h +++ b/SoObjects/Contacts/SOGoContactFolder.h @@ -42,10 +42,6 @@ @protocol SOGoContactFolder -- (void) appendObject: (NSDictionary *) object - withBaseURL: (NSString *) baseURL - toREPORTResponse: (WOResponse *) r; - - (NSArray *) lookupContactsWithFilter: (NSString *) filter sortBy: (NSString *) sortKey ordering: (NSComparisonResult) sortOrdering; diff --git a/SoObjects/Contacts/SOGoContactFolders.m b/SoObjects/Contacts/SOGoContactFolders.m index 98197dfa6..6c791ba5b 100644 --- a/SoObjects/Contacts/SOGoContactFolders.m +++ b/SoObjects/Contacts/SOGoContactFolders.m @@ -35,6 +35,7 @@ #import #import #import +#import #import "SOGoContactGCSFolder.h" #import "SOGoContactSourceFolder.h" @@ -87,4 +88,16 @@ return [self labelForKey: @"Personal Address Book"]; } +- (NSArray *) toManyRelationshipKeys +{ + NSMutableArray *keys; + + if ([[context request] isAddressBookApp]) + keys = [NSMutableArray arrayWithObject: @"personal"]; + else + keys = (NSMutableArray *) [super toManyRelationshipKeys]; + + return keys; +} + @end diff --git a/SoObjects/Contacts/SOGoContactGCSEntry.m b/SoObjects/Contacts/SOGoContactGCSEntry.m index 22ee70bc0..03251f6ed 100644 --- a/SoObjects/Contacts/SOGoContactGCSEntry.m +++ b/SoObjects/Contacts/SOGoContactGCSEntry.m @@ -115,6 +115,11 @@ return @"text/x-vcard"; } +- (NSString *) davAddressData +{ + return [self contentAsString]; +} + /* specialized actions */ - (void) save diff --git a/SoObjects/Contacts/SOGoContactGCSFolder.m b/SoObjects/Contacts/SOGoContactGCSFolder.m index 1fa3f8f9e..30a5401b7 100644 --- a/SoObjects/Contacts/SOGoContactGCSFolder.m +++ b/SoObjects/Contacts/SOGoContactGCSFolder.m @@ -23,6 +23,7 @@ #import #import #import +#import #import #import @@ -31,15 +32,19 @@ #import #import #import -#import +#import #import #import + +#import #import +#import #import #import #import #import +#import #import "SOGoContactGCSEntry.h" #import "SOGoContactGCSList.h" @@ -84,7 +89,7 @@ static NSArray *folderListingFields = nil; - (Class) objectClassForComponentName: (NSString *) componentName { Class objectClass; - + if ([componentName isEqualToString: @"vcard"]) objectClass = [SOGoContactGCSEntry class]; else if ([componentName isEqualToString: @"vlist"]) @@ -289,38 +294,17 @@ static NSArray *folderListingFields = nil; return records; } -#warning this should be unified within SOGoFolder -- (void) appendObject: (NSDictionary *) object - withBaseURL: (NSString *) baseURL - toREPORTResponse: (WOResponse *) r +- (NSDictionary *) davSQLFieldsTable { - SOGoContactGCSEntry *component; - NSString *name, *etagLine, *contactString; + static NSMutableDictionary *davSQLFieldsTable = nil; - name = [object objectForKey: @"c_name"]; - component = [self lookupName: name inContext: context acquire: NO]; + if (!davSQLFieldsTable) + { + davSQLFieldsTable = [[super davSQLFieldsTable] mutableCopy]; + [davSQLFieldsTable setObject: @"c_content" forKey: @"{urn:ietf:params:xml:ns:carddav}address-data"]; + } - [r appendContentString: @" \r\n"]; - [r appendContentString: @" "]; - [r appendContentString: baseURL]; - if (![baseURL hasSuffix: @"/"]) - [r appendContentString: @"/"]; - [r appendContentString: name]; - [r appendContentString: @"\r\n"]; - - [r appendContentString: @" \r\n"]; - [r appendContentString: @" \r\n"]; - etagLine = [NSString stringWithFormat: @" %@\r\n", - [component davEntityTag]]; - [r appendContentString: etagLine]; - [r appendContentString: @" \r\n"]; - [r appendContentString: @" HTTP/1.1 200 OK\r\n"]; - [r appendContentString: @" \r\n"]; - [r appendContentString: @" "]; - contactString = [[component contentAsString] stringByEscapingXMLString]; - [r appendContentString: contactString]; - [r appendContentString: @"\r\n"]; - [r appendContentString: @" \r\n"]; + return davSQLFieldsTable; } - (NSArray *) davComplianceClassesInContext: (id)_ctx @@ -359,6 +343,12 @@ static NSArray *folderListingFields = nil; return resourceType; } +- (id) davAddressbookMultiget: (id) queryContext +{ + return [self performMultigetInContext: queryContext + inNamespace: @"urn:ietf:params:xml:ns:carddav"]; +} + /* sorting */ - (NSComparisonResult) compare: (id) otherFolder { @@ -385,4 +375,22 @@ static NSArray *folderListingFields = nil; return @"IPF.Contact"; } +/* TODO: multiget reorg */ +- (NSString *) _nodeTagForProperty: (NSString *) property +{ + NSString *namespace, *nodeName, *nsRep; + NSRange nsEnd; + + nsEnd = [property rangeOfString: @"}"]; + namespace + = [property substringFromRange: NSMakeRange (1, nsEnd.location - 1)]; + nodeName = [property substringFromIndex: nsEnd.location + 1]; + if ([namespace isEqualToString: XMLNS_CARDDAV]) + nsRep = @"C"; + else + nsRep = @"D"; + + return [NSString stringWithFormat: @"%@:%@", nsRep, nodeName]; +} + @end /* SOGoContactGCSFolder */ diff --git a/SoObjects/Contacts/SOGoContactSourceFolder.m b/SoObjects/Contacts/SOGoContactSourceFolder.m index c33fff60c..214bafe06 100644 --- a/SoObjects/Contacts/SOGoContactSourceFolder.m +++ b/SoObjects/Contacts/SOGoContactSourceFolder.m @@ -34,7 +34,6 @@ #import #import #import -#import #import #import @@ -50,49 +49,6 @@ @implementation SOGoContactSourceFolder -#warning this should be unified within SOGoFolder -- (void) appendObject: (NSDictionary *) object - withBaseURL: (NSString *) baseURL - toREPORTResponse: (WOResponse *) r -{ - SOGoContactLDIFEntry *component; - NSString *name, *etagLine, *contactString; - - name = [object objectForKey: @"c_name"]; - if ([name length]) - { - component = [self lookupName: name inContext: context acquire: NO]; - - if ([component isKindOfClass: [NSException class]]) - { - [self logWithFormat: @"Object with name '%@' not found. You likely have a LDAP configuration issue.", name]; - return; - } - - [r appendContentString: @" \r\n"]; - [r appendContentString: @" "]; - [r appendContentString: baseURL]; - if (![baseURL hasSuffix: @"/"]) - [r appendContentString: @"/"]; - [r appendContentString: name]; - [r appendContentString: @"\r\n"]; - - [r appendContentString: @" \r\n"]; - [r appendContentString: @" \r\n"]; - etagLine = [NSString stringWithFormat: @" %@\r\n", - [component davEntityTag]]; - [r appendContentString: etagLine]; - [r appendContentString: @" \r\n"]; - [r appendContentString: @" HTTP/1.1 200 OK\r\n"]; - [r appendContentString: @" \r\n"]; - [r appendContentString: @" "]; - contactString = [[component contentAsString] stringByEscapingXMLString]; - [r appendContentString: contactString]; - [r appendContentString: @"\r\n"]; - [r appendContentString: @" \r\n"]; - } -} - + (id) folderWithName: (NSString *) aName andDisplayName: (NSString *) aDisplayName inContainer: (id) aContainer diff --git a/SoObjects/Contacts/SOGoFolder+CardDAV.m b/SoObjects/Contacts/SOGoFolder+CardDAV.m index 8ad51dc28..26494ba8e 100644 --- a/SoObjects/Contacts/SOGoFolder+CardDAV.m +++ b/SoObjects/Contacts/SOGoFolder+CardDAV.m @@ -27,6 +27,7 @@ #import #import #import +#import #import #import #import @@ -39,6 +40,50 @@ @implementation SOGoFolder (CardDAV) +- (void) _appendObject: (NSDictionary *) object + withBaseURL: (NSString *) baseURL + toREPORTResponse: (WOResponse *) r +{ + id component; + NSString *name, *etagLine, *contactString; + + name = [object objectForKey: @"c_name"]; + if ([name length]) + { + component = [self lookupName: name inContext: context acquire: NO]; + if ([component isKindOfClass: [NSException class]]) + { + [self logWithFormat: @"Object with name '%@' not found. You likely have a LDAP configuration issue.", name]; + return; + } + +#warning we provide both "address-data" and "addressbook-data" for compatibility reasons, we should actually check which one has been queried + [r appendContentString: @"" + @""]; + [r appendContentString: baseURL]; + if (![baseURL hasSuffix: @"/"]) + [r appendContentString: @"/"]; + [r appendContentString: name]; + [r appendContentString: @"" + @"" + @""]; + etagLine = [NSString stringWithFormat: @"%@", + [component davEntityTag]]; + [r appendContentString: etagLine]; + [r appendContentString: @"" + @"HTTP/1.1 200 OK" + @"" + @""]; + contactString = [[component contentAsString] stringByEscapingXMLString]; + [r appendContentString: contactString]; + [r appendContentString: @"" + @""]; + [r appendContentString: contactString]; + [r appendContentString: @"" + @""]; + } +} + - (void) _appendComponentsMatchingFilters: (NSArray *) filters toResponse: (WOResponse *) response context: (id) localContext @@ -60,9 +105,8 @@ objectEnumerator]; while ((contact = [contacts nextObject])) - [(id)self appendObject: contact - withBaseURL: baseURL - toREPORTResponse: response]; + [self _appendObject: contact withBaseURL: baseURL + toREPORTResponse: response]; } } @@ -143,7 +187,7 @@ [self _appendComponentsMatchingFilters: filters toResponse: r context: queryContext]; - [r appendContentString:@""]; + [r appendContentString: @""]; return r; } diff --git a/SoObjects/Mailer/SOGoDraftObject.m b/SoObjects/Mailer/SOGoDraftObject.m index 294bb431e..6333cd51d 100644 --- a/SoObjects/Mailer/SOGoDraftObject.m +++ b/SoObjects/Mailer/SOGoDraftObject.m @@ -722,7 +722,7 @@ static NSString *userAgent = nil; { // TODO: use subject for filename? // error = [newDraft saveAttachment:content withName:@"forward.eml"]; - signature = [ud mailSignature]; + signature = [[self mailAccountFolder] signature]; if ([signature length]) [self setText: [NSString stringWithFormat: @"\n-- \n%@", signature]]; attachment = [NSDictionary dictionaryWithObjectsAndKeys: diff --git a/SoObjects/Mailer/SOGoMailAccount.h b/SoObjects/Mailer/SOGoMailAccount.h index b442e1a20..ea90d7195 100644 --- a/SoObjects/Mailer/SOGoMailAccount.h +++ b/SoObjects/Mailer/SOGoMailAccount.h @@ -50,7 +50,6 @@ typedef enum { @interface SOGoMailAccount : SOGoMailBaseObject { - NSString *accountName; SOGoMailFolder *inboxFolder; SOGoDraftsFolder *draftsFolder; SOGoSentFolder *sentFolder; @@ -58,14 +57,16 @@ typedef enum { SOGoIMAPAclStyle imapAclStyle; } -- (void) setAccountName: (NSString *) newAccountName; - - (SOGoIMAPAclStyle) imapAclStyle; - (BOOL) imapAclConformsToIMAPExt; - (BOOL) supportsQuotas; - (BOOL) updateFilters; +- (NSArray *) identities; +- (NSString *) signature; +- (NSString *) encryption; + /* folder pathes */ - (NSArray *) allFolderPaths; diff --git a/SoObjects/Mailer/SOGoMailAccount.m b/SoObjects/Mailer/SOGoMailAccount.m index 74cd6ea67..5a38ed1fc 100644 --- a/SoObjects/Mailer/SOGoMailAccount.m +++ b/SoObjects/Mailer/SOGoMailAccount.m @@ -40,6 +40,7 @@ #import #import +#import #import #import #import @@ -69,7 +70,6 @@ static NSString *sieveScriptName = @"sogo"; draftsFolder = nil; sentFolder = nil; trashFolder = nil; - accountName = nil; imapAclStyle = undefined; } @@ -82,15 +82,9 @@ static NSString *sieveScriptName = @"sogo"; [draftsFolder release]; [sentFolder release]; [trashFolder release]; - [accountName release]; [super dealloc]; } -- (void) setAccountName: (NSString *) newAccountName -{ - ASSIGN (accountName, newAccountName); -} - /* listing the available folders */ - (BOOL) isInDraftsFolder @@ -362,7 +356,7 @@ static NSString *sieveScriptName = @"sogo"; result = [client login: [[self imap4URL] user] password: password]; if (![[result valueForKey:@"result"] boolValue]) { [self errorWithFormat: @"failure. Attempting with a renewed password."]; - password = [self imap4PasswordRenewed: NO]; + password = [self imap4PasswordRenewed: YES]; result = [client login: [[self imap4URL] user] password: password]; } @@ -475,76 +469,82 @@ static NSString *sieveScriptName = @"sogo"; /* IMAP4 */ -- (BOOL) useSSL -{ - return NO; -} - -- (NSString *) imap4LoginFromHTTP -{ - WORequest *rq; - NSString *s; - NSArray *creds; - - rq = [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 *) _urlHostString +- (NSDictionary *) _mailAccount { NSDictionary *mailAccount; - NSString *username, *escUsername, *hostString; + NSArray *accounts; + SOGoUser *user; - mailAccount = [[context activeUser] accountWithName: accountName]; - if (mailAccount) - { - username = [mailAccount objectForKey: @"userName"]; - escUsername - = [[username stringByEscapingURL] stringByReplacingString: @"@" - withString: @"%40"]; - hostString = [NSString stringWithFormat: @"%@@%@", escUsername, - [mailAccount objectForKey: @"serverName"]]; - } + user = [SOGoUser userWithLogin: [self ownerInContext: nil]]; + accounts = [user mailAccounts]; + mailAccount = [accounts objectAtIndex: [nameInContainer intValue]]; + + return mailAccount; +} + +- (NSArray *) identities +{ + return [[self _mailAccount] objectForKey: @"identities"]; +} + +- (NSString *) signature +{ + NSArray *identities; + NSString *signature; + + identities = [self identities]; + if ([identities count] > 0) + signature = [[identities objectAtIndex: 0] objectForKey: @"signature"]; else - hostString = @"localhost"; + signature = nil; - return hostString; + return signature; +} + +- (NSString *) encryption +{ + NSString *encryption; + + encryption = [[self _mailAccount] objectForKey: @"encryption"]; + if (![encryption length]) + encryption = @"none"; + + return encryption; } - (NSMutableString *) imap4URLString { - /* private, overridden by SOGoSharedMailAccount */ - NSMutableString *urlString; - NSString *host; + NSMutableString *imap4URLString; + NSDictionary *mailAccount; + NSString *encryption, *protocol, *username, *escUsername; + int defaultPort, port; - urlString = [NSMutableString string]; - - if ([self useSSL]) - [urlString appendString: @"imaps://"]; + mailAccount = [self _mailAccount]; + encryption = [mailAccount objectForKey: @"encryption"]; + if ([encryption isEqualToString: @"ssl"]) + { + protocol = @"imaps"; + defaultPort = 993; + } else - [urlString appendString: @"imap://"]; + { + protocol = @"imap"; + defaultPort = 143; + } + + username = [mailAccount objectForKey: @"userName"]; + escUsername + = [[username stringByEscapingURL] stringByReplacingString: @"@" + withString: @"%40"]; + imap4URLString = [NSMutableString stringWithFormat: @"%@://%@@%@", + protocol, escUsername, + [mailAccount objectForKey: @"serverName"]]; + port = [[mailAccount objectForKey: @"port"] intValue]; + if (port && port != defaultPort) + [imap4URLString appendFormat: @":%d", port]; + [imap4URLString appendString: @"/"]; - host = [self _urlHostString]; - if (![host rangeOfString: @"@"].length) - [urlString appendFormat: @"%@@", [self imap4LoginFromHTTP]]; - [urlString appendFormat: @"%@/", host]; - - return urlString; + return imap4URLString; } - (NSMutableString *) traversalFromMailAccount @@ -552,9 +552,33 @@ static NSString *sieveScriptName = @"sogo"; return [NSMutableString string]; } -- (NSString *) imap4Login +- (NSString *) imap4PasswordRenewed: (BOOL) renewed { - return [[self imap4URL] user]; + /* + Extract password from basic authentication. + */ + NSURL *imapURL; + NSString *password; + + if ([nameInContainer isEqualToString: @"0"]) + { + imapURL = [self imap4URL]; + + password = [[self authenticatorInContext: context] + imapPasswordInContext: context + forServer: [imapURL host] + forceRenew: renewed]; + if (!password) + [self errorWithFormat: @"no IMAP4 password available"]; + } + else + { + password = [[self _mailAccount] objectForKey: @"password"]; + if (!password) + password = @""; + } + + return password; } /* name lookup */ @@ -610,70 +634,27 @@ static NSString *sieveScriptName = @"sogo"; return inboxFolderName; } -- (BOOL) _migrateFolderWithPurpose: (NSString *) purpose - withName: (NSString *) folderName -{ - SOGoUserDefaults *ud; - NSString *methodName; - SEL methodSel; - BOOL rc; - - ud = [[context activeUser] userDefaults]; - methodName = [NSString stringWithFormat: @"set%@FolderName:", purpose]; - methodSel = NSSelectorFromString (methodName); - if ([ud respondsToSelector: methodSel]) - { - [ud performSelector: methodSel withObject: folderName]; - [ud synchronize]; - rc = YES; - } - else - { - [self errorWithFormat: @"method '%@' not available with user defaults" - @" object, folder migration fails", methodName]; - rc = NO; - } - - return rc; -} - - (NSString *) _userFolderNameWithPurpose: (NSString *) purpose { SOGoUser *user; - SOGoUserSettings *us; - SOGoUserDefaults *ud; - NSMutableDictionary *mailSettings; - NSString *folderName, *key, *methodName; - SEL methodSel; + NSArray *accounts; + int accountIdx; + NSDictionary *account; + NSString *folderName; folderName = nil; - user = [context activeUser]; - /* migration part: */ - us = [user userSettings]; - mailSettings = [us objectForKey: @"Mail"]; - if (mailSettings) + user = [SOGoUser userWithLogin: [self ownerInContext: nil]]; + accounts = [user mailAccounts]; + accountIdx = [nameInContainer intValue]; + account = [accounts objectAtIndex: accountIdx]; + folderName = [[account objectForKey: @"mailboxes"] + objectForKey: purpose]; + if (!folderName && accountIdx > 0) { - key = [NSString stringWithFormat: @"%@Folder", purpose]; - folderName = [mailSettings objectForKey: key]; - if ([folderName length] - && [self _migrateFolderWithPurpose: purpose withName: folderName]) - { - [mailSettings removeObjectForKey: key]; - [us synchronize]; - folderName = nil; - } - } - else - folderName = nil; - - if (!folderName) - { - ud = [[context activeUser] userDefaults]; - methodName = [NSString stringWithFormat: @"%@FolderName", - [purpose lowercaseString]]; - methodSel = NSSelectorFromString (methodName); - folderName = [ud performSelector: methodSel]; + account = [accounts objectAtIndex: 0]; + folderName = [[account objectForKey: @"mailboxes"] + objectForKey: purpose]; } return folderName; @@ -800,40 +781,9 @@ static NSString *sieveScriptName = @"sogo"; return [[self imap4Connection] createMailbox:_name atURL:[self imap4URL]]; } -- (NSString *) shortTitle -{ - NSString *login, *host; - NSRange r; - - r = [accountName rangeOfString:@"@"]; - if (r.length > 0) - { - login = [accountName substringToIndex:r.location]; - host = [accountName substringFromIndex:(r.location + r.length)]; - } - else - { - login = nil; - host = accountName; - } - - 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]; + return [[self _mailAccount] objectForKey: @"name"]; } @end /* SOGoMailAccount */ diff --git a/SoObjects/Mailer/SOGoMailAccounts.h b/SoObjects/Mailer/SOGoMailAccounts.h index acf179dc9..2fe066a31 100644 --- a/SoObjects/Mailer/SOGoMailAccounts.h +++ b/SoObjects/Mailer/SOGoMailAccounts.h @@ -41,12 +41,10 @@ @class NSMutableDictionary; @interface SOGoMailAccounts : SOGoFolder -{ - NSMutableDictionary *accountKeys; -} + +- (NSArray *) mailAccounts; - (NSArray *) toManyRelationshipKeys; -- (NSDictionary *) accountKeys; @end diff --git a/SoObjects/Mailer/SOGoMailAccounts.m b/SoObjects/Mailer/SOGoMailAccounts.m index 1db84bdf9..fda419240 100644 --- a/SoObjects/Mailer/SOGoMailAccounts.m +++ b/SoObjects/Mailer/SOGoMailAccounts.m @@ -36,113 +36,55 @@ @implementation SOGoMailAccounts -/* listing the available mailboxes */ - -// - (BOOL) isInHomeFolderBranchOfLoggedInAccount: (NSString *) userLogin -// { -// return [[container nameInContainer] isEqualToString: userLogin]; -// } - -- (id) init +- (NSArray *) mailAccounts { - if ((self = [super init])) - { - accountKeys = nil; - } + SOGoUser *user; + + user = [SOGoUser userWithLogin: [self ownerInContext: nil]]; - return self; -} - -- (void) dealloc -{ - [accountKeys release]; - [super dealloc]; -} - -- (void) _initAccountKeys -{ - NSArray *accounts; - NSString *currentName; - int count, max; - - if (!accountKeys) - { - accountKeys = [NSMutableDictionary new]; - - accounts = [[context activeUser] mailAccounts]; - max = [accounts count]; - for (count = 0; count < max; count++) - { - currentName = [[accounts objectAtIndex: count] objectForKey: @"name"]; - [accountKeys setObject: currentName - forKey: [currentName asCSSIdentifier]]; - } - } -} - -- (NSDictionary *) accountKeys -{ - [self _initAccountKeys]; - - return accountKeys; + return [user mailAccounts]; } - (NSArray *) toManyRelationshipKeys { - [self _initAccountKeys]; + NSMutableArray *keys; + NSArray *accounts; + int count, max; + SOGoUser *user; + + user = [SOGoUser userWithLogin: [self ownerInContext: nil]]; + accounts = [user mailAccounts]; + max = [accounts count]; - return [accountKeys allKeys]; + keys = [NSMutableArray arrayWithCapacity: max]; + for (count = 0; count < max; count++) + [keys addObject: [NSString stringWithFormat: @"%d", count]]; + + return keys; } /* name lookup */ -// - (id) mailAccountWithName: (NSString *) _key -// inContext: (id) _ctx -// { -// static Class ctClass = Nil; -// id ct; - -// if (ctClass == Nil) -// ctClass = NSClassFromString(@"SOGoMailAccount"); -// if (ctClass == Nil) { -// [self errorWithFormat:@"missing SOGoMailAccount class!"]; -// return nil; -// } - -// ct = [[ctClass alloc] initWithName:_key inContainer:self]; - -// return [ct autorelease]; -// } - - (id) lookupName: (NSString *) _key inContext: (id) _ctx acquire: (BOOL) _flag { id obj; - NSString *accountName; -// NSString *userLogin; - -// userLogin = [[context activeUser] login]; + NSArray *accounts; + SOGoUser *user; + int keyCount; -// if (![self isInHomeFolderBranchOfLoggedInAccount: userLogin]) { -// [self warnWithFormat:@ "User %@ tried to access mail hierarchy of %@", -// userLogin, [container nameInContainer]]; - -// return [NSException exceptionWithHTTPStatus:403 /* Forbidden */ -// reason:@"Tried to access the mail of another user"]; -// } - /* first check attributes directly bound to the application */ obj = [super lookupName:_key inContext:_ctx acquire:NO]; if (!obj) { - [self _initAccountKeys]; - accountName = [accountKeys objectForKey: _key]; - if ([accountName length]) - { - obj = [SOGoMailAccount objectWithName: _key inContainer: self]; - [obj setAccountName: accountName]; - } + user = [SOGoUser userWithLogin: [self ownerInContext: nil]]; + accounts = [user mailAccounts]; + + keyCount = [_key intValue]; + if ([_key isEqualToString: [NSString stringWithFormat: @"%d", keyCount]] + && keyCount > -1 && keyCount < [accounts count]) + obj = [SOGoMailAccount objectWithName: _key inContainer: self]; else obj = [NSException exceptionWithHTTPStatus: 404 /* Not Found */]; } diff --git a/SoObjects/Mailer/SOGoMailBaseObject.h b/SoObjects/Mailer/SOGoMailBaseObject.h index 11c56105c..e7878b73d 100644 --- a/SoObjects/Mailer/SOGoMailBaseObject.h +++ b/SoObjects/Mailer/SOGoMailBaseObject.h @@ -69,7 +69,6 @@ - (NSMutableString *) traversalFromMailAccount; - (NSURL *) imap4URL; -- (NSString *) imap4Login; - (NSString *) imap4PasswordRenewed: (BOOL) renew; - (void) flushMailCaches; diff --git a/SoObjects/Mailer/SOGoMailBaseObject.m b/SoObjects/Mailer/SOGoMailBaseObject.m index eb77fa2f9..be239344a 100644 --- a/SoObjects/Mailer/SOGoMailBaseObject.m +++ b/SoObjects/Mailer/SOGoMailBaseObject.m @@ -30,8 +30,7 @@ #import #import -#import - +#import "SOGoMailAccount.h" #import "SOGoMailManager.h" #import "SOGoMailBaseObject.h" @@ -187,44 +186,28 @@ static BOOL debugOn = YES; - (NSURL *) imap4URL { + SOGoMailAccount *account; + NSString *urlString; + /* this could probably be handled better from NSURL but it's buggy in GNUstep */ if (!imap4URL) - imap4URL = [[NSURL alloc] initWithString: [self imap4URLString]]; + { + account = [self mailAccountFolder]; + if ([[account encryption] isEqualToString: @"tls"]) + urlString = [NSString stringWithFormat: @"%@?tls=YES", + [self imap4URLString]]; + else + urlString = [self imap4URLString]; + imap4URL = [[NSURL alloc] initWithString: urlString]; + } return imap4URL; } -- (NSString *) imap4Login -{ - if (![container respondsToSelector:_cmd]) - return nil; - - return [container imap4Login]; -} - - (NSString *) imap4PasswordRenewed: (BOOL) renewed { - /* - Extract password from basic authentication. - - TODO: we might want to - a) move the primary code to SOGoMailAccount - b) cache the password - */ - NSURL *imapURL; - NSString *password; - - imapURL = [[self mailAccountFolder] imap4URL]; - - password = [[self authenticatorInContext: context] - imapPasswordInContext: context - forServer: [imapURL host] - forceRenew: renewed]; - if (!password) - [self errorWithFormat: @"no IMAP4 password available"]; - - return password; + return [[self mailAccountFolder] imap4PasswordRenewed: renewed]; } - (NSMutableString *) traversalFromMailAccount diff --git a/SoObjects/Mailer/SOGoMailFolder.m b/SoObjects/Mailer/SOGoMailFolder.m index 1d60f8d43..e0b321eeb 100644 --- a/SoObjects/Mailer/SOGoMailFolder.m +++ b/SoObjects/Mailer/SOGoMailFolder.m @@ -447,7 +447,7 @@ static NSString *defaultUserID = @"anyone"; inContext: (id) localContext { NSArray *folders; - NSString *currentFolderName; + NSString *currentFolderName, *currentAccountName; NSMutableString *imapDestinationFolder; NGImap4Client *client; id result; @@ -458,31 +458,45 @@ static NSString *defaultUserID = @"anyone"; folders = [[destinationFolder componentsSeparatedByString: @"/"] resultsOfSelector: @selector (fromCSSIdentifier)]; max = [folders count]; - for (count = 2; count < max; count++) + if (max > 1) { - currentFolderName - = [[folders objectAtIndex: count] substringFromIndex: 6]; - [imapDestinationFolder appendFormat: @"/%@", currentFolderName]; + currentAccountName = [[self mailAccountFolder] nameInContainer]; + if ([[folders objectAtIndex: 1] isEqualToString: currentAccountName]) + { + for (count = 2; count < max; count++) + { + currentFolderName + = [[folders objectAtIndex: count] substringFromIndex: 6]; + [imapDestinationFolder appendFormat: @"/%@", currentFolderName]; + } + + client = [[self imap4Connection] client]; + [imap4 selectFolder: [self imap4URL]]; + + // We make sure the destination IMAP folder exist, if not, we create it. + result = [[client status: imapDestinationFolder + flags: [NSArray arrayWithObject: @"UIDVALIDITY"]] + objectForKey: @"result"]; + if (![result boolValue]) + result = [[self imap4Connection] createMailbox: imapDestinationFolder + atURL: [[self mailAccountFolder] imap4URL]]; + if (!result || [result boolValue]) + result = [client copyUids: uids toFolder: imapDestinationFolder]; + + if ([[result valueForKey: @"result"] boolValue]) + result = nil; + else + result = [NSException exceptionWithHTTPStatus: 500 + reason: @"Couldn't copy UIDs."]; + } + else + result = [NSException exceptionWithHTTPStatus: 500 + reason: @"Cannot copy messages across different accounts."]; } - - client = [[self imap4Connection] client]; - [imap4 selectFolder: [self imap4URL]]; - - // We make sure the destination IMAP folder exist, if not, we create it. - result = [[client status: imapDestinationFolder - flags: [NSArray arrayWithObject: @"UIDVALIDITY"]] - objectForKey: @"result"]; - if (![result boolValue]) - result = [[self imap4Connection] createMailbox: imapDestinationFolder - atURL: [[self mailAccountFolder] imap4URL]]; - if (!result || [result boolValue]) - result = [client copyUids: uids toFolder: imapDestinationFolder]; - - if ([[result valueForKey: @"result"] boolValue]) - result = nil; else - result = [NSException exceptionWithHTTPStatus: 500 reason: @"Couldn't copy UIDs."]; - + result = [NSException exceptionWithHTTPStatus: 500 + reason: @"Invalid destination."]; + return result; } @@ -1061,9 +1075,8 @@ static NSString *defaultUserID = @"anyone"; { thisAccount = [self mailAccountFolder]; mailAccount = [[user mailAccounts] objectAtIndex: 0]; - url = [NSString stringWithFormat: @"%@/%@%@", + url = [NSString stringWithFormat: @"%@/0%@", [self soURLToBaseContainerForUser: uid], - [mailAccount objectForKey: @"name"], otherUsersPath]; } else diff --git a/SoObjects/Mailer/SOGoMailForward.m b/SoObjects/Mailer/SOGoMailForward.m index cd152a289..7dddfe7bc 100644 --- a/SoObjects/Mailer/SOGoMailForward.m +++ b/SoObjects/Mailer/SOGoMailForward.m @@ -29,6 +29,7 @@ #import #import +#import "SOGoMailAccount.h" #import "SOGoMailObject+Draft.h" #import "SOGoMailForward.h" @@ -221,7 +222,7 @@ SOGoUserDefaults *ud; ud = [[context activeUser] userDefaults]; - signature = [ud mailSignature]; + signature = [[sourceMail mailAccountFolder] signature]; if ([signature length]) mailSignature = [NSString stringWithFormat: @"-- \n%@", signature]; else diff --git a/SoObjects/SOGo/DOMNode+SOGo.h b/SoObjects/SOGo/DOMNode+SOGo.h index b4297f6cb..2906058e8 100644 --- a/SoObjects/SOGo/DOMNode+SOGo.h +++ b/SoObjects/SOGo/DOMNode+SOGo.h @@ -23,6 +23,7 @@ #ifndef S_GO_SOOBJECTS_SOGO_DOMNODE_SOGO_H #define S_GO_SOOBJECTS_SOGO_DOMNODE_SOGO_H +#import #import @class DOMElement; diff --git a/SoObjects/SOGo/NSNumber+Utilities.m b/SoObjects/SOGo/NSNumber+Utilities.m index c71929e0f..1fac51667 100644 --- a/SoObjects/SOGo/NSNumber+Utilities.m +++ b/SoObjects/SOGo/NSNumber+Utilities.m @@ -28,8 +28,15 @@ - (NSString *) jsonRepresentation { + NSString *jsonRepresentation; - return [NSString stringWithFormat: @"%@", self]; + jsonRepresentation = [NSString stringWithFormat: @"%@", self]; + if ([jsonRepresentation isEqualToString: @"YES"]) + jsonRepresentation = @"true"; + else if ([jsonRepresentation isEqualToString: @"NO"]) + jsonRepresentation = @"false"; + + return jsonRepresentation; } @end diff --git a/SoObjects/SOGo/SOGoDomainDefaults.h b/SoObjects/SOGo/SOGoDomainDefaults.h index 76ed304fb..ac9cb73c2 100644 --- a/SoObjects/SOGo/SOGoDomainDefaults.h +++ b/SoObjects/SOGo/SOGoDomainDefaults.h @@ -37,6 +37,8 @@ - (NSArray *) userSources; +- (BOOL) mailAuxiliaryUserAccountsEnabled; + - (NSString *) mailDomain; - (NSString *) imapServer; - (NSString *) imapAclStyle; @@ -60,6 +62,7 @@ - (NSArray *) calendarDefaultRoles; - (NSArray *) contactsDefaultRoles; - (NSArray *) mailPollingIntervals; +- (BOOL) mailCheckAllUnseenCounts; - (NSString *) calendarDefaultCategoryColor; diff --git a/SoObjects/SOGo/SOGoDomainDefaults.m b/SoObjects/SOGo/SOGoDomainDefaults.m index 2d5f17f6c..3338bfdb6 100644 --- a/SoObjects/SOGo/SOGoDomainDefaults.m +++ b/SoObjects/SOGo/SOGoDomainDefaults.m @@ -98,6 +98,11 @@ return [self stringForKey: @"OCSFolderInfoURL"]; } +- (BOOL) mailAuxiliaryUserAccountsEnabled +{ + return [self boolForKey: @"SOGoMailAuxiliaryUserAccountsEnabled"]; +} + - (NSString *) mailDomain { return [self stringForKey: @"SOGoMailDomain"]; @@ -193,6 +198,11 @@ return [self arrayForKey: @"SOGoMailPollingIntervals"]; } +- (BOOL) mailCheckAllUnseenCounts +{ + return [self boolForKey: @"SOGoMailCheckAllUnseenCounts"]; +} + - (NSString *) smtpServer { return [self stringForKey: @"SOGoSMTPServer"]; diff --git a/SoObjects/SOGo/SOGoGCSFolder.h b/SoObjects/SOGo/SOGoGCSFolder.h index eca340d24..7079c4991 100644 --- a/SoObjects/SOGo/SOGoGCSFolder.h +++ b/SoObjects/SOGo/SOGoGCSFolder.h @@ -121,6 +121,10 @@ - (NSString *) davCollectionTag; +/* multiget helper */ +- (WOResponse *) performMultigetInContext: (WOContext *) queryContext + inNamespace: (NSString *) namespace; + @end #endif /* __SOGo_SOGoGCSFolder_H__ */ diff --git a/SoObjects/SOGo/SOGoGCSFolder.m b/SoObjects/SOGo/SOGoGCSFolder.m index 3f8388008..82df98235 100644 --- a/SoObjects/SOGo/SOGoGCSFolder.m +++ b/SoObjects/SOGo/SOGoGCSFolder.m @@ -1685,4 +1685,392 @@ static NSArray *childRecordFields = nil; [_ms appendFormat:@" ocs=%@", [self ocsPath]]; } +/* tmp: multiget */ + +- (NSArray *) _fetchComponentsWithNames: (NSArray *) cNames + fields: (NSArray *) fields +{ + NSArray *records; + NSString *sqlFilter; + NSMutableString *filterString; + EOQualifier *qualifier; + + sqlFilter = [self aclSQLListingFilter]; + if (sqlFilter) + { + filterString = [NSMutableString stringWithCapacity: 8192]; + [filterString appendFormat: @"(c_name='%@')", + [cNames componentsJoinedByString: @"' OR c_name='"]]; + if ([sqlFilter length] > 0) + [filterString appendFormat: @" AND (%@)", sqlFilter]; + qualifier = [EOQualifier qualifierWithQualifierFormat: filterString]; + records = [[self ocsFolder] fetchFields: fields + matchingQualifier: qualifier]; + if (![records isNotNull]) + { + [self errorWithFormat: @"(%s): fetch failed!", __PRETTY_FUNCTION__]; + return nil; + } + } + else + records = [NSArray array]; + + return records; +} + +#define maxQuerySize 2500 +#define baseQuerySize 160 +#define idQueryOverhead 13 + +- (NSArray *) _fetchComponentsMatchingObjectNames: (NSArray *) cNames + fields: (NSArray *) fields +{ + NSMutableArray *components; + NSArray *records; + NSMutableArray *currentNames; + unsigned int count, max, currentSize, queryNameLength; + NSString *currentName; + +// NSLog (@"fetching components matching names"); + + currentNames = [NSMutableArray array]; + currentSize = baseQuerySize; + + max = [cNames count]; + components = [NSMutableArray arrayWithCapacity: max]; + for (count = 0; count < max; count++) + { + currentName = [cNames objectAtIndex: count]; + queryNameLength = idQueryOverhead + [currentName length]; + if ((currentSize + queryNameLength) + > maxQuerySize) + { + records = [self _fetchComponentsWithNames: currentNames fields: fields]; + [components addObjectsFromArray: records]; + [currentNames removeAllObjects]; + currentSize = baseQuerySize; + } + [currentNames addObject: currentName]; + currentSize += queryNameLength; + } + + records = [self _fetchComponentsWithNames: currentNames fields: fields]; + [components addObjectsFromArray: records]; + +// NSLog (@"/fetching components matching names"); + + return components; +} + +- (NSDictionary *) _deduceObjectNamesFromURLs: (NSArray *) urls +{ + unsigned int count, max; + NSString *url, *componentURLPath, *cName, *baseURLString; + NSMutableDictionary *cNames; + NSURL *componentURL, *baseURL; + NSArray *urlComponents; + + max = [urls count]; + cNames = [NSMutableDictionary dictionaryWithCapacity: max]; + baseURL = [self davURL]; + baseURLString = [self davURLAsString]; + + for (count = 0; count < max; count++) + { + url = [NSString stringWithFormat: @"%@/%@", + [[urls objectAtIndex: count] stringByDeletingLastPathComponent], + [[[urls objectAtIndex: count] lastPathComponent] stringByEscapingURL]]; + componentURL = [[NSURL URLWithString: url relativeToURL: baseURL] + standardizedURL]; + componentURLPath = [componentURL absoluteString]; + if ([componentURLPath rangeOfString: baseURLString].location + != NSNotFound) + { + urlComponents = [componentURLPath componentsSeparatedByString: @"/"]; + cName = [[urls objectAtIndex: count] lastPathComponent]; + [cNames setObject: [urls objectAtIndex: count] forKey: cName]; + } + } + + return cNames; +} + +- (NSDictionary *) _fetchComponentsMatchingURLs: (NSArray *) urls + fields: (NSArray *) fields +{ + NSMutableDictionary *components; + NSDictionary *cnames, *record; + NSString *recordURL; + NSArray *records; + unsigned int count, max; + + components = [NSMutableDictionary dictionary]; + + cnames = [self _deduceObjectNamesFromURLs: urls]; + records = [self _fetchComponentsMatchingObjectNames: [cnames allKeys] + fields: fields]; + max = [records count]; + for (count = 0; count < max; count++) + { + record = [records objectAtIndex: count]; + recordURL = [cnames objectForKey: [record objectForKey: @"c_name"]]; + if (recordURL) + [components setObject: record forKey: recordURL]; + } + + return components; +} + +#warning the two following methods should be replaced with the new dav rendering mechanism +- (NSString *) _nodeTagForProperty: (NSString *) property +{ + [self subclassResponsibility: _cmd]; + return nil; +} + +- (NSString *) _nodeTag: (NSString *) property +{ + static NSMutableDictionary *tags = nil; + NSString *nodeTag; + + if (!tags) + tags = [NSMutableDictionary new]; + nodeTag = [tags objectForKey: property]; + if (!nodeTag) + { + nodeTag = [self _nodeTagForProperty: property]; + [tags setObject: nodeTag forKey: property]; + } + + return nodeTag; +} + +- (NSString **) _properties: (NSString **) properties + count: (unsigned int) propertiesCount + ofObject: (NSDictionary *) object +{ + SOGoContentObject *sogoObject; + NSString **currentProperty; + NSString **values, **currentValue; + SEL methodSel; + +// NSLog (@"_properties:ofObject:: %@", [NSDate date]); + + values = NSZoneMalloc (NULL, + (propertiesCount + 1) * sizeof (NSString *)); + *(values + propertiesCount) = nil; + + //c = [self objectClassForComponentName: [object objectForKey: @"c_component"]]; + + sogoObject = [self createChildComponentWithRecord: object]; + currentProperty = properties; + currentValue = values; + while (*currentProperty) + { + methodSel = SOGoSelectorForPropertyGetter (*currentProperty); + if (methodSel && [sogoObject respondsToSelector: methodSel]) + *currentValue = [[sogoObject performSelector: methodSel] + stringByEscapingXMLString]; + currentProperty++; + currentValue++; + } + +// NSLog (@"/_properties:ofObject:: %@", [NSDate date]); + + return values; +} + +- (NSArray *) _propstats: (NSString **) properties + count: (unsigned int) propertiesCount + ofObject: (NSDictionary *) object +{ + NSMutableArray *propstats, *properties200, *properties404, *propDict; + NSString **property, **values, **currentValue; + NSString *propertyValue, *nodeTag; + +// NSLog (@"_propstats:ofObject:: %@", [NSDate date]); + + propstats = [NSMutableArray array]; + + properties200 = [NSMutableArray array]; + properties404 = [NSMutableArray array]; + + values = [self _properties: properties count: propertiesCount + ofObject: object]; + currentValue = values; + + property = properties; + while (*property) + { + nodeTag = [self _nodeTag: *property]; + if (*currentValue) + { + propertyValue = [NSString stringWithFormat: @"<%@>%@", + nodeTag, *currentValue, nodeTag]; + propDict = properties200; + } + else + { + propertyValue = [NSString stringWithFormat: @"<%@/>", nodeTag]; + propDict = properties404; + } + [propDict addObject: propertyValue]; + property++; + currentValue++; + } + free (values); + + if ([properties200 count]) + [propstats addObject: [NSDictionary dictionaryWithObjectsAndKeys: + properties200, @"properties", + @"HTTP/1.1 200 OK", @"status", + nil]]; + if ([properties404 count]) + [propstats addObject: [NSDictionary dictionaryWithObjectsAndKeys: + properties404, @"properties", + @"HTTP/1.1 404 Not Found", @"status", + nil]]; +// NSLog (@"/_propstats:ofObject:: %@", [NSDate date]); + + return propstats; +} + +- (void) _appendPropstat: (NSDictionary *) propstat + toBuffer: (NSMutableString *) r +{ + NSArray *properties; + unsigned int count, max; + + [r appendString: @""]; + properties = [propstat objectForKey: @"properties"]; + max = [properties count]; + for (count = 0; count < max; count++) + [r appendString: [properties objectAtIndex: count]]; + [r appendString: @""]; + [r appendString: [propstat objectForKey: @"status"]]; + [r appendString: @""]; +} + +#warning We need to use the new DAV utilities here... +#warning this is baddddd because we return a single-valued dictionary containing \ + a cname which may not event exist... the logic behind appendObject:... should be \ + rethought, especially since we may start using SQL views + +- (void) appendObject: (NSDictionary *) object + properties: (NSString **) properties + count: (unsigned int) propertiesCount + withBaseURL: (NSString *) baseURL + toBuffer: (NSMutableString *) r +{ + NSArray *propstats; + unsigned int count, max; + + [r appendFormat: @""]; + [r appendString: baseURL]; + [r appendString: [object objectForKey: @"c_name"]]; + [r appendString: @""]; + +// NSLog (@"(appendPropstats...): %@", [NSDate date]); + propstats = [self _propstats: properties count: propertiesCount + ofObject: object]; + max = [propstats count]; + for (count = 0; count < max; count++) + [self _appendPropstat: [propstats objectAtIndex: count] + toBuffer: r]; +// NSLog (@"/(appendPropstats...): %@", [NSDate date]); + + [r appendString: @""]; +} + +- (void) appendMissingObjectRef: (NSString *) href + toBuffer: (NSMutableString *) r +{ + [r appendString: @""]; + [r appendString: href]; + [r appendString: @"HTTP/1.1 404 Not Found"]; +} + +- (void) _appendComponentProperties: (NSDictionary *) properties + matchingURLs: (id ) refs + toResponse: (WOResponse *) response +{ + NSObject *element; + NSDictionary *currentComponent, *components; + NSString *currentURL, *baseURL, *currentField; + NSString **propertiesArray; + NSMutableArray *urls, *fields; + NSMutableString *buffer; + unsigned int count, max, propertiesCount; + NSEnumerator *addFields; + + baseURL = [self davURLAsString]; +#warning review this when fixing http://www.scalableogo.org/bugs/view.php?id=276 + if (![baseURL hasSuffix: @"/"]) + baseURL = [NSString stringWithFormat: @"%@/", baseURL]; + + urls = [NSMutableArray array]; + max = [refs length]; + for (count = 0; count < max; count++) + { + element = [refs objectAtIndex: count]; + currentURL = [[element firstChild] nodeValue]; + [urls addObject: currentURL]; + } + + propertiesArray = [[properties allKeys] asPointersOfObjects]; + propertiesCount = [properties count]; + + fields = [NSMutableArray arrayWithObjects: @"c_name", @"c_component", nil]; + addFields = [[properties allValues] objectEnumerator]; + while ((currentField = [addFields nextObject])) + if ([currentField length]) + [fields addObjectUniquely: currentField]; + + components = [self _fetchComponentsMatchingURLs: urls fields: fields]; + max = [urls count]; +// NSLog (@"adding properties with url"); + buffer = [NSMutableString stringWithCapacity: max*512]; + for (count = 0; count < max; count++) + { + currentComponent = [components objectForKey: [urls objectAtIndex: count]]; + if (currentComponent) + [self appendObject: currentComponent + properties: propertiesArray + count: propertiesCount + withBaseURL: baseURL + toBuffer: buffer]; + else + [self appendMissingObjectRef: currentURL + toBuffer: buffer]; + } + [response appendContentString: buffer]; +// NSLog (@"/adding properties with url"); + + NSZoneFree (NULL, propertiesArray); +} + +- (WOResponse *) performMultigetInContext: (WOContext *) queryContext + inNamespace: (NSString *) namespace +{ + WOResponse *r; + id document; + DOMElement *documentElement, *propElement; + + r = [context response]; + [r prepareDAVResponse]; + [r appendContentString: + [NSString stringWithFormat: @"", namespace]]; + document = [[queryContext request] contentAsDOMDocument]; + documentElement = (DOMElement *) [document documentElement]; + propElement = [documentElement firstElementWithTag: @"prop" + inNamespace: @"DAV:"]; + [self _appendComponentProperties: [self parseDAVRequestedProperties: propElement] + matchingURLs: [documentElement getElementsByTagName: @"href"] + toResponse: r]; + [r appendContentString:@""]; + + return r; +} + @end /* SOGoFolder */ diff --git a/SoObjects/SOGo/SOGoUser.h b/SoObjects/SOGo/SOGoUser.h index b196c3003..4cdddacaf 100644 --- a/SoObjects/SOGo/SOGoUser.h +++ b/SoObjects/SOGo/SOGoUser.h @@ -40,7 +40,6 @@ @class NSMutableArray; @class NSMutableDictionary; @class NSString; -@class NSURL; @class WOContext; @@ -69,7 +68,7 @@ NSString *domainId; NSString *language; NSArray *allEmails; - NSArray *mailAccounts; + NSMutableArray *mailAccounts; NSString *cn; BOOL propagateCache; } diff --git a/SoObjects/SOGo/SOGoUser.m b/SoObjects/SOGo/SOGoUser.m index 15b217c28..e5c337430 100644 --- a/SoObjects/SOGo/SOGoUser.m +++ b/SoObjects/SOGo/SOGoUser.m @@ -436,53 +436,145 @@ } /* mail */ -- (NSArray *) mailAccounts +- (BOOL) _migrateFolderWithPurpose: (NSString *) purpose + withName: (NSString *) folderName { - NSMutableDictionary *mailAccount, *identity; + NSString *methodName; + SEL methodSel; + BOOL rc; + + [self userDefaults]; + methodName = [NSString stringWithFormat: @"set%@FolderName:", purpose]; + methodSel = NSSelectorFromString (methodName); + if ([_defaults respondsToSelector: methodSel]) + { + [_defaults performSelector: methodSel withObject: folderName]; + rc = YES; + } + else + { + [self errorWithFormat: @"method '%@' not available with user defaults" + @" object, folder migration fails", methodName]; + rc = NO; + } + + return rc; +} + +- (void) _migrateFolderSettings +{ + NSMutableDictionary *mailSettings; + NSString *folderName, *key; + BOOL migrated; + NSString **purpose; + NSString *purposes[] = { @"Drafts", @"Sent", @"Trash", nil }; + + [self userSettings]; + mailSettings = [_settings objectForKey: @"Mail"]; + if (mailSettings) + { + migrated = NO; + purpose = purposes; + while (*purpose) + { + key = [NSString stringWithFormat: @"%@Folder", *purpose]; + folderName = [mailSettings objectForKey: key]; + if ([folderName length] + && [self _migrateFolderWithPurpose: *purpose + withName: folderName]) + { + migrated = YES; + [mailSettings removeObjectForKey: key]; + folderName = nil; + } + purpose++; + } + if (migrated) + { + [_settings synchronize]; + [self userDefaults]; + [_defaults synchronize]; + } + } +} + +- (void) _appendSystemMailAccount +{ + NSMutableDictionary *mailAccount, *identity, *mailboxes; NSMutableArray *identities; - NSString *fullName, *imapLogin, *imapServer; + NSString *fullName, *imapLogin, *imapServer, *signature; NSArray *mails; unsigned int count, max; + mailAccount = [NSMutableDictionary new]; + + imapLogin = [[SOGoUserManager sharedUserManager] + getImapLoginForUID: login]; + imapServer = [self _fetchFieldForUser: @"c_imaphostname"]; + if (!imapServer) + imapServer = [[self domainDefaults] imapServer]; + + [mailAccount setObject: imapLogin forKey: @"userName"]; + [mailAccount setObject: imapServer forKey: @"serverName"]; + + identities = [NSMutableArray new]; + mails = [self allEmails]; + [mailAccount setObject: [mails objectAtIndex: 0] forKey: @"name"]; + + max = [mails count]; + if (max > 1) + max--; + for (count = 0; count < max; count++) + { + identity = [NSMutableDictionary new]; + fullName = [self cn]; + if (![fullName length]) + fullName = login; + [identity setObject: fullName forKey: @"fullName"]; + [identity setObject: [mails objectAtIndex: count] forKey: @"email"]; + signature = [[self userDefaults] mailSignature]; + if (signature) + [identity setObject: signature forKey: @"signature"]; + [identities addObject: identity]; + [identity release]; + } + [[identities objectAtIndex: 0] setObject: [NSNumber numberWithBool: YES] + forKey: @"isDefault"]; + + [mailAccount setObject: identities forKey: @"identities"]; + [identities release]; + + mailboxes = [NSMutableDictionary new]; + + [self userDefaults]; + [self _migrateFolderSettings]; + [mailboxes setObject: [_defaults draftsFolderName] + forKey: @"Drafts"]; + [mailboxes setObject: [_defaults sentFolderName] + forKey: @"Sent"]; + [mailboxes setObject: [_defaults trashFolderName] + forKey: @"Trash"]; + [mailAccount setObject: mailboxes forKey: @"mailboxes"]; + [mailboxes release]; + + [mailAccounts addObject: mailAccount]; + [mailAccount release]; +} + +- (NSArray *) mailAccounts +{ + NSArray *auxAccounts; + if (!mailAccounts) { - imapLogin = [[SOGoUserManager sharedUserManager] - getImapLoginForUID: login]; - imapServer = [self _fetchFieldForUser: @"c_imaphostname"]; - if (!imapServer) - imapServer = [[self domainDefaults] imapServer]; - mailAccount = [NSMutableDictionary new]; - [mailAccount setObject: imapLogin forKey: @"userName"]; - [mailAccount setObject: imapServer forKey: @"serverName"]; - - identities = [NSMutableArray new]; - mails = [self allEmails]; - [mailAccount setObject: [mails objectAtIndex: 0] - forKey: @"name"]; - - max = [mails count]; - if (max > 1) - max--; - for (count = 0; count < max; count++) + mailAccounts = [NSMutableArray new]; + [self _appendSystemMailAccount]; + if ([[self domainDefaults] mailAuxiliaryUserAccountsEnabled]) { - identity = [NSMutableDictionary new]; - fullName = [self cn]; - if (![fullName length]) - fullName = login; - [identity setObject: fullName forKey: @"fullName"]; - [identity setObject: [mails objectAtIndex: count] forKey: @"email"]; - [identities addObject: identity]; - [identity release]; + auxAccounts = [[self userDefaults] auxiliaryMailAccounts]; + if (auxAccounts) + [mailAccounts addObjectsFromArray: auxAccounts]; } - [[identities objectAtIndex: 0] setObject: [NSNumber numberWithBool: YES] - forKey: @"isDefault"]; - - [mailAccount setObject: identities forKey: @"identities"]; - [identities release]; - - mailAccounts = [NSArray arrayWithObject: mailAccount]; - [mailAccounts retain]; - [mailAccount release]; } return mailAccounts; @@ -505,60 +597,6 @@ return mailAccount; } -/* -@interface SOGoMailIdentity : NSObject -{ - NSString *name; - NSString *email; - NSString *replyTo; - NSString *organization; - NSString *signature; - NSString *vCard; - NSString *sentFolderName; - NSString *sentBCC; - NSString *draftsFolderName; - NSString *templatesFolderName; - struct - { - int composeHTML:1; - int reserved:31; - } idFlags; -} - -- (void) setName: (NSString *) _value; -- (NSString *) name; - -- (void) setEmail: (NSString *) _value; -- (NSString *) email; - -- (void) setReplyTo: (NSString *) _value; -- (NSString *) replyTo; - -- (void) setOrganization: (NSString *) _value; -- (NSString *) organization; - -- (void) setSignature: (NSString *) _value; -- (NSString *) signature; -- (BOOL) hasSignature; - -- (void) setVCard: (NSString *) _value; -- (NSString *) vCard; -- (BOOL) hasVCard; - -- (void) setSentFolderName: (NSString *) _value; -- (NSString *) sentFolderName; - -- (void) setSentBCC: (NSString *) _value; -- (NSString *) sentBCC; - -- (void) setDraftsFolderName: (NSString *) _value; -- (NSString *) draftsFolderName; - -- (void) setTemplatesFolderName: (NSString *) _value; -- (NSString *) templatesFolderName; - -@end */ - - (NSArray *) allIdentities { NSArray *identities; @@ -603,28 +641,6 @@ ignoringRights: YES]; } -// - (id) schedulingCalendarInContext: (id) _ctx -// { -// /* Note: watch out for cyclic references */ -// id folder; - -// folder = [(WOContext *)_ctx objectForKey:@"ActiveUserCalendar"]; -// if (folder != nil) -// return [folder isNotNull] ? folder : nil; - -// folder = [self homeFolderInContext:_ctx]; -// if ([folder isKindOfClass:[NSException class]]) -// return folder; - -// folder = [folder lookupName:@"Calendar" inContext:_ctx acquire:NO]; -// if ([folder isKindOfClass:[NSException class]]) -// return folder; - -// [(WOContext *)_ctx setObject:folder ? folder : [NSNull null] -// forKey:@"ActiveUserCalendar"]; -// return folder; -// } - - (NSArray *) rolesForObject: (NSObject *) object inContext: (WOContext *) context { diff --git a/SoObjects/SOGo/SOGoUserDefaults.h b/SoObjects/SOGo/SOGoUserDefaults.h index a23901adf..f4940fa35 100644 --- a/SoObjects/SOGo/SOGoUserDefaults.h +++ b/SoObjects/SOGo/SOGoUserDefaults.h @@ -122,6 +122,9 @@ extern NSString *SOGoWeekStartFirstFullWeek; - (void) setMailUseOutlookStyleReplies: (BOOL) newValue; - (BOOL) mailUseOutlookStyleReplies; +- (void) setAuxiliaryMailAccounts: (NSArray *) newAccounts; +- (NSArray *) auxiliaryMailAccounts; + - (void) setCalendarCategories: (NSArray *) newValues; - (NSArray *) calendarCategories; diff --git a/SoObjects/SOGo/SOGoUserDefaults.m b/SoObjects/SOGo/SOGoUserDefaults.m index bfaa27d63..bdd10a13e 100644 --- a/SoObjects/SOGo/SOGoUserDefaults.m +++ b/SoObjects/SOGo/SOGoUserDefaults.m @@ -511,6 +511,16 @@ NSString *SOGoWeekStartFirstFullWeek = @"FirstFullWeek"; return [self boolForKey: @"SOGoMailUseOutlookStyleReplies"]; } +- (void) setAuxiliaryMailAccounts: (NSArray *) newAccounts +{ + [self setObject: newAccounts forKey: @"AuxiliaryMailAccounts"]; +} + +- (NSArray *) auxiliaryMailAccounts +{ + return [self arrayForKey: @"AuxiliaryMailAccounts"]; +} + - (void) setCalendarCategories: (NSArray *) newValues { [self setObject: newValues forKey: @"SOGoCalendarCategories"]; diff --git a/SoObjects/SOGo/SQLSource.h b/SoObjects/SOGo/SQLSource.h index 2a894c367..40f0425f7 100644 --- a/SoObjects/SOGo/SQLSource.h +++ b/SoObjects/SOGo/SQLSource.h @@ -25,7 +25,7 @@ #import -#include "SOGoSource.h" +#import "SOGoSource.h" @class NSArray; @class NSDictionary; diff --git a/SoObjects/SOGo/SQLSource.m b/SoObjects/SOGo/SQLSource.m index aacb36869..07333739d 100644 --- a/SoObjects/SOGo/SQLSource.m +++ b/SoObjects/SOGo/SQLSource.m @@ -20,6 +20,12 @@ * Boston, MA 02111-1307, USA. */ +#define _XOPEN_SOURCE 1 +#include + +#include +#include + #import #import #import @@ -35,14 +41,9 @@ #import #import -#include +#import "SOGoConstants.h" -#include -#include - -#include "SQLSource.h" - -#include "SOGoConstants.h" +#import "SQLSource.h" /** * The view MUST contain the following columns: diff --git a/SoObjects/SOGo/WORequest+SOGo.h b/SoObjects/SOGo/WORequest+SOGo.h index 415c3dbb4..f5b775e90 100644 --- a/SoObjects/SOGo/WORequest+SOGo.h +++ b/SoObjects/SOGo/WORequest+SOGo.h @@ -34,6 +34,7 @@ - (BOOL) isIPhone; - (BOOL) isICal; - (BOOL) isICal4; +- (BOOL) isAddressBookApp; @end diff --git a/SoObjects/SOGo/WORequest+SOGo.m b/SoObjects/SOGo/WORequest+SOGo.m index c1c0d042a..744a4802b 100644 --- a/SoObjects/SOGo/WORequest+SOGo.m +++ b/SoObjects/SOGo/WORequest+SOGo.m @@ -137,4 +137,14 @@ return [self isAppleDAVWithSubstring: @"iCal/4."]; } +- (BOOL) isAddressBookApp +{ + WEClientCapabilities *cc; + + cc = [self clientCapabilities]; + + return ([[cc userAgent] rangeOfString: @"CFNetwork"].location != NSNotFound + && [[cc userAgent] rangeOfString: @"Darwin"].location != NSNotFound); +} + @end diff --git a/Tests/Integration/test-caldav-scheduling.py b/Tests/Integration/test-caldav-scheduling.py index bd9ffb7c6..4588770f0 100755 --- a/Tests/Integration/test-caldav-scheduling.py +++ b/Tests/Integration/test-caldav-scheduling.py @@ -473,7 +473,7 @@ class CalDAVITIPDelegationTest(unittest.TestCase): att_inv = self._getEvent(self.client, "%stest-delegation.ics" % self.attendee1_calendar, 404) del_inv = self._getEvent(self.client, - "%stest-delegation.ics" % self.attendee1_calendar, 404) + "%stest-delegation.ics" % self.attendee1_delegate_calendar, 404) if __name__ == "__main__": sogotests.runTests() diff --git a/UI/Common/BrazilianPortuguese.lproj/Localizable.strings b/UI/Common/BrazilianPortuguese.lproj/Localizable.strings index 805c824be..6b73bfa2a 100644 --- a/UI/Common/BrazilianPortuguese.lproj/Localizable.strings +++ b/UI/Common/BrazilianPortuguese.lproj/Localizable.strings @@ -65,6 +65,12 @@ "delegate is a participant" = "The delegate is already a participant."; "delegate is a group" = "The specified address corresponds to a group. You can only delegate to a unique person."; +/* common buttons */ +"OK" = "OK"; +"Cancel" = "Cancel"; +"Yes" = "Yes"; +"No" = "No"; + /* alarms */ "Reminder:" = "Lembrete:"; "Start:" = "Inicio:"; diff --git a/UI/Common/Czech.lproj/Localizable.strings b/UI/Common/Czech.lproj/Localizable.strings index 75df7b530..0db6d1f6b 100644 --- a/UI/Common/Czech.lproj/Localizable.strings +++ b/UI/Common/Czech.lproj/Localizable.strings @@ -65,6 +65,12 @@ "delegate is a participant" = "Delegovaný je již účastníkem."; "delegate is a group" = "Určená adresa odpovídá skupině. Delegovat můžete pouze na osobu."; +/* common buttons */ +"OK" = "OK"; +"Cancel" = "Cancel"; +"Yes" = "Yes"; +"No" = "No"; + /* alarms */ "Reminder:" = "Upomínka:"; "Start:" = "Začátek:"; diff --git a/UI/Common/Dutch.lproj/Localizable.strings b/UI/Common/Dutch.lproj/Localizable.strings index 90502093d..536cdf0f6 100644 --- a/UI/Common/Dutch.lproj/Localizable.strings +++ b/UI/Common/Dutch.lproj/Localizable.strings @@ -65,6 +65,12 @@ "delegate is a participant" = "The delegate is already a participant."; "delegate is a group" = "The specified address corresponds to a group. You can only delegate to a unique person."; +/* common buttons */ +"OK" = "OK"; +"Cancel" = "Cancel"; +"Yes" = "Ja"; +"No" = "Nee"; + /* alarms */ "Reminder:" = "Alarm:"; "Start:" = "Begin:"; diff --git a/UI/Common/English.lproj/Localizable.strings b/UI/Common/English.lproj/Localizable.strings index 6e0729988..9377dc92c 100644 --- a/UI/Common/English.lproj/Localizable.strings +++ b/UI/Common/English.lproj/Localizable.strings @@ -65,6 +65,12 @@ "delegate is a participant" = "The delegate is already a participant."; "delegate is a group" = "The specified address corresponds to a group. You can only delegate to a unique person."; +/* common buttons */ +"OK" = "OK"; +"Cancel" = "Cancel"; +"Yes" = "Yes"; +"No" = "No"; + /* alarms */ "Reminder:" = "Reminder:"; "Start:" = "Start:"; diff --git a/UI/Common/French.lproj/Localizable.strings b/UI/Common/French.lproj/Localizable.strings index 05123606f..24224ff43 100644 --- a/UI/Common/French.lproj/Localizable.strings +++ b/UI/Common/French.lproj/Localizable.strings @@ -65,6 +65,12 @@ "delegate is a participant" = "Le délégué est déjà un participant."; "delegate is a group" = "L'adresse spécifiée correspond à un groupe. Vous ne pouvez déléguer qu'à une personne."; +/* common buttons */ +"OK" = "OK"; +"Cancel" = "Annuler"; +"Yes" = "Oui"; +"No" = "Non"; + /* alarms */ "Reminder:" = "Rappel :"; "Start:" = "Début :"; diff --git a/UI/Common/German.lproj/Localizable.strings b/UI/Common/German.lproj/Localizable.strings index bc5dc18ca..136ea67c9 100644 --- a/UI/Common/German.lproj/Localizable.strings +++ b/UI/Common/German.lproj/Localizable.strings @@ -65,6 +65,12 @@ "delegate is a participant" = "Der Vertreter ist bereits ein Teilnehmer."; "delegate is a group" = "Die angegebene Adresse gehört einer Gruppe. Es kann nur zu einer einzelnen Person delegiert werden."; +/* common buttons */ +"OK" = "OK"; +"Cancel" = "Cancel"; +"Yes" = "Ja"; +"No" = "Nein"; + /* alarms */ "Reminder:" = "Alarm:"; "Start:" = "Beginn:"; diff --git a/UI/Common/Hungarian.lproj/Localizable.strings b/UI/Common/Hungarian.lproj/Localizable.strings index 3eca675d2..a9214427e 100644 --- a/UI/Common/Hungarian.lproj/Localizable.strings +++ b/UI/Common/Hungarian.lproj/Localizable.strings @@ -65,6 +65,12 @@ "delegate is a participant" = "The delegate is already a participant."; "delegate is a group" = "The specified address corresponds to a group. You can only delegate to a unique person."; +/* common buttons */ +"OK" = "OK"; +"Cancel" = "Cancel"; +"Yes" = "Yes"; +"No" = "No"; + /* alarms */ "Reminder:" = "Emlékeztető:"; "Start:" = "Kezdés:"; diff --git a/UI/Common/Italian.lproj/Localizable.strings b/UI/Common/Italian.lproj/Localizable.strings index beddbcf8e..9905bc230 100644 --- a/UI/Common/Italian.lproj/Localizable.strings +++ b/UI/Common/Italian.lproj/Localizable.strings @@ -65,6 +65,12 @@ "delegate is a participant" = "The delegate is already a participant."; "delegate is a group" = "The specified address corresponds to a group. You can only delegate to a unique person."; +/* common buttons */ +"OK" = "OK"; +"Cancel" = "Cancel"; +"Yes" = "Si"; +"No" = "No"; + /* alarms */ "Reminder:" = "Promemoria:"; "Start:" = "Inizio:"; diff --git a/UI/Common/Russian.lproj/Localizable.strings b/UI/Common/Russian.lproj/Localizable.strings index 80c157ff9..d08f0ed77 100644 --- a/UI/Common/Russian.lproj/Localizable.strings +++ b/UI/Common/Russian.lproj/Localizable.strings @@ -65,6 +65,12 @@ "delegate is a participant" = "The delegate is already a participant."; "delegate is a group" = "The specified address corresponds to a group. You can only delegate to a unique person."; +/* common buttons */ +"OK" = "OK"; +"Cancel" = "Cancel"; +"Yes" = "Yes"; +"No" = "No"; + /* alarms */ "Reminder:" = "Напоминание:"; "Start:" = "Начало:"; diff --git a/UI/Common/Spanish.lproj/Localizable.strings b/UI/Common/Spanish.lproj/Localizable.strings index 158e6ebb9..a7167769c 100644 --- a/UI/Common/Spanish.lproj/Localizable.strings +++ b/UI/Common/Spanish.lproj/Localizable.strings @@ -65,6 +65,12 @@ "delegate is a participant" = "The delegate is already a participant."; "delegate is a group" = "The specified address corresponds to a group. You can only delegate to a unique person."; +/* common buttons */ +"OK" = "OK"; +"Cancel" = "Cancel"; +"Yes" = "Yes"; +"No" = "No"; + /* alarms */ "Reminder:" = "Recordatorio:"; "Start:" = "Desde:"; diff --git a/UI/Common/Swedish.lproj/Localizable.strings b/UI/Common/Swedish.lproj/Localizable.strings index 12f3eb57c..d1886330a 100644 --- a/UI/Common/Swedish.lproj/Localizable.strings +++ b/UI/Common/Swedish.lproj/Localizable.strings @@ -65,6 +65,12 @@ "delegate is a participant" = "Personen du delegerar till är redan en deltagare."; "delegate is a group" = "Adressen du skrivit går till en grupp. Du kan bara delegera till en unik person."; +/* common buttons */ +"OK" = "OK"; +"Cancel" = "Cancel"; +"Yes" = "Yes"; +"No" = "No"; + /* alarms */ "Reminder:" = "Påminnelse:"; "Start:" = "Start:"; diff --git a/UI/Common/UIxPageFrame.h b/UI/Common/UIxPageFrame.h index 080f949bb..331db446f 100644 --- a/UI/Common/UIxPageFrame.h +++ b/UI/Common/UIxPageFrame.h @@ -40,6 +40,8 @@ NSString *toolbar; id item; BOOL isPopup; + NSArray *udKeys; + NSArray *usKeys; NSMutableArray *additionalCSSFiles; NSMutableArray *additionalJSFiles; } diff --git a/UI/Common/UIxPageFrame.m b/UI/Common/UIxPageFrame.m index ec6b0bb08..ab9ac4947 100644 --- a/UI/Common/UIxPageFrame.m +++ b/UI/Common/UIxPageFrame.m @@ -21,10 +21,12 @@ */ #import +#import #import #import +#import #import #import #import @@ -44,6 +46,8 @@ item = nil; title = nil; toolbar = nil; + udKeys = nil; + usKeys = nil; additionalJSFiles = nil; additionalCSSFiles = nil; } @@ -56,6 +60,8 @@ [item release]; [title release]; [toolbar release]; + [udKeys release]; + [usKeys release]; [additionalJSFiles release]; [additionalCSSFiles release]; [super dealloc]; @@ -462,30 +468,73 @@ return [ud language]; } -- (NSString *) userSettings +/* UserDefaults, UserSettings */ +- (NSString *) _dictionaryWithKeys: (NSArray *) keys + fromSource: (SOGoDefaultsSource *) source { - SOGoUserSettings *us; - NSString *jsonResult; + NSString *key; + int count, max; + NSMutableDictionary *dict; + NSNull *nsNull; + id value; - us = [[context activeUser] userSettings]; - jsonResult = [[us source] jsonRepresentation]; - if (!jsonResult) - jsonResult = @"{}"; + nsNull = [NSNull null]; - return jsonResult; + max = [keys count]; + + dict = [NSMutableDictionary dictionaryWithCapacity: max]; + for (count = 0; count < max; count++) + { + key = [keys objectAtIndex: count]; + value = [source objectForKey: key]; + if (!value) + value = nsNull; + [dict setObject: value forKey: key]; + } + + return [dict jsonRepresentation]; +} + +- (void) setUserDefaultsKeys: (NSString *) newKeys +{ + [udKeys release]; + udKeys = [[newKeys componentsSeparatedByString: @","] trimmedComponents]; + [udKeys retain]; +} + +- (BOOL) hasUserDefaultsKeys +{ + return ([udKeys count] > 0); } - (NSString *) userDefaults { SOGoUserDefaults *ud; - NSString *jsonResult; ud = [[context activeUser] userDefaults]; - jsonResult = [[ud source] jsonRepresentation]; - if (!jsonResult) - jsonResult = @"{}"; - return jsonResult; + return [self _dictionaryWithKeys: udKeys fromSource: ud]; +} + +- (void) setUserSettingsKeys: (NSString *) newKeys +{ + [usKeys release]; + usKeys = [[newKeys componentsSeparatedByString: @","] trimmedComponents]; + [usKeys retain]; +} + +- (BOOL) hasUserSettingsKeys +{ + return ([usKeys count] > 0); +} + +- (NSString *) userSettings +{ + SOGoUserSettings *us; + + us = [[context activeUser] userSettings]; + + return [self _dictionaryWithKeys: usKeys fromSource: us]; } /* browser/os identification */ diff --git a/UI/Common/Ukrainian.lproj/Localizable.strings b/UI/Common/Ukrainian.lproj/Localizable.strings index 7ac70dacb..e7e4eb31c 100644 --- a/UI/Common/Ukrainian.lproj/Localizable.strings +++ b/UI/Common/Ukrainian.lproj/Localizable.strings @@ -61,6 +61,12 @@ "delegate is a participant" = "Цього учасника вже запрошено."; "delegate is a group" = "Зазначена адреса є групою. Ви можете запросити лише окрему особу."; +/* common buttons */ +"OK" = "OK"; +"Cancel" = "Cancel"; +"Yes" = "Yes"; +"No" = "No"; + /* alarms */ "Reminder:" = "Нагадування:"; "Start:" = "Початок:"; diff --git a/UI/Common/Welsh.lproj/Localizable.strings b/UI/Common/Welsh.lproj/Localizable.strings index f1cc84433..bf0c34925 100644 --- a/UI/Common/Welsh.lproj/Localizable.strings +++ b/UI/Common/Welsh.lproj/Localizable.strings @@ -65,6 +65,12 @@ "delegate is a participant" = "The delegate is already a participant."; "delegate is a group" = "The specified address corresponds to a group. You can only delegate to a unique person."; +/* common buttons */ +"OK" = "OK"; +"Cancel" = "Cancel"; +"Yes" = "Yes"; +"No" = "No"; + /* alarms */ "Reminder:" = "Atgoffa:"; "Start:" = "Dechrau:"; diff --git a/UI/MailerUI/UIxMailAccountActions.h b/UI/MailerUI/UIxMailAccountActions.h index 72ccf6b6d..9e98ae5fb 100644 --- a/UI/MailerUI/UIxMailAccountActions.h +++ b/UI/MailerUI/UIxMailAccountActions.h @@ -35,7 +35,6 @@ NSString *trashFolderName; } -- (WOResponse *) statusFoldersAction; - (WOResponse *) listMailboxesAction; @end diff --git a/UI/MailerUI/UIxMailAccountActions.m b/UI/MailerUI/UIxMailAccountActions.m index 954ee4a23..c62275bcc 100644 --- a/UI/MailerUI/UIxMailAccountActions.m +++ b/UI/MailerUI/UIxMailAccountActions.m @@ -31,8 +31,6 @@ #import #import -#import - #import #import #import @@ -41,7 +39,6 @@ #import #import #import -#import #import "../Common/WODirectAction+SOGo.h" @@ -125,50 +122,6 @@ return folders; } -- (NSDictionary *) _statusFolders -{ - EOQualifier *searchQualifier; - NSArray *searchResult; - NSDictionary *imapResult; - NGImap4Client *client; - NSNumber *unseen; - SOGoMailFolder *inbox; - SOGoMailAccount *co; - - co = [self clientObject]; - inbox = [co inboxFolderInContext: context]; - client = [[inbox imap4Connection] client]; - unseen = nil; - - if ([client select: [inbox relativeImap4Name]]) - { - searchQualifier = [EOQualifier qualifierWithQualifierFormat: @"flags = %@ AND not flags = %@", @"unseen", @"deleted"]; - imapResult = [client searchWithQualifier: searchQualifier]; - searchResult = [[imapResult objectForKey: @"RawResponse"] objectForKey: @"search"]; - unseen = [NSNumber numberWithInt: [searchResult count]]; - } - - if (!unseen) - unseen = [NSNumber numberWithInt: 0]; - - return [NSDictionary dictionaryWithObjectsAndKeys: unseen, @"unseen", nil]; -} - -- (WOResponse *) statusFoldersAction -{ - WOResponse *response; - NSDictionary *data; - - response = [self responseWithStatus: 200]; - data = [self _statusFolders]; - - [response setHeader: @"text/plain; charset=utf-8" - forKey: @"content-type"]; - [response appendContentString: [data jsonRepresentation]]; - - return response; -} - - (WOResponse *) listMailboxesAction { SOGoMailAccount *co; @@ -215,13 +168,12 @@ // The parameter order is important here, as if the server doesn't support // quota, inboxQuota will be nil and it'll terminate the list of objects/keys. data = [NSDictionary dictionaryWithObjectsAndKeys: folders, @"mailboxes", - [self _statusFolders], @"status", inboxQuota, @"quotas", nil]; - response = [self responseWithStatus: 200]; - [response setHeader: @"text/plain; charset=utf-8" + response = [self responseWithStatus: 200 + andString: [data jsonRepresentation]]; + [response setHeader: @"application/json" forKey: @"content-type"]; - [response appendContentString: [data jsonRepresentation]]; return response; } @@ -232,7 +184,6 @@ { SOGoDraftsFolder *drafts; SOGoDraftObject *newDraftMessage; - SOGoUserDefaults *ud; NSString *urlBase, *url, *value, *signature; NSArray *mailTo; NSMutableDictionary *headers; @@ -262,8 +213,7 @@ if (save) [newDraftMessage setHeaders: headers]; - ud = [[context activeUser] userDefaults]; - signature = [ud mailSignature]; + signature = [[self clientObject] signature]; if ([signature length]) { [newDraftMessage diff --git a/UI/MailerUI/UIxMailEditor.m b/UI/MailerUI/UIxMailEditor.m index 87420f393..04c4e7446 100644 --- a/UI/MailerUI/UIxMailEditor.m +++ b/UI/MailerUI/UIxMailEditor.m @@ -347,19 +347,21 @@ static NSArray *infoKeys = nil; - (NSArray *) fromEMails { - NSArray *allIdentities; + NSArray *identities; int count, max; NSString *email; + SOGoMailAccount *account; if (!fromEMails) { - allIdentities = [[context activeUser] allIdentities]; - fromEMails = [NSMutableArray new]; - max = [allIdentities count]; + account = [[self clientObject] mailAccountFolder]; + identities = [account identities]; + max = [identities count]; + fromEMails = [[NSMutableArray alloc] initWithCapacity: max]; for (count = 0; count < max; count++) { email - = [self _emailFromIdentity: [allIdentities objectAtIndex: count]]; + = [self _emailFromIdentity: [identities objectAtIndex: count]]; [fromEMails addObjectUniquely: email]; } } diff --git a/UI/MailerUI/UIxMailFolderActions.m b/UI/MailerUI/UIxMailFolderActions.m index 4d0653287..1d530e236 100644 --- a/UI/MailerUI/UIxMailFolderActions.m +++ b/UI/MailerUI/UIxMailFolderActions.m @@ -24,6 +24,7 @@ #import #import #import +#import #import #import @@ -31,12 +32,15 @@ #import #import #import +#import -#import -#import #import +#import #import +#import + #import +#import #import #import #import @@ -351,26 +355,94 @@ return response; } +- (void) _setFolderPurposeOnMainAccount: (NSString *) purpose + inUserDefaults: (SOGoUserDefaults *) ud + to: (NSString *) value +{ + NSString *selName; + SEL setter; + + selName = [NSString stringWithFormat: @"set%@FolderName:", purpose]; + setter = NSSelectorFromString (selName); + [ud performSelector: setter withObject: value]; +} + +- (WOResponse *) _setFolderPurpose: (NSString *) purpose + onAuxAccount: (int) accountIdx + inUserDefaults: (SOGoUserDefaults *) ud + to: (NSString *) value +{ + NSArray *accounts; + int realIdx; + NSMutableDictionary *account, *mailboxes; + WOResponse *response; + + if (accountIdx > 0) + { + realIdx = accountIdx - 1; + accounts = [ud auxiliaryMailAccounts]; + if ([accounts count] > realIdx) + { + account = [accounts objectAtIndex: realIdx]; + mailboxes = [account objectForKey: @"mailboxes"]; + if (!mailboxes) + { + mailboxes = [NSMutableDictionary new]; + [account setObject: mailboxes forKey: @"mailboxes"]; + [mailboxes release]; + } + [mailboxes setObject: value forKey: purpose]; + [ud setAuxiliaryMailAccounts: accounts]; + response = [self responseWith204]; + } + else + response + = [self responseWithStatus: 500 + andString: @"You reached an impossible end."]; + } + else + response + = [self responseWithStatus: 500 + andString: @"You reached an impossible end."]; + + return response; +} + - (WOResponse *) _setFolderPurpose: (NSString *) purpose { SOGoMailFolder *co; WOResponse *response; - SOGoUserSettings *us; - NSMutableDictionary *mailSettings; + SOGoUser *owner; + SOGoUserDefaults *ud; + NSString *accountIdx, *traversal; co = [self clientObject]; - if ([NSStringFromClass ([co class]) isEqualToString: @"SOGoMailFolder"]) + if ([co isKindOfClass: [SOGoMailFolder class]]) { - us = [[context activeUser] userSettings]; - mailSettings = [us objectForKey: @"Mail"]; - if (!mailSettings) - mailSettings = [NSMutableDictionary dictionary]; - [us setObject: mailSettings forKey: @"Mail"]; - [mailSettings setObject: [co traversalFromMailAccount] - forKey: [NSString stringWithFormat: @"%@Folder", - purpose]]; - [us synchronize]; - response = [self responseWith204]; + accountIdx = [[co mailAccountFolder] nameInContainer]; + owner = [SOGoUser userWithLogin: [co ownerInContext: nil]]; + ud = [owner userDefaults]; + traversal = [co traversalFromMailAccount]; + if ([accountIdx isEqualToString: @"0"]) + { + /* default account: we directly set the corresponding pref in the ud + */ + [self _setFolderPurposeOnMainAccount: purpose + inUserDefaults: ud + to: traversal]; + response = [self responseWith204]; + } + else if ([[owner domainDefaults] mailAuxiliaryUserAccountsEnabled]) + response = [self _setFolderPurpose: purpose + onAuxAccount: [accountIdx intValue] + inUserDefaults: ud + to: traversal]; + else + response + = [self responseWithStatus: 500 + andString: @"You reached an impossible end."]; + if ([response status] == 204) + [ud synchronize]; } else { @@ -515,4 +587,51 @@ return response; } +- (NSDictionary *) _unseenCount +{ + EOQualifier *searchQualifier; + NSArray *searchResult; + NSDictionary *imapResult; + NGImap4Connection *connection; + NGImap4Client *client; + int unseen; + SOGoMailFolder *folder; + + folder = [self clientObject]; + + connection = [folder imap4Connection]; + client = [connection client]; + + if ([connection selectFolder: [folder imap4URL]]) + { + searchQualifier + = [EOQualifier qualifierWithQualifierFormat: @"flags = %@ AND not flags = %@", + @"unseen", @"deleted"]; + imapResult = [client searchWithQualifier: searchQualifier]; + searchResult = [[imapResult objectForKey: @"RawResponse"] objectForKey: @"search"]; + unseen = [searchResult count]; + } + else + unseen = 0; + + return [NSDictionary + dictionaryWithObject: [NSNumber numberWithInt: unseen] + forKey: @"unseen"]; +} + +- (WOResponse *) unseenCountAction +{ + WOResponse *response; + NSDictionary *data; + + response = [self responseWithStatus: 200]; + data = [self _unseenCount]; + + [response setHeader: @"text/plain; charset=utf-8" + forKey: @"content-type"]; + [response appendContentString: [data jsonRepresentation]]; + + return response; +} + @end diff --git a/UI/MailerUI/UIxMailMainFrame.m b/UI/MailerUI/UIxMailMainFrame.m index fc8aabb59..abca1d46c 100644 --- a/UI/MailerUI/UIxMailMainFrame.m +++ b/UI/MailerUI/UIxMailMainFrame.m @@ -103,33 +103,12 @@ - (NSString *) mailAccounts { - SOGoMailAccounts *accounts; - NSDictionary *accountKeys; - NSArray *keys, *entry; - NSMutableArray *values; - NSString *key; - int i, max; + NSArray *accounts, *names; - accounts = [self clientObject]; - accountKeys = [accounts accountKeys]; - keys = [accountKeys allKeys]; - values = [NSMutableArray array]; + accounts = [[self clientObject] mailAccounts]; + names = [accounts objectsForKey: @"name" notFoundMarker: nil]; - max = [keys count]; - for (i = 0; i < max; i++) - { - key = [keys objectAtIndex: i]; - entry = [NSArray arrayWithObjects: key, [accountKeys objectForKey: key], nil]; - [values addObject: entry]; - } - - return [values jsonRepresentation]; -} - -- (NSString *) defaultColumnsOrder -{ - return [[[self columnsDisplayOrder] objectsForKey: @"value" - notFoundMarker: @""] jsonRepresentation]; + return [names jsonRepresentation]; } - (NSString *) pageFormURL @@ -182,21 +161,18 @@ SOGoMailAccounts *accounts; SOGoMailAccount *account; SOGoMailFolder *inbox; - - NSString *firstAccount; NSDictionary *data; SOGoUser *activeUser; UIxMailListActions *actions; [self _setupContext]; +#warning this code is dirty: we should not invoke UIxMailListActions from here! actions = [[[UIxMailListActions new] initWithRequest: [context request]] autorelease]; activeUser = [context activeUser]; accounts = [self clientObject]; - firstAccount = [[[accounts accountKeys] allKeys] - objectAtIndex: 0]; - account = [accounts lookupName: firstAccount inContext: context acquire: NO]; + account = [accounts lookupName: @"0" inContext: context acquire: NO]; inbox = [account inboxFolderInContext: context]; data = [actions getUIDsAndHeadersInFolder: inbox]; @@ -207,8 +183,8 @@ - (id ) composeAction { id contact; - NSArray *accounts, *contactsId, *cards; - NSString *firstAccount, *firstEscapedAccount, *newLocation, *parameters, *folderId, *uid, *formattedMail; + NSArray *contactsId, *cards; + NSString *newLocation, *parameters, *folderId, *uid, *formattedMail; NSEnumerator *uids; NSMutableArray *addresses; NGVCard *card; @@ -222,11 +198,6 @@ parameters = nil; co = [self clientObject]; - // We use the first mail account - accounts = [[context activeUser] mailAccounts]; - firstAccount = [[accounts objectsForKey: @"name" notFoundMarker: nil] - objectAtIndex: 0]; - firstEscapedAccount = [firstAccount asCSSIdentifier]; request = [context request]; if ((folderId = [request formValueForKey: @"folder"]) && @@ -290,9 +261,8 @@ // No parameter passed; simply open the compose window parameters = @"?mailto="; - newLocation = [NSString stringWithFormat: @"%@/%@/compose%@", + newLocation = [NSString stringWithFormat: @"%@/0/compose%@", [co baseURLInContext: context], - firstEscapedAccount, parameters]; return [self redirectToLocation: newLocation]; @@ -643,5 +613,14 @@ return [self labelForKey: [currentColumn objectForKey: @"value"]]; } +- (NSString *) getUnseenCountForAllFolders +{ + SOGoDomainDefaults *dd; + + dd = [[context activeUser] domainDefaults]; + + return ([dd mailCheckAllUnseenCounts] ? @"true" : @"false"); +} + @end /* UIxMailMainFrame */ diff --git a/UI/MailerUI/product.plist b/UI/MailerUI/product.plist index 5429ca2bc..abd4c0c67 100644 --- a/UI/MailerUI/product.plist +++ b/UI/MailerUI/product.plist @@ -171,6 +171,11 @@ protectedBy = "ReadAcls"; pageName = "UIxMailUserRightsEditor"; }; + unseenCount = { + protectedBy = "View"; + actionClass = "UIxMailFolderActions"; + actionName = "unseenCount"; + }; saveUserRights = { protectedBy = "Change Permissions"; pageName = "UIxMailUserRightsEditor"; @@ -383,11 +388,6 @@ actionClass = "UIxMailFolderActions"; actionName = "createFolder"; }; - statusFolders = { - protectedBy = "View"; - actionClass = "UIxMailAccountActions"; - actionName = "statusFolders"; - }; }; }; diff --git a/UI/PreferencesUI/BrazilianPortuguese.lproj/Localizable.strings b/UI/PreferencesUI/BrazilianPortuguese.lproj/Localizable.strings index f771cad8d..5bd1d5c22 100644 --- a/UI/PreferencesUI/BrazilianPortuguese.lproj/Localizable.strings +++ b/UI/PreferencesUI/BrazilianPortuguese.lproj/Localizable.strings @@ -6,10 +6,9 @@ "General" = "Geral"; "Calendar Options" = "Calendário"; "Mail Options" = "Correio"; -"Signature" = "Assinatura"; +"IMAP Accounts" = "IMAP Accounts"; "Vacation" = "Vacation"; "Forward" = "Forward"; -"Identities" = "Identidade"; "Password" = "Senha"; "Categories" = "Categories"; "Name" = "Name"; @@ -119,9 +118,6 @@ "messageforward_inline" = "No corpo da mensagem"; "messageforward_attached" = "Como anexo"; -/* Identities */ -"Default identity:" = "Identidade Padrão:"; -"Manage identities..." = "Gerenciar Identidades..."; "replyplacement_above" = "Começar minha resposta acima das citações"; "replyplacement_below" = "Começar minha resposta abaixo das citações"; "And place my signature" = "E colocar minha assinatura"; @@ -131,7 +127,21 @@ "composemessagestype_html" = "HTML"; "composemessagestype_text" = "Plain text"; -"composeMessageChanged" = "Changing your messages composition type requires to save your preferences immediately. Continue?"; +/* IMAP Accounts */ +"New Mail Account" = "New Mail Account"; + +"Server Name:" = "Server Name:"; +"Port:" = "Port:"; +"User Name:" = "User Name:"; +"Password:" = "Senha:"; + +"Full Name:" = "Full Name:"; +"Email:" = "Email:"; +"Signature:" = "Assinatura:"; +"(Click to create)" = "(Click to create)"; + +"Signature" = "Assinatura"; +"Please enter your signature below:" = "Please enter your signature below:"; /* Additional Parameters */ "Additional Parameters" = "Parâmetros Adicionais"; diff --git a/UI/PreferencesUI/Czech.lproj/Localizable.strings b/UI/PreferencesUI/Czech.lproj/Localizable.strings index 123d898c2..874d93082 100644 --- a/UI/PreferencesUI/Czech.lproj/Localizable.strings +++ b/UI/PreferencesUI/Czech.lproj/Localizable.strings @@ -6,10 +6,9 @@ "General" = "Obecný"; "Calendar Options" = "Možnosti kalendáře"; "Mail Options" = "Možnosti pošty"; -"Signature" = "Podpis"; +"IMAP Accounts" = "IMAP Accounts"; "Vacation" = "Nepřítomnost"; "Forward" = "Přeposílání"; -"Identities" = "Identity"; "Password" = "Heslo"; "Categories" = "Kategorie"; "Name" = "Jméno"; @@ -119,9 +118,6 @@ "messageforward_inline" = "Do řady"; "messageforward_attached" = "Jako přílohu"; -/* Identities */ -"Default identity:" = "Výchozí identita:"; -"Manage identities..." = "Spravovat identity..."; "replyplacement_above" = "Začít mojí odpověď nad citací"; "replyplacement_below" = "Začít mojí odpověď pod citací"; "And place my signature" = "A umístit můj podpis"; @@ -131,7 +127,21 @@ "composemessagestype_html" = "HTML"; "composemessagestype_text" = "Prostý text"; -"composeMessageChanged" = "Změna formátu vytváření zprávy vyžaduje okamžité uložení předvoleb. Pokračovat?"; +/* IMAP Accounts */ +"New Mail Account" = "New Mail Account"; + +"Server Name:" = "Server Name:"; +"Port:" = "Port:"; +"User Name:" = "User Name:"; +"Password:" = "Heslo:"; + +"Full Name:" = "Full Name:"; +"Email:" = "Email:"; +"Signature:" = "Podpis:"; +"(Click to create)" = "(Click to create)"; + +"Signature" = "Podpis"; +"Please enter your signature below:" = "Please enter your signature below:"; /* Additional Parameters */ "Additional Parameters" = "Dodatečné parametry"; diff --git a/UI/PreferencesUI/Dutch.lproj/Localizable.strings b/UI/PreferencesUI/Dutch.lproj/Localizable.strings index c98f57615..0fb6ea593 100644 --- a/UI/PreferencesUI/Dutch.lproj/Localizable.strings +++ b/UI/PreferencesUI/Dutch.lproj/Localizable.strings @@ -6,10 +6,9 @@ "General" = "Algemeen"; "Calendar Options" = "Agenda"; "Mail Options" = "E-mail"; -"Signature" = "Ondertekening"; +"IMAP Accounts" = "IMAP Accounts"; "Vacation" = "Vacation"; "Forward" = "Forward"; -"Identities" = "Identiteit"; "Password" = "Wachtwoord"; "Categories" = "Categories"; "Name" = "Name"; @@ -119,9 +118,6 @@ "messageforward_inline" = "In het bericht"; "messageforward_attached" = "Als bijlage"; -/* Identities */ -"Default identity:" = "Standaardidentiteit:"; -"Manage identities..." = "Identiteiten beheren..."; "replyplacement_above" = "reactie boven orgineletekst"; "replyplacement_below" = "reactie onder orgineletekst"; "And place my signature" = "Ondertekening plaatsen"; @@ -131,7 +127,21 @@ "composemessagestype_html" = "HTML"; "composemessagestype_text" = "Plain text"; -"composeMessageChanged" = "Changing your messages composition type requires to save your preferences immediately. Continue?"; +/* IMAP Accounts */ +"New Mail Account" = "New Mail Account"; + +"Server Name:" = "Server Name:"; +"Port:" = "Port:"; +"User Name:" = "User Name:"; +"Password:" = "Wachtwoord:"; + +"Full Name:" = "Full Name:"; +"Email:" = "Email:"; +"Signature:" = "Ondertekening:"; +"(Click to create)" = "(Click to create)"; + +"Signature" = "Ondertekening"; +"Please enter your signature below:" = "Please enter your signature below:"; /* Additional Parameters */ "Additional Parameters" = "Extra Parameters"; diff --git a/UI/PreferencesUI/English.lproj/Localizable.strings b/UI/PreferencesUI/English.lproj/Localizable.strings index 5487450a5..e28519dee 100644 --- a/UI/PreferencesUI/English.lproj/Localizable.strings +++ b/UI/PreferencesUI/English.lproj/Localizable.strings @@ -6,10 +6,9 @@ "General" = "General"; "Calendar Options" = "Calendar Options"; "Mail Options" = "Mail Options"; -"Signature" = "Signature"; +"IMAP Accounts" = "IMAP Accounts"; "Vacation" = "Vacation"; "Forward" = "Forward"; -"Identities" = "Identities"; "Password" = "Password"; "Categories" = "Categories"; "Name" = "Name"; @@ -119,9 +118,6 @@ "messageforward_inline" = "Inline"; "messageforward_attached" = "As Attachment"; -/* Identities */ -"Default identity:" = "Default identity:"; -"Manage identities..." = "Manage identities..."; "replyplacement_above" = "Start my reply above the quote"; "replyplacement_below" = "Start my reply below the quote"; "And place my signature" = "And place my signature"; @@ -131,7 +127,21 @@ "composemessagestype_html" = "HTML"; "composemessagestype_text" = "Plain text"; -"composeMessageChanged" = "Changing your messages composition type requires to save your preferences immediately. Continue?"; +/* IMAP Accounts */ +"New Mail Account" = "New Mail Account"; + +"Server Name:" = "Server Name:"; +"Port:" = "Port:"; +"User Name:" = "User Name:"; +"Password:" = "Password:"; + +"Full Name:" = "Full Name:"; +"Email:" = "Email:"; +"Signature:" = "Signature:"; +"(Click to create)" = "(Click to create)"; + +"Signature" = "Signature"; +"Please enter your signature below:" = "Please enter your signature below:"; /* Additional Parameters */ "Additional Parameters" = "Additional Parameters"; diff --git a/UI/PreferencesUI/French.lproj/Localizable.strings b/UI/PreferencesUI/French.lproj/Localizable.strings index c7dba8922..1fc3a277a 100644 --- a/UI/PreferencesUI/French.lproj/Localizable.strings +++ b/UI/PreferencesUI/French.lproj/Localizable.strings @@ -6,10 +6,9 @@ "General" = "Général"; "Calendar Options" = "Calendrier"; "Mail Options" = "Courrier"; -"Signature" = "Signature"; +"IMAP Accounts" = "Comptes IMAP"; "Vacation" = "Absence prolongée"; "Forward" = "Transfert"; -"Identities" = "Identités"; "Password" = "Mot de passe"; "Categories" = "Catégories"; "Name" = "Nom"; @@ -119,9 +118,6 @@ "messageforward_inline" = "intégrés"; "messageforward_attached" = "en pièces jointes"; -/* Identities */ -"Default identity:" = "Identité par défaut :"; -"Manage identities..." = "Gérer les identitiés..."; "replyplacement_above" = "Placer ma réponse avant la citation"; "replyplacement_below" = "Placer ma réponse après la citation"; "And place my signature" = "Et placer ma signature"; @@ -131,7 +127,21 @@ "composemessagestype_html" = "HTML"; "composemessagestype_text" = "Texte"; -"composeMessageChanged" = "Changer la composition des messages nécessite d'enregistrer immédiatement vos préférences.\nVoulez-vous continuer?"; +/* IMAP Accounts */ +"New Mail Account" = "Nouveau compte"; + +"Server Name:" = "Serveur :"; +"Port:" = "Port :"; +"User Name:" = "Utilisateur :"; +"Password:" = "Mot de passe :"; +"(Click to create)" = "(Signature vide)"; + +"Full Name:" = "Nom complet :"; +"Email:" = "Email :"; +"Signature:" = "Signature :"; + +"Signature" = "Signature"; +"Please enter your signature below:" = "Introduisez votre signature ci-dessous :"; /* Additional Parameters */ "Additional Parameters" = "Paramètres supplémentaires"; diff --git a/UI/PreferencesUI/German.lproj/Localizable.strings b/UI/PreferencesUI/German.lproj/Localizable.strings index 93e792eaf..f3c93febb 100644 --- a/UI/PreferencesUI/German.lproj/Localizable.strings +++ b/UI/PreferencesUI/German.lproj/Localizable.strings @@ -6,10 +6,9 @@ "General" = "Allgemein"; "Calendar Options" = "Kalender"; "Mail Options" = "E-Mail"; -"Signature" = "Signatur"; +"IMAP Accounts" = "IMAP Accounts"; "Vacation" = "Vacation"; "Forward" = "Forward"; -"Identities" = "Identität"; "Password" = "Passwort"; "Categories" = "Kategorien"; "Name" = "Name"; @@ -119,9 +118,6 @@ "messageforward_inline" = "Eingebunden"; "messageforward_attached" = "Als Anhang"; -/* Identities */ -"Default identity:" = "Standard Identität:"; -"Manage identities..." = "Identitäten verwalten..."; "replyplacement_above" = "Antwort oberhalb"; "replyplacement_below" = "Antwort unterhalb"; "And place my signature" = "Und setze meine Signatur"; @@ -131,7 +127,21 @@ "composemessagestype_html" = "HTML"; "composemessagestype_text" = "Reintext"; -"composeMessageChanged" = "Das Ändern des Typs erfordert das sofortige Speichern der Einstellungen. Fortfahren?"; +/* IMAP Accounts */ +"New Mail Account" = "New Mail Account"; + +"Server Name:" = "Server Name:"; +"Port:" = "Port:"; +"User Name:" = "User Name:"; +"Password:" = "Passwort:"; + +"Full Name:" = "Full Name:"; +"Email:" = "Email:"; +"Signature:" = "Signatur:"; +"(Click to create)" = "(Click to create)"; + +"Signature" = "Signatur"; +"Please enter your signature below:" = "Please enter your signature below:"; /* Additional Parameters */ "Additional Parameters" = "Zusätzliche Einstellungen"; diff --git a/UI/PreferencesUI/Hungarian.lproj/Localizable.strings b/UI/PreferencesUI/Hungarian.lproj/Localizable.strings index 65e422008..a4ffe0e4d 100644 --- a/UI/PreferencesUI/Hungarian.lproj/Localizable.strings +++ b/UI/PreferencesUI/Hungarian.lproj/Localizable.strings @@ -6,7 +6,7 @@ "General" = "Általános"; "Calendar Options" = "Naptár"; "Mail Options" = "Levelezés"; -"Identities" = "Identitások"; +"IMAP Accounts" = "IMAP Accounts"; "Password" = "Jelszó"; "Categories" = "Categories"; "Name" = "Name"; @@ -102,10 +102,6 @@ "messageforward_inline" = "Levélként"; "messageforward_attached" = "Mellékletként"; -/* Identities */ -"Default identity:" = "Alapértelmezett identitás:"; -"Manage identities..." = "Identitások kezelése..."; -"Signature" = "Aláírás"; "replyplacement_above" = "Válasz elhelyezése az idézet fölött"; "replyplacement_below" = "Válasz elhelyezése az idézet alatt"; "And place my signature" = "Aláírás beszúrása"; @@ -115,7 +111,21 @@ "composemessagestype_html" = "HTML"; "composemessagestype_text" = "Egyszerű szöveg"; -"composeMessageChanged" = "Changing your messages composition type requires to save your preferences immediately. Continue?"; +/* IMAP Accounts */ +"New Mail Account" = "New Mail Account"; + +"Server Name:" = "Server Name:"; +"Port:" = "Port:"; +"User Name:" = "User Name:"; +"Password:" = "Jelszó:"; + +"Full Name:" = "Full Name:"; +"Email:" = "Email:"; +"Signature:" = "Aláírás:"; +"(Click to create)" = "(Click to create)"; + +"Signature" = "Aláírás"; +"Please enter your signature below:" = "Please enter your signature below:"; /* Additional Parameters */ "Additional Parameters" = "További beállítások"; diff --git a/UI/PreferencesUI/Italian.lproj/Localizable.strings b/UI/PreferencesUI/Italian.lproj/Localizable.strings index 53e40bd47..0795ca1af 100644 --- a/UI/PreferencesUI/Italian.lproj/Localizable.strings +++ b/UI/PreferencesUI/Italian.lproj/Localizable.strings @@ -6,10 +6,9 @@ "General" = "Generale"; "Calendar Options" = "Opzioni calendario"; "Mail Options" = "Opzioni di posta"; -"Signature" = "Firma"; +"IMAP Accounts" = "IMAP Accounts"; "Vacation" = "Vacation"; "Forward" = "Forward"; -"Identities" = "Identità"; "Password" = "Password"; "Categories" = "Categories"; "Name" = "Name"; @@ -119,9 +118,6 @@ "messageforward_inline" = "Parte del messaggio"; "messageforward_attached" = "Allegato"; -/* Identities */ -"Default identity:" = "Identità principale:"; -"Manage identities..." = "Gestisci identità..."; "replyplacement_above" = "Inizia la risposta sopra il testo a cui si risponde"; "replyplacement_below" = "Inizia la risposta sotto il testo a cui si risponde"; "And place my signature" = "Metti la firma"; @@ -131,7 +127,21 @@ "composemessagestype_html" = "HTML"; "composemessagestype_text" = "Plain text"; -"composeMessageChanged" = "Changing your messages composition type requires to save your preferences immediately. Continue?"; +/* IMAP Accounts */ +"New Mail Account" = "New Mail Account"; + +"Server Name:" = "Server Name:"; +"Port:" = "Port:"; +"User Name:" = "User Name:"; +"Password:" = "Password:"; + +"Full Name:" = "Full Name:"; +"Email:" = "Email:"; +"Signature:" = "Firma:"; +"(Click to create)" = "(Click to create)"; + +"Signature" = "Firma"; +"Please enter your signature below:" = "Please enter your signature below:"; /* Additional Parameters */ "Additional Parameters" = "Parametri addizionali"; diff --git a/UI/PreferencesUI/Russian.lproj/Localizable.strings b/UI/PreferencesUI/Russian.lproj/Localizable.strings index fbd2410eb..89126d5e1 100644 --- a/UI/PreferencesUI/Russian.lproj/Localizable.strings +++ b/UI/PreferencesUI/Russian.lproj/Localizable.strings @@ -6,10 +6,9 @@ "General" = "Общее"; "Calendar Options" = "Календарь"; "Mail Options" = "Почта"; -"Signature" = "Подпись"; +"IMAP Accounts" = "IMAP Accounts"; "Vacation" = "Vacation"; "Forward" = "Forward"; -"Identities" = "Identities"; "Password" = "Пароль"; "Categories" = "Categories"; "Name" = "Name"; @@ -124,9 +123,6 @@ "messageforward_inline" = "В теле письма"; "messageforward_attached" = "Приложенным файлом"; -/* Identities */ -"Default identity:" = "Обысно я представляюсь как:"; -"Manage identities..." = "Управлять именами..."; "replyplacement_above" = "Начинать мой ответ над цитируемым текстом"; "replyplacement_below" = "Начинать мой ответ под цитируемым текстом"; "And place my signature" = "И поместить мою подпись"; @@ -136,7 +132,21 @@ "composemessagestype_html" = "HTML"; "composemessagestype_text" = "Plain text"; -"composeMessageChanged" = "Changing your messages composition type requires to save your preferences immediately. Continue?"; +/* IMAP Accounts */ +"New Mail Account" = "New Mail Account"; + +"Server Name:" = "Server Name:"; +"Port:" = "Port:"; +"User Name:" = "User Name:"; +"Password:" = "Пароль:"; + +"Full Name:" = "Full Name:"; +"Email:" = "Email:"; +"Signature:" = "Подпись:"; +"(Click to create)" = "(Click to create)"; + +"Signature" = "Подпись"; +"Please enter your signature below:" = "Please enter your signature below:"; /* Additional Parameters */ "Additional Parameters" = "Дополнительные параметры"; diff --git a/UI/PreferencesUI/Spanish.lproj/Localizable.strings b/UI/PreferencesUI/Spanish.lproj/Localizable.strings index 5d04e1a9f..e5f0adafe 100644 --- a/UI/PreferencesUI/Spanish.lproj/Localizable.strings +++ b/UI/PreferencesUI/Spanish.lproj/Localizable.strings @@ -6,10 +6,9 @@ "General" = "General"; "Calendar Options" = "Opciones de calendario"; "Mail Options" = "Opciones de correo"; -"Signature" = "Firma"; +"IMAP Accounts" = "IMAP Accounts"; "Vacation" = "Vacation"; "Forward" = "Forward"; -"Identities" = "Identidades"; "Password" = "Contraseña"; "Categories" = "Categories"; "Name" = "Name"; @@ -121,9 +120,6 @@ "messageforward_inline" = "Incorporado"; "messageforward_attached" = "Como adjunto"; -/* Identities */ -"Default identity:" = "Identidad por defecto:"; -"Manage identities..." = "Gestionar identidades..."; "replyplacement_above" = "replyplacement_above"; "replyplacement_below" = "replyplacement_below"; "And place my signature" = "And place my signature"; @@ -133,7 +129,21 @@ "composemessagestype_html" = "HTML"; "composemessagestype_text" = "Plain text"; -"composeMessageChanged" = "Changing your messages composition type requires to save your preferences immediately. Continue?"; +/* IMAP Accounts */ +"New Mail Account" = "New Mail Account"; + +"Server Name:" = "Server Name:"; +"Port:" = "Port:"; +"User Name:" = "User Name:"; +"Password:" = "Contraseña:"; + +"Full Name:" = "Full Name:"; +"Email:" = "Email:"; +"Signature:" = "Firma:"; +"(Click to create)" = "(Click to create)"; + +"Signature" = "Firma"; +"Please enter your signature below:" = "Please enter your signature below:"; /* Additional Parameters */ "Additional Parameters" = "Additional Parameters"; diff --git a/UI/PreferencesUI/Swedish.lproj/Localizable.strings b/UI/PreferencesUI/Swedish.lproj/Localizable.strings index bdd4f37ad..48d43dd7b 100644 --- a/UI/PreferencesUI/Swedish.lproj/Localizable.strings +++ b/UI/PreferencesUI/Swedish.lproj/Localizable.strings @@ -6,10 +6,9 @@ "General" = "Allmänt"; "Calendar Options" = "Kalenderinställningar"; "Mail Options" = "E-postinställningar"; -"Signature" = "Signatur"; +"IMAP Accounts" = "IMAP Accounts"; "Vacation" = "Frånvaro"; "Forward" = "Vidarebefordring"; -"Identities" = "Identiteter"; "Password" = "Lösenord"; "Categories" = "Kategorier"; "Name" = "Namn"; @@ -119,9 +118,6 @@ "messageforward_inline" = "Infogade"; "messageforward_attached" = "Bifogade"; -/* Identities */ -"Default identity:" = "Standardidentitet:"; -"Manage identities..." = "Redigera identiteter..."; "replyplacement_above" = "Börja mitt svar ovanför det infogade meddelandet"; "replyplacement_below" = "Börja mitt svar under det infogade meddelandet"; "And place my signature" = "Lägg till min signatur"; @@ -131,7 +127,21 @@ "composemessagestype_html" = "HTML"; "composemessagestype_text" = "Oformaterad text"; -"composeMessageChanged" = "Ändring av din meddelandekodning sparar dina inställingar direkt. Fortsätt?"; +/* IMAP Accounts */ +"New Mail Account" = "New Mail Account"; + +"Server Name:" = "Server Name:"; +"Port:" = "Port:"; +"User Name:" = "User Name:"; +"Password:" = "Lösenord:"; + +"Full Name:" = "Full Name:"; +"Email:" = "Email:"; +"Signature:" = "Signatur:"; +"(Click to create)" = "(Click to create)"; + +"Signature" = "Signatur"; +"Please enter your signature below:" = "Please enter your signature below:"; /* Additional Parameters */ "Additional Parameters" = "Övriga parametrar"; diff --git a/UI/PreferencesUI/UIxFilterEditor.m b/UI/PreferencesUI/UIxFilterEditor.m index 894728afe..28ce6c231 100644 --- a/UI/PreferencesUI/UIxFilterEditor.m +++ b/UI/PreferencesUI/UIxFilterEditor.m @@ -80,26 +80,4 @@ return filterId; } -- (NSString *) firstMailAccount -{ - NSArray *accounts; - NSDictionary *account; - NSString *login, *accountName; - SOGoUser *ownerUser; - - login = [[self clientObject] nameInContainer]; - ownerUser = [SOGoUser userWithLogin: login]; - - accounts = [ownerUser mailAccounts]; - if ([accounts count] > 0) - { - account = [accounts objectAtIndex: 0]; - accountName = [[account objectForKey: @"name"] asCSSIdentifier]; - } - else - accountName = @""; - - return accountName; -} - @end diff --git a/UI/PreferencesUI/UIxPreferences.m b/UI/PreferencesUI/UIxPreferences.m index e63ac7514..928c69956 100644 --- a/UI/PreferencesUI/UIxPreferences.m +++ b/UI/PreferencesUI/UIxPreferences.m @@ -67,6 +67,7 @@ if ((self = [super init])) { item = nil; +#warning user should be the owner rather than the activeUser ASSIGN (user, [context activeUser]); ASSIGN (userDefaults, [user userDefaults]); ASSIGN (today, [NSCalendarDate date]); @@ -575,35 +576,6 @@ [userDefaults setMailMessageForwarding: newMessageForwarding]; } -/* -// -- (NSArray *) identitiesList -{ - NSDictionary *primaryAccount; - -#warning we manage only one account per user at this time... - primaryAccount = [[user mailAccounts] objectAtIndex: 0]; - - return [primaryAccount objectForKey: @"identities"]; -} - -- (NSString *) itemIdentityText -{ - return [(NSDictionary *) item keysWithFormat: @"%{fullName} <%{email}>"]; - } */ - -- (NSString *) signature -{ - return [userDefaults mailSignature]; -} - -- (void) setSignature: (NSString *) newSignature -{ - [userDefaults setMailSignature: newSignature]; -} - - (NSArray *) replyPlacementList { return [NSArray arrayWithObjects: @"above", @"below", nil]; @@ -676,7 +648,7 @@ - (NSString *) sieveCapabilities { -#warning this should be deduced from the server +#warning sieve caps should be deduced from the server static NSArray *capabilities = nil; if (!capabilities) @@ -931,8 +903,7 @@ if ([[request method] isEqualToString: @"POST"]) { SOGoMailAccount *account; - id mailAccounts; - id folder; + SOGoMailAccounts *folder; dd = [[context activeUser] domainDefaults]; if ([dd sieveScriptsEnabled]) @@ -943,13 +914,10 @@ [userDefaults setForwardOptions: forwardOptions]; [userDefaults synchronize]; - - mailAccounts = [[[context activeUser] mailAccounts] objectAtIndex: 0]; + folder = [[self clientObject] mailAccountsFolder: @"Mail" inContext: context]; - account = [folder lookupName: [[mailAccounts objectForKey: @"name"] asCSSIdentifier] - inContext: context - acquire: NO]; + account = [folder lookupName: @"0" inContext: context acquire: NO]; [account updateFilters]; if (hasChanged) @@ -1093,4 +1061,221 @@ return [self labelForKey: item]; } +- (BOOL) mailAuxiliaryUserAccountsEnabled +{ + return [[user domainDefaults] mailAuxiliaryUserAccountsEnabled]; +} + +- (void) _extractMainSignature: (NSDictionary *) account +{ + /* We perform some validation here as we have no guaranty on the input + validity. */ + NSString *signature; + NSArray *identities; + NSDictionary *identity; + + if ([account isKindOfClass: [NSDictionary class]]) + { + identities = [account objectForKey: @"identities"]; + if ([identities isKindOfClass: [NSArray class]]) + { + signature = nil; + + if ([identities count] > 0) + { + identity = [identities objectAtIndex: 0]; + if ([identity isKindOfClass: [NSDictionary class]]) + { + signature = [identity objectForKey: @"signature"]; + if (!signature) + signature = @""; + [userDefaults setMailSignature: signature]; + } + } + } + } +} + +- (BOOL) _validateAccountIdentities: (NSArray *) identities +{ + static NSString *identityKeys[] = { @"fullName", @"email", nil }; + static NSArray *knownKeys = nil; + NSString **key, *value; + NSDictionary *identity; + NSMutableDictionary *clone; + BOOL valid; + int count, max; + + if (!knownKeys) + { + knownKeys = [NSArray arrayWithObjects: @"fullName", @"email", + @"signature", nil]; + [knownKeys retain]; + } + + valid = [identities isKindOfClass: [NSArray class]]; + if (valid) + { + max = [identities count]; + valid = (max > 0); + for (count = 0; valid && count < max; count++) + { + identity = [identities objectAtIndex: count]; + clone = [identity mutableCopy]; + [clone removeObjectsForKeys: knownKeys]; + valid = ([clone count] == 0); + [clone autorelease]; + if (valid) + { + key = identityKeys; + while (valid && *key) + { + value = [identity objectForKey: *key]; + if ([value isKindOfClass: [NSString class]] + && [value length] > 0) + key++; + else + valid = NO; + } + if (valid) + { + value = [identity objectForKey: @"signature"]; + valid = (!value || [value isKindOfClass: [NSString class]]); + } + } + } + } + + return valid; +} + +- (BOOL) _validateAccount: (NSDictionary *) account +{ + static NSString *accountKeys[] = { @"name", @"serverName", @"userName", + nil }; + static NSArray *knownKeys = nil; + NSMutableDictionary *clone; + NSString **key, *value; + BOOL valid; + + if (!knownKeys) + { + knownKeys = [NSArray arrayWithObjects: @"name", @"serverName", + @"userName", @"password", @"encryption", + @"identities", @"mailboxes", nil]; + [knownKeys retain]; + } + + valid = [account isKindOfClass: [NSDictionary class]]; + if (valid) + { + clone = [account mutableCopy]; + [clone removeObjectsForKeys: knownKeys]; + valid = ([clone count] == 0); + [clone autorelease]; + + key = accountKeys; + while (valid && *key) + { + value = [account objectForKey: *key]; + if ([value isKindOfClass: [NSString class]] + && [value length] > 0) + key++; + else + valid = NO; + } + + if (valid) + { + value = [account objectForKey: @"security"]; + if (value) + valid = ([value isKindOfClass: [NSString class]] + && ([value isEqualToString: @"none"] + || [value isEqualToString: @"ssl"] + || [value isEqualToString: @"tls"])); + + valid &= [self _validateAccountIdentities: [account objectForKey: @"identities"]]; + } + } + + return valid; +} + +- (void) _extractAuxiliaryAccounts: (NSArray *) accounts +{ + int count, max, oldMax; + NSArray *oldAccounts; + NSMutableArray *auxAccounts; + NSDictionary *oldAccount; + NSMutableDictionary *account; + NSString *password; + + oldAccounts = [user mailAccounts]; + oldMax = [oldAccounts count]; + + max = [accounts count]; + auxAccounts = [NSMutableArray arrayWithCapacity: max]; + + for (count = 1; count < max; count++) + { + account = [accounts objectAtIndex: count]; + if ([self _validateAccount: account]) + { + password = [account objectForKey: @"password"]; + if (!password) + { + if (count < oldMax) + { + oldAccount = [oldAccounts objectAtIndex: count]; + password = [oldAccount objectForKey: @"password"]; + } + if (!password) + password = @""; + [account setObject: password forKey: @"password"]; + } + [auxAccounts addObject: account]; + } + } + + [userDefaults setAuxiliaryMailAccounts: auxAccounts]; +} + +- (void) setMailAccounts: (NSString *) newMailAccounts +{ + NSArray *accounts; + NSScanner *scanner; + int max; + + scanner = [NSScanner scannerWithString: newMailAccounts]; + [scanner scanJSONArray: &accounts]; + if (accounts && [accounts isKindOfClass: [NSArray class]]) + { + max = [accounts count]; + if (max > 0) + { + [self _extractMainSignature: [accounts objectAtIndex: 0]]; + + if ([self mailAuxiliaryUserAccountsEnabled]) + [self _extractAuxiliaryAccounts: accounts]; + } + } +} + +- (NSString *) mailAccounts +{ + NSArray *accounts; + NSMutableDictionary *account; + int count, max; + + accounts = [user mailAccounts]; + max = [accounts count]; + for (count = 0; count < max; count++) + { + account = [accounts objectAtIndex: count]; + [account removeObjectForKey: @"password"]; + } + + return [accounts jsonRepresentation]; +} + @end diff --git a/UI/PreferencesUI/Ukrainian.lproj/Localizable.strings b/UI/PreferencesUI/Ukrainian.lproj/Localizable.strings index 3499b141d..84f205f2d 100644 --- a/UI/PreferencesUI/Ukrainian.lproj/Localizable.strings +++ b/UI/PreferencesUI/Ukrainian.lproj/Localizable.strings @@ -6,10 +6,9 @@ "General" = "Загальне"; "Calendar Options" = "Календар"; "Mail Options" = "Пошта"; -"Signature" = "Підпис"; +"IMAP Accounts" = "IMAP Accounts"; "Vacation" = "Відпустка"; "Forward" = "Перенаправлення"; -"Identities" = "Облікові записи"; "Password" = "Пароль"; "Categories" = "Категорії"; "Name" = "Назва"; @@ -121,9 +120,6 @@ "messageforward_inline" = "В тілі листа"; "messageforward_attached" = "Вкладеним файлом"; -/* Identities */ -"Default identity:" = "Зазвичай я представляюсь як:"; -"Manage identities..." = "Керувати іменами..."; "replyplacement_above" = "Починати мою відповідь над текстом, що цитується"; "replyplacement_below" = "Починати мою відповідь під текстом, що цитується"; "And place my signature" = "Та додати мій підпис"; @@ -133,7 +129,22 @@ "composemessagestype_html" = "HTML"; "composemessagestype_text" = "Звичайний текст"; -"composeMessageChanged" = "Щоб змінити спосіб створення повідомлення потрібно зараз зберегти Ваші налаштування. Продовжити?"; +/* IMAP Accounts */ +"New Mail Account" = "New Mail Account"; + +"Server Name:" = "Server Name:"; +"Port:" = "Port:"; +"User Name:" = "User Name:"; +"Password:" = "Пароль:"; + +"Full Name:" = "Full Name:"; +"Email:" = "Email:"; +"Signature:" = "Підпис:"; +"(Click to create)" = "(Click to create)"; + +"Signature" = "Підпис"; +"Please enter your signature below:" = "Please enter your signature below:"; + /* Additional Parameters */ "Additional Parameters" = "Додаткові параметри"; diff --git a/UI/PreferencesUI/Welsh.lproj/Localizable.strings b/UI/PreferencesUI/Welsh.lproj/Localizable.strings index 2c482ab56..799ea5a0f 100644 --- a/UI/PreferencesUI/Welsh.lproj/Localizable.strings +++ b/UI/PreferencesUI/Welsh.lproj/Localizable.strings @@ -6,10 +6,9 @@ "General" = "Cyffredinol"; "Calendar Options" = "Opsiynau Calendr"; "Mail Options" = "Opsiynau Ebost"; -"Signature" = "Llofnod"; +"IMAP Accounts" = "IMAP Accounts"; "Vacation" = "Vacation"; "Forward" = "Forward"; -"Identities" = "hunaniaethau"; "Password" = "Cyfrinair"; "Categories" = "Categories"; "Name" = "Name"; @@ -82,8 +81,7 @@ "Day start time must be prior to day end time." = "Day start time must be prior to day end time."; "First week of year :" = "Wythnos cyntaf y flwyddyn :"; "Enable reminders for Calendar items" = "Galluogu atgoffa ar gyfer eitemau calendr"; -"Play a sound when a reminder comes due" -= "Chwarae swn pan fydd amser atgoffa"; +"Play a sound when a reminder comes due" = "Chwarae swn pan fydd amser atgoffa"; "Default reminder :" = "Atgoffa gwreiddiol :"; "firstWeekOfYear_January1" = "Dechrau ar Ionawr 1"; @@ -119,9 +117,6 @@ "messageforward_inline" = "Mewn llinell"; "messageforward_attached" = "Fel Atodiad"; -/* Identities */ -"Default identity:" = "Hunaniaeth Gwreiddiol:"; -"Manage identities..." = "Rheoli hunaniaethau..."; "replyplacement_above" = "Dechrau fy ymateb uwchben y dyfynnod"; "replyplacement_below" = "Dechrau fy ymateb o dan y dyfynnod"; "And place my signature" = "A rhowch fy llofnod"; @@ -131,7 +126,21 @@ "composemessagestype_html" = "HTML"; "composemessagestype_text" = "Plain text"; -"composeMessageChanged" = "Changing your messages composition type requires to save your preferences immediately. Continue?"; +/* IMAP Accounts */ +"New Mail Account" = "New Mail Account"; + +"Server Name:" = "Server Name:"; +"Port:" = "Port:"; +"User Name:" = "User Name:"; +"Password:" = "Cyfrinair:"; + +"Full Name:" = "Full Name:"; +"Email:" = "Email:"; +"Signature:" = "Llofnod:"; +"(Click to create)" = "(Click to create)"; + +"Signature" = "Llofnod"; +"Please enter your signature below:" = "Please enter your signature below:"; /* Additional Parameters */ "Additional Parameters" = "Additional Parameters"; diff --git a/UI/Templates/MailerUI/UIxMailEditor.wox b/UI/Templates/MailerUI/UIxMailEditor.wox index a74617252..41e07d8e9 100644 --- a/UI/Templates/MailerUI/UIxMailEditor.wox +++ b/UI/Templates/MailerUI/UIxMailEditor.wox @@ -10,6 +10,7 @@ className="UIxPageFrame" title="panelTitle" const:popup="YES" + const:userDefaultsKeys="SOGoMailComposeMessageType,SOGoMailReplyPlacement,SOGoMailSignature" const:jsFiles="UIxMailToSelection.js,ckeditor/ckeditor.js,SOGoAutoCompletion.js">