Compare commits

...
169 Commits
Author SHA1 Message Date
Francis Lachapelle f0b4a05c70 Merge to 2.2.5 2014-06-05 13:35:45 -04:00
Francis Lachapelle 6b6ec8a2ae Update ChangeLog 2014-06-05 13:35:17 -04:00
Francis Lachapelle ad30df26af Preparation for release 2.2.5 2014-06-05 13:34:40 -04:00
Francis Lachapelle 6e56f46f86 Update Dutch, Finnish, German, Polish translations 2014-06-05 13:29:10 -04:00
Francis Lachapelle fe38518a36 Minor syntax and comments improvements 2014-06-05 12:14:34 -04:00
Francis Lachapelle e118a72eab Add meta tag to tell IE to use the highest mode 2014-06-05 10:32:13 -04:00
Ludovic Marcotte 72bec939d6 Fixed text/plain reencoding issues for Outlook 2014-06-04 15:56:22 -04:00
Ludovic Marcotte ba258ca806 Fixed repetitive events issues 2014-06-04 15:23:41 -04:00
Ludovic Marcotte 0c2e2f28f7 Don't try to add attachments during reply operations 2014-05-30 13:53:20 -04:00
Ludovic Marcotte f0dfe37a55 Avoid crashing when forwarding mails with no Subject header 2014-05-30 08:41:42 -04:00
Francis Lachapelle 95d6911f1a Merge to 2.2.4 2014-05-29 11:17:46 -04:00
Francis Lachapelle dace1439df Update ChangeLog 2014-05-29 11:16:59 -04:00
Francis Lachapelle d91b37413f Preparation for release 2.2.4 2014-05-29 11:16:40 -04:00
Francis Lachapelle bd916f06eb Update ChangeLog 2014-05-29 11:15:13 -04:00
Francis Lachapelle ecbe0858f7 Update translations 2014-05-29 11:14:23 -04:00
Francis Lachapelle 24095a464c Update CKEditor to version 4.4.1 2014-05-29 10:50:48 -04:00
Francis Lachapelle 9ce3b8ff8f Merge pull request #40 from dekkers/fix-unit-test
Fix unit test with gnustep 1.24
2014-05-29 10:32:30 -04:00
Jeroen Dekkers efc33d8174 Fix unit test by also checking for the different gnustep 1.24 ordering of elements 2014-05-29 15:53:13 +02:00
Jeroen Dekkers 93971dc434 Add $(TEST_TOOL) as dependency of make check 2014-05-29 15:35:46 +02:00
Francis Lachapelle fac9520f64 Fix update of participation status via CalDAV
Fixes #2786
2014-05-28 11:39:25 -04:00
Francis Lachapelle 9eb075cc3f Rename "Archive" to "Export" in Webmail
Fixes #2758
2014-05-28 11:24:10 -04:00
Francis Lachapelle c5922b07aa Restore options when editing a draft
Fixes #193
2014-05-28 11:21:11 -04:00
Ludovic Marcotte 51e07fc306 Fix over previous commit 2014-05-27 15:33:39 -04:00
Ludovic Marcotte d35c52bb38 Fix for bug #2688 2014-05-27 14:44:57 -04:00
Ludovic Marcotte 6bab4c94f2 Added an empty stub for the Provision command 2014-05-27 09:42:02 -04:00
Ludovic Marcotte c31429a5d5 Also include the BS character in non-safe ones. 2014-05-27 08:45:00 -04:00
Ludovic Marcotte 04bb88a470 Updated NEWS file regarding pull request #39 2014-05-26 13:25:39 -04:00
extrafu c79cd750d8 Merge pull request #39 from alexcloutier/fix/publicFolder_ConfigNaming
Added new SOGo configurable variable
2014-05-26 13:24:50 -04:00
Ludovic Marcotte 6fc81e61ec Fixed documentation: NGImap4ConnectionGroupPrefix -> NGImap4ConnectionGroupIdPrefix 2014-05-23 11:30:28 -04:00
Francis Lachapelle d9ad286650 Fix newline in signature of attachment
Fixes #2787
2014-05-23 11:23:02 -04:00
Alexandre Cloutier dd57d56cf9 remove crufts and applies comments 2014-05-23 10:15:21 -04:00
Ludovic Marcotte ab7780d617 Prevent a crash when someone tries to access /SOGo/Microsoft-Server-ActiveSync directly 2014-05-22 14:39:58 -04:00
Ludovic Marcotte cb9332f240 Updated NEWS file regarding #2775 2014-05-22 10:26:58 -04:00
Ludovic Marcotte 7b60ad4797 Added patch from #2775 2014-05-22 09:20:05 -04:00
Alexandre Cloutier 3261c21549 fix case where the displayed name for the subscribed folders is duplicated 2014-05-21 14:26:08 -04:00
Francis Lachapelle 364b409501 FIx mail templates for Brazilian Portuguese
Fixes #2738
2014-05-21 08:44:35 -04:00
Alexandre Cloutier 064565e1a9 Added new SOGo configurable variable for which you can change the default display when adding a shared calendar or a shared addressBook 2014-05-20 17:05:13 -04:00
Alexandre Cloutier f92d280fbb remove unneeded condition to fix navigation in print preview 2014-05-20 11:24:27 -04:00
Francis Lachapelle ea2bce706c Update NEWS file 2014-05-16 21:15:38 -04:00
Francis Lachapelle d74afaf295 Update French translation 2014-05-16 21:15:28 -04:00
Francis Lachapelle 5e7beb3345 Remove unused localizable strings 2014-05-16 21:14:59 -04:00
Francis Lachapelle 2df5887eec Merge pull request #36 from alexcloutier/feat/calendarPrint
New print option in Calendar module
2014-05-16 16:56:51 -04:00
Francis Lachapelle 3d1bdf1a09 Update French translation 2014-05-16 16:52:44 -04:00
Francis Lachapelle 5de1343506 Remove unused localizable string 2014-05-16 16:44:25 -04:00
Francis Lachapelle 1eaa3f3891 Update NEWS file 2014-05-16 16:34:17 -04:00
Francis Lachapelle c2d3a827bd Merge branch 'alexcloutier-feat/searchCapabilities' 2014-05-16 16:30:24 -04:00
Francis Lachapelle 6b72a8d126 Merge branch 'feat/searchCapabilities' 2014-05-16 16:28:43 -04:00
Francis Lachapelle 819d0641b7 Remove common localizable strings from UI/Contacts 2014-05-16 16:19:40 -04:00
Francis Lachapelle 5d7f4ba6c6 Restore localizable string
To avoid new translation ..
2014-05-16 15:59:52 -04:00
Francis Lachapelle 34a7e28858 Rename 'Properties' button of contacts to 'Edit' 2014-05-16 15:58:34 -04:00
Francis Lachapelle 87b010c700 Indentation 2014-05-16 15:57:33 -04:00
Francis Lachapelle a44b183b7e Merge pull request #38 from alexcloutier/fix/link_address
New AddressBook properties popup window
2014-05-16 15:33:23 -04:00
Alexandre Cloutier b488721f83 Reverse loop to make sure no objects is skipped 2014-05-16 15:00:47 -04:00
Alexandre Cloutier 8a03f3bd63 remove start date in tasks 2014-05-16 14:36:51 -04:00
Alexandre Cloutier f8c8abc989 Function isPublicAccessEnabled was missing 2014-05-16 13:50:52 -04:00
Alexandre Cloutier db8d9bb16c Repair broken logic 2014-05-16 11:29:37 -04:00
Alexandre Cloutier 2794425a3f added last changes from cgx 2014-05-16 10:04:37 -04:00
Ludovic Marcotte 2f7979c796 Fix for bug #2709 2014-05-16 08:33:06 -04:00
Ludovic Marcotte c426afd7f2 New cache subsystem for ActiveSync. 2014-05-15 15:03:24 -04:00
Alexandre Cloutier 07445eb069 Created a window for address book properties and add the cardDav URL 2014-05-15 14:45:53 -04:00
Ludovic Marcotte 93bf98c326 Fixed copyright 2014-05-15 14:45:32 -04:00
Ludovic Marcotte c08c3e27d5 Improved modseq code 2014-05-15 14:44:53 -04:00
Ludovic Marcotte c8860dc92b Dropped old user-settings for ActiveSync 2014-05-15 14:32:46 -04:00
Ludovic Marcotte 1a1543a380 Updated script to reflect new table names 2014-05-15 14:29:39 -04:00
Ludovic Marcotte d2d5467317 Removed unused fluff in the file. 2014-05-14 10:51:16 -04:00
Ludovic Marcotte 18443de9bf Updated copyright info 2014-05-14 09:58:05 -04:00
Ludovic Marcotte be72456a9a Renamed folder cache creation method 2014-05-14 09:56:10 -04:00
Ludovic Marcotte a54b3e4289 Renamed the default table name from socfs_ to sogo_cache_folder_ 2014-05-14 08:56:35 -04:00
Ludovic Marcotte 7a4feae8b8 Updated code to reflect cache regorg. 2014-05-14 06:56:05 -04:00
Ludovic Marcotte 99d49b9bd9 Updated code to use the category 2014-05-13 21:14:57 -04:00
Ludovic Marcotte 5f9fb4e1c4 Big refactor for new caching mechanism. 2014-05-13 21:06:59 -04:00
Ludovic Marcotte ef2188962a Renamed cache files for generalization.
The upcoming commit will rename the content.
2014-05-13 19:58:19 -04:00
Ludovic Marcotte 2922f15ca7 More rename 2014-05-13 18:03:37 -04:00
Ludovic Marcotte 41e6fb89d3 Renamed constants in preparation for the big cache cleanup 2014-05-13 17:54:02 -04:00
Ludovic Marcotte c4a3843695 Removed unused configuration parameters 2014-05-13 13:52:20 -04:00
Alexandre Cloutier bf67b6435f applies comments 2014-05-13 13:21:18 -04:00
Alexandre Cloutier b5319446db display and printing modification 2014-05-13 10:36:26 -04:00
Francis Lachapelle d58184af15 Fix synchronization of seen/unseen status of msgs 2014-05-13 10:15:02 -04:00
Francis Lachapelle fbc6856fd5 Improve contextual menu in events list 2014-05-12 16:06:49 -04:00
Francis Lachapelle 5c41915080 Fix focus of popup windows with FF on Windows 2014-05-12 15:25:50 -04:00
Francis Lachapelle 698524b39f Fix synchronization of seen/unseen status of msgs
Fixes #2715
2014-05-09 17:28:02 -04:00
Ludovic Marcotte 1f5a2a4c62 Fixed IMAP copy/move op between subfolder in different accounts 2014-05-09 15:40:57 -04:00
Alexandre Cloutier fc3cfa5484 applied comments 2014-05-08 19:14:39 -04:00
Alexandre Cloutier 593b44ed01 forgotten code 2014-05-08 17:53:20 -04:00
Alexandre Cloutier b2ed7e3f6b search capabilites 2014-05-08 17:17:05 -04:00
Ludovic Marcotte bc2b41f383 Fixed unit test. 2014-05-08 09:13:37 -04:00
Ludovic Marcotte 7d7961ffcf Fix for bug #2492 2014-05-02 13:35:21 -04:00
Francis Lachapelle 0b9afec635 Fix saving preferences with no Sieve requirements
Fixes #2746
2014-05-01 14:07:25 -04:00
Francis Lachapelle 4e42a6a689 Don't limit Sieve script to 8KB
Fixes #2745
2014-04-30 12:36:51 -04:00
Alexandre Cloutier da8febf138 Printing display appear on one page only, fix issues with browsers 2014-04-29 15:42:50 -04:00
Francis Lachapelle 8c563c2922 Fix possible exception when retrieving reminder
Fixes #2744
2014-04-29 14:26:18 -04:00
Alexandre Cloutier fa5cf55367 Update GNUmakefile 2014-04-28 15:42:17 -04:00
Alexandre Cloutier e43b704858 Update UIxCalViewPrint.js 2014-04-28 15:32:47 -04:00
Alexandre Cloutier 44a4baca7f fix the comments in the headers 2014-04-28 15:30:32 -04:00
Alexandre Cloutier 78447d81ae Code refactoring and retouch 2014-04-28 13:54:29 -04:00
Alexandre Cloutier 9dc07fccc0 It is now possible to print the calendar as a list, dayView or weekView 2014-04-25 18:04:24 -04:00
Ludovic Marcotte ba7b65cf7b Fix for bug #2733 2014-04-24 07:59:07 -04:00
Francis Lachapelle 66bfbeaac1 MailEditor: fix text conversion with HTML entities 2014-04-23 10:55:04 -04:00
Ludovic Marcotte 619cb2c0ec Fix for bug #2721 2014-04-23 09:22:20 -04:00
Alexandre Cloutier f9869d19b4 Finished the view, need to display the eventsblock 2014-04-22 17:47:29 -04:00
Alexandre Cloutier 57b747c24a Events list is done. 2014-04-17 17:33:30 -04:00
Alexandre Cloutier 8f6b0f80a7 Creation of the first version of the printing interface 2014-04-14 10:32:08 -04:00
Alexandre Cloutier cb5e6e7ef2 adding button print calendar toolbar, adding template for the print window, ajusting indentation 2014-04-14 10:32:08 -04:00
Francis Lachapelle 49363cfe36 Update datepicker to latest version
From https://github.com/eternicode/bootstrap-datepicker/
2014-04-11 21:37:55 -04:00
Francis Lachapelle c345fc2b8d Fix display of category of events
Fixes #2703
2014-04-10 12:25:42 -04:00
Francis Lachapelle d200629b01 Fix CSS in contact editor and preferences module 2014-04-10 10:05:28 -04:00
Francis Lachapelle cfc1ac6b3b Add missing localizable string 2014-04-10 10:02:51 -04:00
Ludovic Marcotte e66ceaad07 Fixed gcc warnings and added some rationale around yesterday's fix 2014-04-10 09:40:25 -04:00
Ludovic Marcotte f370ac114a Now include message/rfc822 parts as attachments when forwarding mails 2014-04-10 09:24:43 -04:00
Ludovic Marcotte 75ee6b4806 Fixed once more true/false errors 2014-04-10 08:44:44 -04:00
Ludovic Marcotte 0961fbbab3 Properly escape attendee names in case they contain HTML entities 2014-04-10 06:59:18 -04:00
Ludovic Marcotte 0d1c39ca71 Added new configuration parameter to limit the window size 2014-04-09 20:12:19 -04:00
Ludovic Marcotte 5739356f88 Fix tests to handle list-component 2014-04-09 19:27:38 -04:00
Ludovic Marcotte 33467093c1 Fix for #2695 2014-04-09 18:03:33 -04:00
Ludovic Marcotte 96ece873ca Fix for bug #2654 2014-04-09 14:49:31 -04:00
Ludovic Marcotte 8909775cf6 Fix for #2680 2014-04-09 12:19:23 -04:00
Ludovic Marcotte 0ba59de88e Fixed formatting/warnings in previous commit. 2014-04-09 11:09:13 -04:00
extrafu 4fbe937f6d Merge pull request #22 from alexcloutier/fix/1046
BugFix #1046; Whenever the sieve server is unavailable an error message will appear
2014-04-09 11:01:19 -04:00
Ludovic Marcotte ab80e87a3f Applied patches from #2700 2014-04-09 10:57:56 -04:00
Alexandre Cloutier 068eb921aa rebase and fix comments made by extrafu 2014-04-09 10:48:47 -04:00
Alexandre Cloutier 5c13fade22 Fix merge 2014-04-09 10:35:10 -04:00
Ludovic Marcotte 371e5ead08 Updated the doc in prepration for the release 2014-04-09 10:34:48 -04:00
Alexandre Cloutier c5e6ccf332 Fix function sieveClient and the way the iVar Client is handled. Change behaviour of saveMailAccounts()so sogo wont crash if the user tries to resave his preferences after a connection error. 2014-04-09 10:31:34 -04:00
Alexandre Cloutier ec9d48c3a9 Change the function name getClient to sieveClient 2014-04-09 10:29:23 -04:00
Alexandre Cloutier 877082f042 Added missing functions getClient and IsSieveServerConnected and the changes that comes with it 2014-04-09 10:24:35 -04:00
Alexandre Cloutier 342b68fd44 Erase the merging conflicts writings 2014-04-09 10:23:43 -04:00
Alexandre Cloutier e24c22e3dc BugFix #1046; Whenever the sieve server is unavailable an error message will appear. 2014-04-09 10:23:43 -04:00
Ludovic Marcotte 7997448d41 Fixed issus in previous commits 2014-04-09 06:33:02 -04:00
Ludovic Marcotte 3629670fca Fix for #2489 2014-04-08 15:35:36 -04:00
Alexandre Cloutier c250b9a6d4 Fix deleting calendar
Added a "/" inside the function: deletePersonalCalendarConfirm().
2014-04-08 15:32:13 -04:00
extrafu afa564a6a6 Merge pull request #23 from alexcloutier/fix/1636
BugFix#1636; fix the issue where the user could add any birthday for his contact.
2014-04-08 13:17:54 -04:00
Alexandre Cloutier d9b99f536c Erased strings no longer used 2014-04-08 12:48:59 -04:00
Alexandre Cloutier 5699e5e1b4 Add datepicker for contact birthday 2014-04-08 12:12:30 -04:00
Francis Lachapelle e869406081 Minor CSS improvement in mail headers 2014-04-08 11:51:47 -04:00
Ludovic Marcotte 3e221253ad Fix for feature #1496 2014-04-08 11:19:53 -04:00
Alexandre Cloutier 446ef3467a Fix typo 2014-04-08 09:14:00 -04:00
extrafu bf64076b87 Merge pull request #29 from alexcloutier/feat/1496
New feature : 1496

Don't forget to fix the typo.
2014-04-07 19:42:31 -04:00
Alexandre Cloutier c9cbcf2566 applied Morgan's law 2014-04-07 16:40:44 -04:00
extrafu 4fe0358581 Merge pull request #20 from alexcloutier/fix/2616
bugFix #0002616 : change the behavior of ApplicationBaseURL.
2014-04-07 15:44:07 -04:00
Francis Lachapelle 534e4e3d4d Fix text nodes of message with HTML entitites 2014-04-07 11:21:52 -04:00
Alexandre Cloutier 09bcd2aee2 fix code 2014-04-07 10:59:38 -04:00
extrafu 526e21e837 Merge pull request #32 from dekkers/cleanup-generated-files
Delete generated source files when running "make clean".
2014-04-06 17:34:22 -04:00
Jeroen Dekkers c80e222210 Delete generated source files when running "make clean". 2014-04-06 22:44:50 +02:00
Francis Lachapelle 6e60bbb553 Fix saved HTML content of draft uploading a file 2014-04-04 20:20:32 -04:00
Ludovic Marcotte 846212336a Make use of a local pool to avoid over-memory usage 2014-04-04 16:53:58 -04:00
Alexandre Cloutier 349f4d824a Fix function sieveClient and the way the iVar Client is handled. Change behaviour of saveMailAccounts()so sogo wont crash if the user tries to resave his preferences after a connection error. 2014-04-04 15:04:37 -04:00
Alexandre Cloutier 2f565d2ec9 fix variable name for UIxFilterEditor.js
FolderEncoding to sieveFolderEncoding
2014-04-04 14:56:14 -04:00
extrafu eb7cabac71 Merge pull request #19 from alexcloutier/fix/2622
BugFix #0002622: The server Dovecot use by default an UTF-8 and by doing...
2014-04-04 09:21:49 -04:00
Ludovic Marcotte 2ff4498983 Fixed previous commit 2014-04-04 08:44:50 -04:00
Ludovic Marcotte 3eec753b73 Slightly improved previous pull request merge. 2014-04-04 08:39:13 -04:00
extrafu 7b0ffacff0 Merge pull request #24 from alexcloutier/fix/2612
It is now possible to see the version on the general page of the preferences window
2014-04-04 08:33:39 -04:00
Alexandre Cloutier f999c87d4d added traduction in localstring french, added some specification in function sogoVersion 2014-04-03 17:09:00 -04:00
Alexandre Cloutier 03d5975be3 Changed strings in french and change the instanciation of dictionary(addressBooksIDWithDisplayName) to release it manually 2014-03-28 10:28:33 -04:00
Alexandre Cloutier 1da1764aae The collected address book will be created only if the user select it in the preferences. Fix labels. 2014-03-27 15:27:48 -04:00
Alexandre Cloutier 365e271a84 Correct typo 2014-03-25 15:44:28 -04:00
Alexandre Cloutier bce701208a Added the modifications specified by extrafu 2014-03-25 14:53:42 -04:00
Alexandre Cloutier b16911edc1 Modified the displayed name of SOGo version and added the label in the .lproj 2014-03-24 13:55:52 -04:00
Alexandre Cloutier 76307cfaaf New feature : 1496; Unknown outgoing email addresses can now be automatically be added to your address books. 2014-03-24 13:41:57 -04:00
Alexandre Cloutier d108777e89 Display the version at the top of the general tap 2014-03-11 10:25:01 -04:00
Alexandre Cloutier 8cc352fb28 It is now possible to see the version on the general page of the preferences window 2014-03-10 14:45:10 -04:00
Alexandre Cloutier 0d69a476bc add specific date for limiting the user to today's for his contact birthday 2014-03-10 12:16:12 -04:00
Alexandre Cloutier cee1e529f8 BugFix#1636; fix the issue where the user could add anything for the birthday of his contacts 2014-03-10 12:05:01 -04:00
Alexandre Cloutier 3a681bd5a5 Change the function name getClient to sieveClient 2014-03-10 10:53:44 -04:00
Alexandre Cloutier f5faa60814 Added missing functions getClient and IsSieveServerConnected and the changes that comes with it 2014-03-10 10:33:27 -04:00
Alexandre Cloutier f8939bfb3e Change the variable name for sieveFolderEncoding instead of folderEncoding 2014-03-10 10:07:47 -04:00
Alexandre Cloutier f4d189ffc8 Erase the merging conflicts writings 2014-03-06 15:20:41 -05:00
Alexandre Cloutier 1b9ac03e94 BugFix #1046; Whenever the sieve server is unavailable an error message will appear. 2014-03-06 14:57:53 -05:00
Alexandre Cloutier 6ad87052ba bugFix #0002616 : change the behavior of ApplicationBaseURL. Now the object return a standard path(without a slash at the end of the path) That means every string added the URL ApplicationBaseURL must start with a Slash. 2014-03-06 14:47:54 -05:00
Alexandre Cloutier e7a16cba0d BugFix #0002622: The server Dovecot use by default an UTF-8 and by doing so it cause a wrong re-encoding in UTF-7 when reaching the sieve server (knowing that the foldername in sieve are encoded in UTF-7). Since it is not the case with a Cyrus server, I created a new variable available in the sogo.conf that let you specified the type of encoding you need. By default the encoding is set to UTF-7. ex : SOGoFolderEncoding = 'UTF-7' 2014-03-06 14:33:31 -05:00
271 changed files with 10482 additions and 3990 deletions

No files matched your search

+3
View File
@@ -1,6 +1,8 @@
config.make
tags
*._*
*/obj/
*._*
*/*/obj/
*/*/*/obj/
*/*/*.SOGo/
@@ -11,3 +13,4 @@ SoObjects/SOGo/SOGo.framework/
SoObjects/SOGo/derived_src/
Tests/*/config.py
*.pyc
._*
+4 -2
View File
@@ -14,16 +14,18 @@ ActiveSync_OBJC_FILES = \
iCalRecurrenceRule+ActiveSync.m \
iCalTimeZone+ActiveSync.m \
iCalToDo+ActiveSync.m \
NSCalendarDate+ActiveSync.m \
NSCalendarDate+ActiveSync.m \
NSData+ActiveSync.m \
NSDate+ActiveSync.m \
NGDOMElement+ActiveSync.m \
NGMimeMessage+ActiveSync.m \
NGVCard+ActiveSync.m \
NSArray+SyncCache.m \
NSString+ActiveSync.m \
SOGoActiveSyncDispatcher.m \
SOGoActiveSyncDispatcher+Sync.m \
SOGoMailObject+ActiveSync.m \
SOGoMailObject+ActiveSync.m \
SOGoSyncCacheObject.m \
SoObjectWebDAVDispatcher+ActiveSync.m
ActiveSync_RESOURCE_FILES += \
+39 -7
View File
@@ -37,6 +37,28 @@ static NSArray *asElementArray = nil;
@implementation NGDOMElement (ActiveSync)
- (BOOL) isTextNode
{
id <DOMNodeList> children;
id <DOMElement> element;
int i;
if ([self nodeType] == DOM_TEXT_NODE)
return YES;
children = [self childNodes];
for (i = 0; i < [children length]; i++)
{
element = [children objectAtIndex: i];
if ([element nodeType] != DOM_TEXT_NODE)
return NO;
}
return YES;
}
//
// We must handle "inner data" like this:
//
@@ -79,7 +101,7 @@ static NSArray *asElementArray = nil;
int i, count;
if (!asElementArray)
asElementArray = [[NSArray alloc] initWithObjects: @"Attendee", nil];
asElementArray = [[NSArray alloc] initWithObjects: @"Attendee", @"Category", nil];
data = [NSMutableDictionary dictionary];
@@ -96,9 +118,17 @@ static NSArray *asElementArray = nil;
tag = [element tagName];
count = [(NSArray *)[element childNodes] count];
// We check if the node is a text one or if all its
// children are text nodes. This is important to avoid side-effects
// in SOPE where "foo & bar" would result into 3 childnodes instead
// of just one.
if ([(id)element isTextNode])
{
value = [(id)element textValue];
}
// Handle inner data - see above for samples
if (count > 2)
else
{
NSMutableArray *innerElements;
id <DOMElement> innerElement;
@@ -123,7 +153,10 @@ static NSArray *asElementArray = nil;
if ([innerTag isEqualToString: [innerElement tagName]])
{
[innerElements addObject: [(NGDOMElement *)innerElement applicationData]];
if ([(id)innerElement isTextNode])
[innerElements addObject: [(NGDOMElement *)innerElement textValue]];
else
[innerElements addObject: [(NGDOMElement *)innerElement applicationData]];
}
else
{
@@ -144,12 +177,11 @@ static NSArray *asElementArray = nil;
value = nil;
}
}
else
value = [[element firstChild] textValue];
if (value && tag)
[data setObject: value forKey: tag];
}
} // if ([element nodeType] == DOM_ELEMENT_NODE)
}
return data;
+58 -15
View File
@@ -47,9 +47,10 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- (NSString *) activeSyncRepresentationInContext: (WOContext *) context
{
NSArray *emails, *addresses, *categories, *elements;
CardElement *n, *homeAdr, *workAdr;
NSArray *emails, *addresses;
NSMutableString *s;
NSString *url;
id o;
int i;
@@ -65,10 +66,42 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
if ((o = [self workCompany]))
[s appendFormat: @"<CompanyName xmlns=\"Contacts:\">%@</CompanyName>", [o activeSyncRepresentationInContext: context]];
if ((o = [[self org] flattenedValueAtIndex: 1 forKey: @""]))
[s appendFormat: @"<Department xmlns=\"Contacts:\">%@</Department>", [o activeSyncRepresentationInContext: context]];
categories = [self categories];
if ([categories count])
{
[s appendFormat: @"<Categories xmlns=\"Contacts:\">"];
for (i = 0; i < [categories count]; i++)
{
[s appendFormat: @"<Category xmlns=\"Contacts:\">%@</Category>", [[categories objectAtIndex: i] activeSyncRepresentationInContext: context]];
}
[s appendFormat: @"</Categories>"];
}
elements = [self childrenWithTag: @"url"
andAttribute: @"type"
havingValue: @"work"];
if ([elements count] > 0)
{
url = [[elements objectAtIndex: 0] flattenedValuesForKey: @""];
[s appendFormat: @"<WebPage xmlns=\"Contacts:\">%@</WebPage>", [url activeSyncRepresentationInContext: context]];
}
if ((o = [[self uniqueChildWithTag: @"x-aim"] flattenedValuesForKey: @""]))
[s appendFormat: @"<IMAddress xmlns=\"Contacts:\">%@</IMAddress>", [o activeSyncRepresentationInContext: context]];
if ((o = [self nickname]))
[s appendFormat: @"<NickName xmlns=\"Contacts:\">%@</NickName>", [o activeSyncRepresentationInContext: context]];
if ((o = [self title]))
[s appendFormat: @"<JobTitle xmlns=\"Contacts:\">%@</JobTitle>", [o activeSyncRepresentationInContext: context]];
if ((o = [self preferredEMail]))
[s appendFormat: @"<Email1Address xmlns=\"Contacts:\">%@</Email1Address>", o];
@@ -154,7 +187,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
// Other, less important fields
if ((o = [self birthday]))
[s appendFormat: @"<Birthday xmlns=\"Contacts:\">%@</Birthday>", [o activeSyncRepresentationWithoutSeparatorsInContext: context]];
[s appendFormat: @"<Birthday xmlns=\"Contacts:\">%@</Birthday>", [o activeSyncRepresentationInContext: context]];
if ((o = [self note]))
{
@@ -183,6 +216,10 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
if ((o = [[theValues objectForKey: @"Body"] objectForKey: @"Data"]))
[self setNote: o];
// Categories
if ((o = [theValues objectForKey: @"Categories"]))
[self setCategories: o];
// Birthday
if ((o = [theValues objectForKey: @"Birthday"]))
{
@@ -238,24 +275,31 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
// Company's name
if ((o = [theValues objectForKey: @"CompanyName"]))
{
[self setOrg: o units: nil];
}
[self setOrg: o units: nil];
// Department
if ((o = [theValues objectForKey: @"Department"]))
[self setOrg: nil units: [NSArray arrayWithObjects:o,nil]];
// Email addresses
if ((o = [theValues objectForKey: @"Email1Address"]))
{
[self addEmail: o types: [NSArray arrayWithObject: @"pref"]];
element = [self elementWithTag: @"email" ofType: @"work"];
[element setSingleValue: o forKey: @""];
}
if ((o = [theValues objectForKey: @"Email2Address"]))
{
[self addEmail: o types: nil];
element = [self elementWithTag: @"email" ofType: @"home"];
[element setSingleValue: o forKey: @""];
}
// SOGo currently only supports 2 email addresses ... but AS clients might send 3
// FIXME: revise this when the GUI revamp is done in SOGo
if ((o = [theValues objectForKey: @"Email3Address"]))
{
[self addEmail: o types: nil];
element = [self elementWithTag: @"email" ofType: @"three"];
[element setSingleValue: o forKey: @""];
}
// Formatted name
@@ -293,16 +337,15 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
// Job's title
if ((o = [theValues objectForKey: @"JobTitle"]))
{
[self setTitle: o];
}
[self setTitle: o];
// WebPage (work)
if ((o = [theValues objectForKey: @"WebPage"]))
{
[[self elementWithTag: @"url" ofType: @"work"]
[[self elementWithTag: @"url" ofType: @"work"]
setSingleValue: o forKey: @""];
}
if ((o = [theValues objectForKey: @"NickName"]))
[self setNickname: o];
}
@end
+49
View File
@@ -0,0 +1,49 @@
/*
Copyright (c) 2014, Inverse inc.
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
* Neither the name of the Inverse inc. nor the
names of its contributors may be used to endorse or promote products
derived from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT HOLDER> BE LIABLE FOR ANY
DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#ifndef __NSARRAYSYNCCACHE_H__
#define __NSARRAYSYNCCACHE_H__
#import <Foundation/NSArray.h>
@class NSDictionary;
@interface NSMutableArray (SyncCache)
- (id) initWithDictionary: (NSDictionary *) theDictionary;
@end
@interface NSArray (SyncCache)
- (NSDictionary *) dictionaryValue;
@end
#endif
+84
View File
@@ -0,0 +1,84 @@
/*
Copyright (c) 2014, Inverse inc.
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
* Neither the name of the Inverse inc. nor the
names of its contributors may be used to endorse or promote products
derived from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT HOLDER> BE LIABLE FOR ANY
DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#import "NSArray+SyncCache.h"
#import <Foundation/NSDictionary.h>
#include "SOGoSyncCacheObject.h"
@implementation NSMutableArray (SyncCache)
- (id) initWithDictionary: (NSDictionary *) theDictionary
{
SOGoSyncCacheObject *o;
NSArray *allKeys;
id key;
int i;
self = [self initWithCapacity: [theDictionary count]];
allKeys = [theDictionary allKeys];
for (i = 0; i < [allKeys count]; i++)
{
key = [allKeys objectAtIndex: i];
o = [SOGoSyncCacheObject syncCacheObjectWithUID: key
sequence: [theDictionary objectForKey: key]];
[self addObject: o];
}
return self;
}
@end
//
//
//
@implementation NSArray (SyncCache)
- (NSDictionary *) dictionaryValue
{
NSMutableDictionary *d;
SOGoSyncCacheObject *o;
int i;
d = [NSMutableDictionary dictionary];
for (i = 0; i < [self count]; i++)
{
o = [self objectAtIndex: i];
[d setObject: [o sequence] forKey: [o uid]];
}
return d;
}
@end
+222 -87
View File
@@ -32,9 +32,11 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#import <Foundation/NSArray.h>
#import <Foundation/NSCalendarDate.h>
#import <Foundation/NSNull.h>
#import <Foundation/NSProcessInfo.h>
#import <Foundation/NSTimeZone.h>
#import <Foundation/NSURL.h>
#import <Foundation/NSValue.h>
#import <NGObjWeb/NSException+HTTP.h>
#import <NGObjWeb/SoApplication.h>
@@ -72,6 +74,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#import <SOGo/SOGoSystemDefaults.h>
#import <SOGo/SOGoUser.h>
#import <SOGo/SOGoUserSettings.h>
#import <SOGo/SOGoCacheGCSObject.h>
#import <Appointments/SOGoAppointmentObject.h>
#import <Appointments/SOGoAppointmentFolder.h>
@@ -101,11 +104,48 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#include "NSString+ActiveSync.h"
#include "SOGoActiveSyncConstants.h"
#include "SOGoMailObject+ActiveSync.h"
#include "SOGoSyncCacheObject.h"
#include <unistd.h>
@implementation SOGoActiveSyncDispatcher (Sync)
- (void) _setFolderMetadata: (NSDictionary *) theFolderMetadata
forKey: (NSString *) theFolderKey
{
SOGoCacheGCSObject *o;
NSString *key;
key = [NSString stringWithFormat: @"%@+%@", [context objectForKey: @"DeviceId"], theFolderKey];
o = [SOGoCacheGCSObject objectWithName: key inContainer: nil];
[o setObjectType: ActiveSyncFolderCacheObject];
[o setTableUrl: [self folderTableURL]];
[o reloadIfNeeded];
[[o properties] removeObjectForKey: @"SyncCache"];
[[o properties] removeObjectForKey: @"DateCache"];
[[o properties] addEntriesFromDictionary: theFolderMetadata];
[o save];
}
- (NSMutableDictionary *) _folderMetadataForKey: (NSString *) theFolderKey
{
SOGoCacheGCSObject *o;
NSString *key;
key = [NSString stringWithFormat: @"%@+%@", [context objectForKey: @"DeviceId"], theFolderKey];
o = [SOGoCacheGCSObject objectWithName: key inContainer: nil];
[o setObjectType: ActiveSyncFolderCacheObject];
[o setTableUrl: [self folderTableURL]];
[o reloadIfNeeded];
return [o properties];
}
//
// <?xml version="1.0"?>
// <!DOCTYPE ActiveSync PUBLIC "-//MICROSOFT//DTD ActiveSync//EN" "http://www.microsoft.com/">
@@ -423,7 +463,6 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
[theBuffer appendString: @"</Fetch>"];
}
//
// The method handles <GetChanges/>
//
@@ -450,6 +489,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
return;
s = [NSMutableString string];
more_available = NO;
switch (theFolderType)
@@ -567,106 +607,189 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
case ActiveSyncMailFolder:
default:
{
NSMutableArray *addedOrChangedMessages;
NSString *uid, *command, *key;
NSMutableDictionary *syncCache, *dateCache, *folderMetadata;
SOGoSyncCacheObject *lastCacheObject, *aCacheObject;
NSMutableArray *allCacheObjects, *sortedBySequence;
SOGoMailObject *mailObject;
NSDictionary *aMessage;
NSArray *allMessages;
int deleted_count;
int j, k, return_count;
BOOL found_in_cache;
allMessages = [theCollection syncTokenFieldsWithProperties: nil matchingSyncToken: theSyncKey fromDate: theFilterType];
addedOrChangedMessages = [NSMutableArray array];
deleted_count = 0;
// Check for the WindowSize.
// FIXME: we should eventually check for modseq and slice the maximum
// amount of messages returned to ensure we don't have the same
// modseq accross contiguous boundaries
max = [allMessages count];
// We first check the number of deleted messages we have
// We do NOT honor the window size here as it seems to be
// impossible to get the modseq of an expunged message so
// we can't iterate in the list of deleted messages.
for (i = 0; i < max; i++)
{
aMessage = [allMessages objectAtIndex: i];
uid = [[[aMessage allKeys] lastObject] stringValue];
command = [[aMessage allValues] lastObject];
if ([command isEqualToString: @"deleted"])
{
[s appendString: @"<Delete xmlns=\"AirSync:\">"];
[s appendFormat: @"<ServerId xmlns=\"AirSync:\">%@</ServerId>", uid];
[s appendString: @"</Delete>"];
deleted_count++;
}
else
{
[addedOrChangedMessages addObject: aMessage];
}
}
// We then "pad" with our added/changed messages. We ALWAYS
// at least return one if available
max = [addedOrChangedMessages count];
allCacheObjects = [NSMutableArray array];
for (i = 0; i < max; i++)
{
aMessage = [addedOrChangedMessages objectAtIndex: i];
uid = [[[aMessage allKeys] lastObject] stringValue];
command = [[aMessage allValues] lastObject];
// We check for Outlook stupidity to avoid creating duplicates - see the comment
// in SOGoActiveSyncDispatcher.m: -processMoveItems:inResponse: for more details.
key = [NSString stringWithFormat: @"%@+%@+%@+%@",
[[context activeUser] login],
[context objectForKey: @"DeviceType"],
[theCollection displayName],
uid];
if ([[SOGoCache sharedCache] valueForKey: key])
[allCacheObjects addObject: [SOGoSyncCacheObject syncCacheObjectWithUID: [[[allMessages objectAtIndex: i] allKeys] lastObject]
sequence: [[[allMessages objectAtIndex: i] allValues] lastObject]]];
}
// If it's a new Sync operation, DateCache and SyncCache need to be deleted
// but GUID stored by folderSync shouldn't be touched
folderMetadata = [self _folderMetadataForKey: [theCollection nameInContainer]];
if ([theSyncKey isEqualToString: @"-1"])
{
[folderMetadata setObject: [NSMutableDictionary dictionary] forKey: @"SyncCache"];
[folderMetadata setObject: [NSMutableDictionary dictionary] forKey: @"DateCache"];
}
// Check whether GUID in cache is equal to the GUID from imap - this is to avoid cache corruptions if a folder has been renamed and a new folder
// with the same name has been created but folderSync has not yet updated the cache
if (!([[theCollection nameInContainer] isEqualToString:
[NSString stringWithFormat: @"folder%@", [self globallyUniqueIDToIMAPFolderName: [folderMetadata objectForKey: @"GUID"] type: theFolderType]]]))
{
NSLog(@"GUID mismatch don't sync now!");
return;
}
syncCache = [folderMetadata objectForKey: @"SyncCache"];
dateCache = [folderMetadata objectForKey: @"DateCache"];
sortedBySequence = [[NSMutableArray alloc] initWithDictionary: syncCache];
[sortedBySequence sortUsingSelector: @selector(compareSequence:)];
[sortedBySequence autorelease];
[allCacheObjects sortUsingSelector: @selector(compareSequence:)];
//NSLog(@"sortedBySequence (%d) - lastObject: %@", [sortedBySequence count], [sortedBySequence lastObject]);
//NSLog(@"allCacheObjects (%d) - lastObject: %@", [allCacheObjects count], [allCacheObjects lastObject]);
lastCacheObject = [sortedBySequence lastObject];
if ([folderMetadata objectForKey: @"MoreAvailable"] && lastCacheObject)
{
for (j = 0; j < [allCacheObjects count]; j++)
{
[[SOGoCache sharedCache] removeValueForKey: key];
command = @"changed";
if ([[lastCacheObject uid] isEqual: [[allCacheObjects objectAtIndex: j] uid]])
{
// Found out where we're at, let's continue from there...
found_in_cache = YES;
break;
}
}
if ([command isEqualToString: @"added"])
[s appendString: @"<Add xmlns=\"AirSync:\">"];
else
[s appendString: @"<Change xmlns=\"AirSync:\">"];
mailObject = [theCollection lookupName: uid
inContext: context
acquire: 0];
[s appendFormat: @"<ServerId xmlns=\"AirSync:\">%@</ServerId>", uid];
[s appendString: @"<ApplicationData xmlns=\"AirSync:\">"];
[s appendString: [mailObject activeSyncRepresentationInContext: context]];
[s appendString: @"</ApplicationData>"];
if ([command isEqualToString: @"added"])
[s appendString: @"</Add>"];
else
[s appendString: @"</Change>"];
// We check if we must stop padding
if (i+1+deleted_count > theWindowSize)
}
else
found_in_cache = NO;
if (found_in_cache)
k = j+1;
else
{
k = 0;
j = 0;
}
//NSLog(@"found in cache: %d k = %d", found_in_cache, k);
return_count = 0;
for (; k < [allCacheObjects count]; k++)
{
// Check for the WindowSize and slice accordingly
if (return_count >= theWindowSize)
{
NSString *lastSequence;
more_available = YES;
lastSequence = ([[aCacheObject sequence] isEqual: [NSNull null]] ? @"1" : [aCacheObject sequence]);
*theLastServerKey = [NSString stringWithFormat: @"%@-%@", [aCacheObject uid], lastSequence];
//NSLog(@"Reached windowSize - lastUID will be: %@", *theLastServerKey);
break;
}
aCacheObject = [allCacheObjects objectAtIndex: k];
// If found in cache, it's either a Change or a Delete
if ([syncCache objectForKey: [aCacheObject uid]])
{
if ([[aCacheObject sequence] isEqual: [NSNull null]])
{
// Deleted
[s appendString: @"<Delete xmlns=\"AirSync:\">"];
[s appendFormat: @"<ServerId xmlns=\"AirSync:\">%@</ServerId>", [aCacheObject uid]];
[s appendString: @"</Delete>"];
[syncCache removeObjectForKey: [aCacheObject uid]];
[dateCache removeObjectForKey: [aCacheObject uid]];
}
else
{
// Changed
outlook_hack:
mailObject = [theCollection lookupName: [aCacheObject uid]
inContext: context
acquire: 0];
[s appendString: @"<Change xmlns=\"AirSync:\">"];
[s appendFormat: @"<ServerId xmlns=\"AirSync:\">%@</ServerId>", [aCacheObject uid]];
[s appendString: @"<ApplicationData xmlns=\"AirSync:\">"];
[s appendString: [mailObject activeSyncRepresentationInContext: context]];
[s appendString: @"</ApplicationData>"];
[s appendString: @"</Change>"];
[syncCache setObject: [aCacheObject sequence] forKey: [aCacheObject uid]];
}
return_count++;
}
else
{
// Added
if (![[aCacheObject sequence] isEqual: [NSNull null]])
{
NSString *key;
// We check for Outlook stupidity to avoid creating duplicates - see the comment
// in SOGoActiveSyncDispatcher.m: -processMoveItems:inResponse: for more details.
key = [NSString stringWithFormat: @"%@+%@+%@+%@",
[[context activeUser] login],
[context objectForKey: @"DeviceType"],
[theCollection displayName],
[aCacheObject uid]];
if ([[SOGoCache sharedCache] valueForKey: key])
{
[[SOGoCache sharedCache] removeValueForKey: key];
goto outlook_hack;
}
mailObject = [theCollection lookupName: [aCacheObject uid]
inContext: context
acquire: 0];
[s appendString: @"<Add xmlns=\"AirSync:\">"];
[s appendFormat: @"<ServerId xmlns=\"AirSync:\">%@</ServerId>", [aCacheObject uid]];
[s appendString: @"<ApplicationData xmlns=\"AirSync:\">"];
[s appendString: [mailObject activeSyncRepresentationInContext: context]];
[s appendString: @"</ApplicationData>"];
[s appendString: @"</Add>"];
[syncCache setObject: [aCacheObject sequence] forKey: [aCacheObject uid]];
[dateCache setObject: [NSCalendarDate date] forKey: [aCacheObject uid]];
return_count++;
}
else
{
//NSLog(@"skipping old deleted UID: %@", [aCacheObject uid]);
}
}
}
//
if (more_available)
{
*theLastServerKey = uid;
}
}
[folderMetadata setObject: [NSNumber numberWithBool: YES] forKey: @"MoreAvailable"];
else
[folderMetadata removeObjectForKey: @"MoreAvailable"];
[self _setFolderMetadata: folderMetadata
forKey: [theCollection nameInContainer]];
} // default:
break;
} // switch (folderType) ...
@@ -767,13 +890,14 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
NSMutableString *changeBuffer, *commandsBuffer;
BOOL getChanges, first_sync;
unsigned int windowSize;
unsigned int windowSize, v;
changeBuffer = [NSMutableString string];
commandsBuffer = [NSMutableString string];
collectionId = [[(id)[theDocumentElement getElementsByTagName: @"CollectionId"] lastObject] textValue];
realCollectionId = [collectionId realCollectionIdWithFolderType: &folderType];
realCollectionId = [self globallyUniqueIDToIMAPFolderName: realCollectionId type: folderType];
collection = [self collectionFromId: realCollectionId type: folderType];
syncKey = davCollectionTag = [[(id)[theDocumentElement getElementsByTagName: @"SyncKey"] lastObject] textValue];
@@ -783,6 +907,11 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
if (windowSize == 0 || windowSize > 512)
windowSize = 100;
// We check if we must overwrite the windowSize with a system preference. This can be useful
// if the user population has large mailboxes and slow connectivity
if ((v = [[SOGoSystemDefaults sharedSystemDefaults] maximumSyncWindowSize]))
windowSize = v;
lastServerKey = nil;
@@ -855,12 +984,18 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
if ([changeBuffer length] || [commandsBuffer length])
{
if (lastServerKey)
davCollectionTag = [collection davCollectionTagFromId: lastServerKey];
else
davCollectionTag = lastServerKey;
else if (![[self _folderMetadataForKey: [collection nameInContainer]] objectForKey: @"MoreAvailable"])
davCollectionTag = [collection davCollectionTag];
*changeDetected = YES;
}
else
{
if (folderType == ActiveSyncMailFolder && [syncKey isEqualToString: @"-1"])
davCollectionTag = [collection davCollectionTag];
}
// Generate the response buffer
[theBuffer appendString: @"<Collection>"];
+8
View File
@@ -32,17 +32,25 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#include "SOGoActiveSyncConstants.h"
@class NSException;
@class NSURL;
@interface SOGoActiveSyncDispatcher : NSObject
{
NSURL *folderTableURL;
id context;
}
- (id) collectionFromId: (NSString *) theCollectionId
type: (SOGoMicrosoftActiveSyncFolderType) theFolderType;
- (id) globallyUniqueIDToIMAPFolderName: (NSString *) theIdToTranslate
type: (SOGoMicrosoftActiveSyncFolderType) theFolderType;
- (NSException *) dispatchRequest: (id) theRequest
inResponse: (id) theResponse
context: (id) theContext;
- (NSURL *) folderTableURL;
- (void) ensureFolderTableExists;
@end
+406 -65
View File
@@ -77,6 +77,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#import <SOGo/NSArray+DAV.h>
#import <SOGo/NSDictionary+DAV.h>
#import <SOGo/SOGoCache.h>
#import <SOGo/SOGoCacheGCSObject.h>
#import <SOGo/SOGoDAVAuthenticator.h>
#import <SOGo/SOGoDomainDefaults.h>
#import <SOGo/SOGoMailer.h>
@@ -85,6 +86,8 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#import <SOGo/SOGoUserFolder.h>
#import <SOGo/SOGoUserManager.h>
#import <SOGo/SOGoUserSettings.h>
#import <SOGo/GCSSpecialQueries+SOGoCacheObject.h>
#import <SOGo/NSString+Utilities.h>
#import <Appointments/SOGoAppointmentFolder.h>
#import <Appointments/SOGoAppointmentFolders.h>
@@ -115,24 +118,76 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#include "SOGoActiveSyncConstants.h"
#include "SOGoMailObject+ActiveSync.h"
#import <GDLContentStore/GCSChannelManager.h>
#include <unistd.h>
@implementation SOGoActiveSyncDispatcher
- (id) init
{
[super init];
folderTableURL = nil;
return self;
}
- (void) dealloc
{
RELEASE(folderTableURL);
[super dealloc];
}
- (void) _setFolderSyncKey: (NSString *) theSyncKey
{
NSMutableDictionary *metadata;
metadata = [[[context activeUser] userSettings] microsoftActiveSyncMetadataForDevice: [context objectForKey: @"DeviceId"]];
[metadata setObject: [NSDictionary dictionaryWithObject: theSyncKey forKey: @"SyncKey"] forKey: @"FolderSync"];
SOGoCacheGCSObject *o;
[[[context activeUser] userSettings] setMicrosoftActiveSyncMetadata: metadata
forDevice: [context objectForKey: @"DeviceId"]];
[[[context activeUser] userSettings] synchronize];
o = [SOGoCacheGCSObject objectWithName: [context objectForKey: @"DeviceId"] inContainer: nil];
[o setObjectType: ActiveSyncGlobalCacheObject];
[o setTableUrl: [self folderTableURL]];
[o reloadIfNeeded];
[[o properties] removeAllObjects];
[[o properties] addEntriesFromDictionary: [NSDictionary dictionaryWithObject: theSyncKey forKey: @"FolderSyncKey"]];
[o save];
}
- (NSMutableDictionary *) _globalMetadataForDevice
{
SOGoCacheGCSObject *o;
o = [SOGoCacheGCSObject objectWithName: [context objectForKey: @"DeviceId"] inContainer: nil];
[o setObjectType: ActiveSyncGlobalCacheObject];
[o setTableUrl: [self folderTableURL]];
[o reloadIfNeeded];
return [o properties];
}
- (id) globallyUniqueIDToIMAPFolderName: (NSString *) theIdToTranslate
type: (SOGoMicrosoftActiveSyncFolderType) theFolderType
{
if (theFolderType == ActiveSyncMailFolder)
{
SOGoMailAccounts *accountsFolder;
SOGoMailAccount *accountFolder;
SOGoUserFolder *userFolder;
NSDictionary *imapGUIDs;
userFolder = [[context activeUser] homeFolderInContext: context];
accountsFolder = [userFolder lookupName: @"Mail" inContext: context acquire: NO];
accountFolder = [accountsFolder lookupName: @"0" inContext: context acquire: NO];
// Get the GUID of the IMAP folder
imapGUIDs = [accountFolder imapFolderGUIDs];
return [[imapGUIDs allKeysForObject: theIdToTranslate] objectAtIndex: 0];
}
return theIdToTranslate;
}
//
//
//
@@ -166,7 +221,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
userFolder = [[context activeUser] homeFolderInContext: context];
accountsFolder = [userFolder lookupName: @"Mail" inContext: context acquire: NO];
currentFolder = [accountsFolder lookupName: @"0" inContext: context acquire: NO];
collection = [currentFolder lookupName: [NSString stringWithFormat: @"folder%@", theCollectionId]
inContext: context
acquire: NO];
@@ -212,20 +267,52 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
accountsFolder = [userFolder lookupName: @"Mail" inContext: context acquire: NO];
currentFolder = [accountsFolder lookupName: @"0" inContext: context acquire: NO];
newFolder = [currentFolder lookupName: [NSString stringWithFormat: @"folder%@", [displayName stringByEncodingImap4FolderName]]
inContext: context
acquire: NO];
// If the parrent is 0 -> ok ; otherwise need to build the foldername based on parentId + displayName
if ([parentId isEqualToString: @"0"])
newFolder = [currentFolder lookupName: [NSString stringWithFormat: @"folder%@", [displayName stringByEncodingImap4FolderName]]
inContext: context
acquire: NO];
else
{
parentId = [self globallyUniqueIDToIMAPFolderName: [[parentId stringByUnescapingURL] substringFromIndex: 5] type: ActiveSyncMailFolder];
newFolder = [currentFolder lookupName: [NSString stringWithFormat: @"folder%@/%@", [parentId stringByEncodingImap4FolderName],
[displayName stringByEncodingImap4FolderName]]
inContext: context
acquire: NO];
}
// FIXME
// handle exists (status == 2)
// handle right synckey
if ([newFolder create])
{
SOGoMailAccount *accountFolder;
NSDictionary *imapGUIDs;
SOGoCacheGCSObject *o;
NSString *key;
nameInContainer = [newFolder nameInContainer];
// We strip the "folder" prefix
nameInContainer = [nameInContainer substringFromIndex: 6];
// save new guid into cache
accountFolder = [accountsFolder lookupName: @"0" inContext: context acquire: NO];
// update GUID in cache
imapGUIDs = [accountFolder imapFolderGUIDs];
key = [NSString stringWithFormat: @"%@+folder%@", [context objectForKey: @"DeviceId"], nameInContainer ];
o = [SOGoCacheGCSObject objectWithName: key inContainer: nil];
[o setObjectType: ActiveSyncFolderCacheObject];
[o setTableUrl: [self folderTableURL]];
[o reloadIfNeeded];
nameInContainer =[imapGUIDs objectForKey: nameInContainer];
[[o properties ] setObject: nameInContainer forKey: @"GUID"];
[o save];
nameInContainer = [[NSString stringWithFormat: @"mail/%@", nameInContainer] stringByEscapingURL];
}
else
@@ -306,9 +393,9 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
NSString *serverId;
SOGoMicrosoftActiveSyncFolderType folderType;
serverId = [[[(id)[theDocumentElement getElementsByTagName: @"ServerId"] lastObject] textValue] realCollectionIdWithFolderType: &folderType];
serverId = [self globallyUniqueIDToIMAPFolderName: serverId type: folderType];
userFolder = [[context activeUser] homeFolderInContext: context];
accountsFolder = [userFolder lookupName: @"Mail" inContext: context acquire: NO];
@@ -369,6 +456,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
int status;
serverId = [[[(id)[theDocumentElement getElementsByTagName: @"ServerId"] lastObject] textValue] realCollectionIdWithFolderType: &folderType];
serverId = [self globallyUniqueIDToIMAPFolderName: serverId type: folderType];
parentId = [[(id)[theDocumentElement getElementsByTagName: @"ParentId"] lastObject] textValue];
displayName = [[(id)[theDocumentElement getElementsByTagName: @"DisplayName"] lastObject] textValue];
@@ -380,7 +468,18 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
inContext: context
acquire: NO];
error = [folderToUpdate renameTo: displayName];
// If parent is 0 or displayname is not changed it is either a rename of a folder in 0 or a move to 0
if ([parentId isEqualToString: @"0"] ||
([serverId hasSuffix: [NSString stringWithFormat: @"/%@", displayName]] && [parentId isEqualToString: @"0"]))
{
error = [folderToUpdate renameTo: [NSString stringWithFormat: @"/%@", [displayName stringByEncodingImap4FolderName]]];
}
else
{
parentId = [self globallyUniqueIDToIMAPFolderName: [[parentId stringByUnescapingURL] substringFromIndex: 5] type: folderType];
error = [folderToUpdate renameTo: [NSString stringWithFormat: @"%@/%@", [parentId stringByEncodingImap4FolderName],
[displayName stringByEncodingImap4FolderName]]];
}
// Handle new name exist
if (!error)
@@ -396,10 +495,12 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
// See http://msdn.microsoft.com/en-us/library/gg675615(v=exchg.80).aspx
// we return '9' - we force a FolderSync
status = 9;
status = 1;
[self _setFolderSyncKey: syncKey];
// FIXME - TODO: We *MUST* update the path in our cache! See -changePathTo in SOGoCacheGCSObject
s = [NSMutableString string];
[s appendString: @"<?xml version=\"1.0\" encoding=\"utf-8\"?>"];
[s appendString: @"<!DOCTYPE ActiveSync PUBLIC \"-//MICROSOFT//DTD ActiveSync//EN\" \"http://www.microsoft.com/\">"];
@@ -430,15 +531,16 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- (void) processFolderSync: (id <DOMElement>) theDocumentElement
inResponse: (WOResponse *) theResponse
{
NSMutableDictionary *metadata;
NSMutableString *s;
NSString *syncKey;
NSData *d;
BOOL first_sync;
int status;
metadata = [[[context activeUser] userSettings] microsoftActiveSyncMetadataForDevice: [context objectForKey: @"DeviceId"]];
metadata = [self _globalMetadataForDevice];
syncKey = [[(id)[theDocumentElement getElementsByTagName: @"SyncKey"] lastObject] textValue];
s = [NSMutableString string];
@@ -450,7 +552,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
first_sync = YES;
syncKey = @"1";
}
else if (![syncKey isEqualToString: [[metadata objectForKey: @"FolderSync"] objectForKey: @"SyncKey"]])
else if (![syncKey isEqualToString: [metadata objectForKey: @"FolderSyncKey"]])
{
// Synchronization key mismatch or invalid synchronization key
status = 9;
@@ -461,84 +563,213 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
[s appendString: @"<?xml version=\"1.0\" encoding=\"utf-8\"?>"];
[s appendString: @"<!DOCTYPE ActiveSync PUBLIC \"-//MICROSOFT//DTD ActiveSync//EN\" \"http://www.microsoft.com/\">"];
[s appendFormat: @"<FolderSync xmlns=\"FolderHierarchy:\"><Status>%d</Status><SyncKey>%@</SyncKey><Changes>", status, syncKey];
// Initial sync, let's return the complete folder list
if (first_sync)
if (status == 1)
{
SOGoMailAccounts *accountsFolder;
SOGoMailAccount *accountFolder;
SOGoUserFolder *userFolder;
id currentFolder;
NSDictionary *folderMetadata;
NSArray *allFoldersMetadata;
NSString *name, *serverId, *parentId;
NSString *key, *cKey, *nkey, *name, *serverId, *parentId;
NSDictionary *folderMetadata, *imapGUIDs;
NSArray *allFoldersMetadata, *allKeys;
NSMutableDictionary *cachedGUIDs;
NSMutableString *commands;
SOGoCacheGCSObject *o;
int i, type, command_count;
int i, type;
userFolder = [[context activeUser] homeFolderInContext: context];
accountsFolder = [userFolder lookupName: @"Mail" inContext: context acquire: NO];
accountFolder = [accountsFolder lookupName: @"0" inContext: context acquire: NO];
accountsFolder = [userFolder lookupName: @"Mail" inContext: context acquire: NO];
accountFolder = [accountsFolder lookupName: @"0" inContext: context acquire: NO];
allFoldersMetadata = [accountFolder allFoldersMetadata];
// Get GUIDs of folder (IMAP)
// e.g. {INBOX = "sogo_73c_192bd57b_d8"
imapGUIDs = [accountFolder imapFolderGUIDs];
// See 2.2.3.170.3 Type (FolderSync) - http://msdn.microsoft.com/en-us/library/gg650877(v=exchg.80).aspx
[s appendFormat: @"<Count>%d</Count>", [allFoldersMetadata count]+3];
cachedGUIDs = [NSMutableDictionary dictionary];
// No need to read cached folder infos during first sync. Otherwise, pull it from the database.
// e.g. {"sogo_73c_192bd57b_d8" = INBOX} - guid = foldername for easy reverse lookup with imapGUIDs
if (!first_sync)
{
NSArray *foldersInCache;
NSString *folderName;
// get the list of folder stored in cache
key = [NSString stringWithFormat: @"%@+%@", [context objectForKey: @"DeviceId"], @"0"];
o = [SOGoCacheGCSObject objectWithName: key inContainer: nil];
[o setObjectType: ActiveSyncFolderCacheObject];
[o setTableUrl: [self folderTableURL]];
[o reloadIfNeeded];
foldersInCache = [o folderList: [context objectForKey: @"DeviceId"] newerThanVersion: -1];
// get guids of folders stored in cache
for (i = 0; i < [foldersInCache count]; i++)
{
folderName = [foldersInCache objectAtIndex: i];
key = [folderName substringFromIndex: 1];
o = [SOGoCacheGCSObject objectWithName: key inContainer: nil];
[o setObjectType: ActiveSyncFolderCacheObject];
[o setTableUrl: [self folderTableURL]];
[o reloadIfNeeded];
if ([[o properties ] objectForKey: @"GUID"])
[cachedGUIDs setObject: [key substringFromIndex: [key rangeOfString: @"+"].location+7]
forKey: [[o properties] objectForKey: @"GUID"]];
}
}
// Handle folders that have been deleted over IMAP
command_count = 0;
commands = [NSMutableString string];
allKeys = [cachedGUIDs allKeys];
for (i = 0; i < [allKeys count]; i++)
{
cKey = [allKeys objectAtIndex: i];
if (![imapGUIDs allKeysForObject: cKey])
{
// Delete folders cache content to avoid stale data if a new folder gets created with the same name
key = [NSString stringWithFormat: @"%@+folder%@", [context objectForKey: @"DeviceId"], [cachedGUIDs objectForKey: cKey]];
o = [SOGoCacheGCSObject objectWithName: key inContainer: nil];
[o setObjectType: ActiveSyncFolderCacheObject];
[o setTableUrl: [self folderTableURL]];
[o reloadIfNeeded];
// Only send a delete command if GUID is found
if ([[o properties] objectForKey: @"GUID"])
{
[commands appendFormat: @"<Delete><ServerId>%@</ServerId></Delete>", [[NSString stringWithFormat: @"mail/%@", [[o properties ] objectForKey: @"GUID"]] stringByEscapingURL] ];
command_count++;
}
[[o properties] removeAllObjects];
[o save];
}
}
// Handle addition and changes
for (i = 0; i < [allFoldersMetadata count]; i++)
{
folderMetadata = [allFoldersMetadata objectAtIndex: i];
serverId = [NSString stringWithFormat: @"mail%@", [folderMetadata objectForKey: @"path"]];
// No GUID -> no sync
if (!([imapGUIDs objectForKey: [[folderMetadata objectForKey: @"path"] substringFromIndex: 1]]))
continue;
serverId = [NSString stringWithFormat: @"mail/%@", [imapGUIDs objectForKey: [[folderMetadata objectForKey: @"path"] substringFromIndex: 1]]];
name = [folderMetadata objectForKey: @"displayName"];
if ([name hasPrefix: @"/"])
name = [name substringFromIndex: 1];
if ([name hasSuffix: @"/"])
name = [name substringToIndex: [name length]-2];
name = [name substringToIndex: [name length]-1];
type = [[folderMetadata objectForKey: @"type"] activeSyncFolderType];
parentId = @"0";
if ([folderMetadata objectForKey: @"parent"])
{
parentId = [NSString stringWithFormat: @"mail%@", [folderMetadata objectForKey: @"parent"]];
parentId = [NSString stringWithFormat: @"mail/%@", [imapGUIDs objectForKey: [[folderMetadata objectForKey: @"parent"] substringFromIndex: 1]]];
name = [[name pathComponents] lastObject];
}
[s appendFormat: @"<Add><ServerId>%@</ServerId><ParentId>%@</ParentId><Type>%d</Type><DisplayName>%@</DisplayName></Add>",
[serverId stringByEscapingURL],
[parentId stringByEscapingURL],
type,
[name activeSyncRepresentationInContext: context]];
// Decide between add and change
if ([cachedGUIDs objectForKey: [imapGUIDs objectForKey: [[folderMetadata objectForKey: @"path"] substringFromIndex: 1]]])
{
// Search GUID to check name change in cache (diff between IMAP and cache)
if ((![[[folderMetadata objectForKey: @"path"] substringFromIndex: 1] isEqualToString: [imapGUIDs objectForKey: [cachedGUIDs objectForKey:
[[folderMetadata objectForKey: @"path"] substringFromIndex: 1]]]]))
{
key = [NSString stringWithFormat: @"%@+folder%@", [context objectForKey: @"DeviceId"], [cachedGUIDs objectForKey:
[imapGUIDs objectForKey: [[folderMetadata objectForKey: @"path"] substringFromIndex: 1]]]];
nkey = [NSString stringWithFormat: @"%@+folder%@", [context objectForKey: @"DeviceId"], [[folderMetadata objectForKey: @"path"] substringFromIndex: 1] ];
if (![key isEqualToString: nkey])
{
[commands appendFormat: @"<Update><ServerId>%@</ServerId><ParentId>%@</ParentId><Type>%d</Type><DisplayName>%@</DisplayName></Update>",
[serverId stringByEscapingURL],
[parentId stringByEscapingURL],
type,
[name activeSyncRepresentationInContext: context]];
// Change path in cache
o = [SOGoCacheGCSObject objectWithName: key inContainer: nil];
[o setObjectType: ActiveSyncFolderCacheObject];
[o setTableUrl: [self folderTableURL]];
[o reloadIfNeeded];
[o changePathTo: [NSString stringWithFormat: @"/%@", nkey]]; // ?? why is '/' prefix needed - problem in changePathTo?
command_count++;
}
}
}
else
{
[commands appendFormat: @"<Add><ServerId>%@</ServerId><ParentId>%@</ParentId><Type>%d</Type><DisplayName>%@</DisplayName></Add>",
[serverId stringByEscapingURL],
[parentId stringByEscapingURL],
type,
[name activeSyncRepresentationInContext: context]];
// Store folder's GUID in cache
key = [NSString stringWithFormat: @"%@+folder%@", [context objectForKey: @"DeviceId"], [[folderMetadata objectForKey: @"path"] substringFromIndex: 1]];
o = [SOGoCacheGCSObject objectWithName: key inContainer: nil];
[o setObjectType: ActiveSyncFolderCacheObject];
[o setTableUrl: [self folderTableURL]];
[o reloadIfNeeded];
[[o properties ] setObject: [imapGUIDs objectForKey: [[folderMetadata objectForKey: @"path"] substringFromIndex: 1]] forKey: @"GUID"];
[o save];
command_count++;
}
}
// We add the personal calendar - events
// FIXME: add all calendars
currentFolder = [[context activeUser] personalCalendarFolderInContext: context];
name = [NSString stringWithFormat: @"vevent/%@", [currentFolder nameInContainer]];
[s appendFormat: @"<Add><ServerId>%@</ServerId><ParentId>%@</ParentId><Type>%d</Type><DisplayName>%@</DisplayName></Add>", name, @"0", 8, [[currentFolder displayName] activeSyncRepresentationInContext: context]];
// We add the personal calendar - tasks
// FIXME: add all calendars
currentFolder = [[context activeUser] personalCalendarFolderInContext: context];
name = [NSString stringWithFormat: @"vtodo/%@", [currentFolder nameInContainer]];
[s appendFormat: @"<Add><ServerId>%@</ServerId><ParentId>%@</ParentId><Type>%d</Type><DisplayName>%@</DisplayName></Add>", name, @"0", 7, [[currentFolder displayName] activeSyncRepresentationInContext: context]];
// We add the personal address book
// FIXME: add all address books
currentFolder = [[context activeUser] personalContactsFolderInContext: context];
name = [NSString stringWithFormat: @"vcard/%@", [currentFolder nameInContainer]];
[s appendFormat: @"<Add><ServerId>%@</ServerId><ParentId>%@</ParentId><Type>%d</Type><DisplayName>%@</DisplayName></Add>", name, @"0", 9, [[currentFolder displayName] activeSyncRepresentationInContext: context]];
if (first_sync)
[s appendFormat: @"<Count>%d</Count>", command_count+3];
else
[s appendFormat: @"<Count>%d</Count>", command_count];
if (command_count > 0)
[s appendFormat: @"%@", commands];
if (first_sync)
{
// We add the personal calendar - events
// FIXME: add all calendars
currentFolder = [[context activeUser] personalCalendarFolderInContext: context];
name = [NSString stringWithFormat: @"vevent/%@", [currentFolder nameInContainer]];
[s appendFormat: @"<Add><ServerId>%@</ServerId><ParentId>%@</ParentId><Type>%d</Type><DisplayName>%@</DisplayName></Add>", name, @"0", 8, [[currentFolder displayName] activeSyncRepresentationInContext: context]];
// We add the personal calendar - tasks
// FIXME: add all calendars
currentFolder = [[context activeUser] personalCalendarFolderInContext: context];
name = [NSString stringWithFormat: @"vtodo/%@", [currentFolder nameInContainer]];
[s appendFormat: @"<Add><ServerId>%@</ServerId><ParentId>%@</ParentId><Type>%d</Type><DisplayName>%@</DisplayName></Add>", name, @"0", 7, [[currentFolder displayName] activeSyncRepresentationInContext: context]];
// We add the personal address book
// FIXME: add all address books
currentFolder = [[context activeUser] personalContactsFolderInContext: context];
name = [NSString stringWithFormat: @"vcard/%@", [currentFolder nameInContainer]];
[s appendFormat: @"<Add><ServerId>%@</ServerId><ParentId>%@</ParentId><Type>%d</Type><DisplayName>%@</DisplayName></Add>", name, @"0", 9, [[currentFolder displayName] activeSyncRepresentationInContext: context]];
}
}
[s appendString: @"</Changes></FolderSync>"];
d = [[s dataUsingEncoding: NSUTF8StringEncoding] xml2wbxml];
[theResponse setContent: d];
}
}
//
// From: http://msdn.microsoft.com/en-us/library/ee157980(v=exchg.80).aspx :
@@ -585,6 +816,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
collectionId = [[(id)[theDocumentElement getElementsByTagName: @"CollectionId"] lastObject] textValue];
realCollectionId = [collectionId realCollectionIdWithFolderType: &folderType];
realCollectionId = [self globallyUniqueIDToIMAPFolderName: realCollectionId type: folderType];
currentCollection = [self collectionFromId: realCollectionId type: folderType];
//
@@ -659,6 +891,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
fileReference = [[[(id)[theDocumentElement getElementsByTagName: @"FileReference"] lastObject] textValue] stringByUnescapingURL];
realCollectionId = [fileReference realCollectionIdWithFolderType: &folderType];
realCollectionId = [self globallyUniqueIDToIMAPFolderName: realCollectionId type: folderType];
if (folderType == ActiveSyncMailFolder)
{
@@ -752,6 +985,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
status = 1;
realCollectionId = [[[(id)[theDocumentElement getElementsByTagName: @"CollectionId"] lastObject] textValue] realCollectionIdWithFolderType: &folderType];
realCollectionId = [self globallyUniqueIDToIMAPFolderName: realCollectionId type: folderType];
userResponse = [[[(id)[theDocumentElement getElementsByTagName: @"UserResponse"] lastObject] textValue] intValue];
requestId = [[(id)[theDocumentElement getElementsByTagName: @"RequestId"] lastObject] textValue];
appointmentObject = nil;
@@ -902,6 +1136,9 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
NSDictionary *response;
NSString *v;
srcFolderId = [self globallyUniqueIDToIMAPFolderName: srcFolderId type: srcFolderType];
dstFolderId = [self globallyUniqueIDToIMAPFolderName: dstFolderId type: dstFolderType];
currentCollection = [self collectionFromId: srcFolderId type: srcFolderType];
client = [[currentCollection imap4Connection] client];
@@ -1037,6 +1274,26 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
[theResponse setContent: d];
}
//
// We ignore everything for now.
//
- (void) processProvision: (id <DOMElement>) theDocumentElement
inResponse: (WOResponse *) theResponse
{
NSMutableString *s;
NSData *d;
s = [NSMutableString string];
[s appendString: @"<?xml version=\"1.0\" encoding=\"utf-8\"?>"];
[s appendString: @"<!DOCTYPE ActiveSync PUBLIC \"-//MICROSOFT//DTD ActiveSync//EN\" \"http://www.microsoft.com/\">"];
[s appendString: @"<Provision xmlns=\"Provision:\">"];
[s appendString: @"</Provision>"];
d = [[s dataUsingEncoding: NSUTF8StringEncoding] xml2wbxml];
[theResponse setContent: d];
}
//
// <?xml version="1.0"?>
// <!DOCTYPE ActiveSync PUBLIC "-//MICROSOFT//DTD ActiveSync//EN" "http://www.microsoft.com/">
@@ -1420,11 +1677,24 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
{
NSString *folderId, *itemId, *realCollectionId;
SOGoMicrosoftActiveSyncFolderType folderType;
id value;
folderId = [[(id)[theDocumentElement getElementsByTagName: @"FolderId"] lastObject] textValue];
itemId = [[(id)[theDocumentElement getElementsByTagName: @"ItemId"] lastObject] textValue];
realCollectionId = [folderId realCollectionIdWithFolderType: &folderType];
realCollectionId = [self globallyUniqueIDToIMAPFolderName: realCollectionId type: folderType];
value = [theDocumentElement getElementsByTagName: @"ReplaceMime"];
// ReplaceMime isn't specified so we must NOT use the server copy
// but rather take the data as-is from the client.
if (![value count])
{
[self processSendMail: theDocumentElement
inResponse: theResponse];
return;
}
if (folderType == ActiveSyncMailFolder)
{
SOGoMailAccounts *accountsFolder;
@@ -1559,6 +1829,9 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
cmdName = [[theRequest uri] command];
// We make sure our cache table exists
[self ensureFolderTableExists];
//
// If the MS-ASProtocolVersion header is set to "12.1", the body of the SendMail request is
// is a "message/rfc822" payload - otherwise, it's a WBXML blob.
@@ -1630,4 +1903,72 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
return nil;
}
- (NSURL *) folderTableURL
{
NSString *urlString, *ocFSTableName;
NSMutableArray *parts;
SOGoUser *user;
if (!folderTableURL)
{
user = [context activeUser];
if (![user loginInDomain])
return nil;
urlString = [[user domainDefaults] folderInfoURL];
parts = [[urlString componentsSeparatedByString: @"/"]
mutableCopy];
[parts autorelease];
if ([parts count] == 5)
{
/* If "OCSFolderInfoURL" is properly configured, we must have 5
parts in this url. */
ocFSTableName = [NSString stringWithFormat: @"sogo_cache_folder_%@",
[[user loginInDomain] asCSSIdentifier]];
[parts replaceObjectAtIndex: 4 withObject: ocFSTableName];
folderTableURL
= [NSURL URLWithString: [parts componentsJoinedByString: @"/"]];
[folderTableURL retain];
}
else
[NSException raise: @"MAPIStoreIOException"
format: @"'OCSFolderInfoURL' is not set"];
}
return folderTableURL;
}
- (void) ensureFolderTableExists
{
GCSChannelManager *cm;
EOAdaptorChannel *channel;
NSString *tableName, *query;
GCSSpecialQueries *queries;
[self folderTableURL];
cm = [GCSChannelManager defaultChannelManager];
channel = [cm acquireOpenChannelForURL: folderTableURL];
/* FIXME: make use of [EOChannelAdaptor describeTableNames] instead */
tableName = [[folderTableURL path] lastPathComponent];
if (tableName &&
[channel evaluateExpressionX:
[NSString stringWithFormat: @"SELECT count(*) FROM %@",
tableName]])
{
queries = [channel specialQueries];
query = [queries createSOGoCacheGCSFolderTableWithName: tableName];
if ([channel evaluateExpressionX: query])
[NSException raise: @"MAPIStoreIOException"
format: @"could not create special table '%@'", tableName];
}
else
[channel cancelFetch];
[cm releaseChannel: channel];
}
@end
+28 -8
View File
@@ -30,6 +30,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#include "SOGoMailObject+ActiveSync.h"
#import <Foundation/NSArray.h>
#import <Foundation/NSAutoreleasePool.h>
#import <Foundation/NSCalendarDate.h>
#import <Foundation/NSDictionary.h>
#import <Foundation/NSException.h>
@@ -284,20 +285,31 @@ struct GlobalObjectId {
[self _sanitizedMIMEPart: body
performed: b];
}
else if ([body isKindOfClass: [NSData class]] &&
else if (([body isKindOfClass: [NSData class]] || [body isKindOfClass: [NSString class]]) &&
[[[thePart contentType] type] isEqualToString: @"text"] &&
([[[thePart contentType] subType] isEqualToString: @"plain"] || [[[thePart contentType] subType] isEqualToString: @"html"]))
{
// We make sure everything is encoded in UTF-8
NSString *charset, *s;
NGMimeType *mimeType;
int encoding;
NSString *s;
charset = [[thePart contentType] valueOfParameter: @"charset"];
encoding = [NGMimeType stringEncodingForCharset: charset];
if ([body isKindOfClass: [NSData class]])
{
NSString *charset;
int encoding;
s = [[NSString alloc] initWithData: body encoding: encoding];
AUTORELEASE(s);
charset = [[thePart contentType] valueOfParameter: @"charset"];
encoding = [NGMimeType stringEncodingForCharset: charset];
s = [[NSString alloc] initWithData: body encoding: encoding];
AUTORELEASE(s);
}
else
{
// Handle situations when SOPE stupidly returns us a NSString
// This can happen for Content-Type: text/plain, Content-Transfer-Encoding: 8bit
s = body;
}
if (s)
{
@@ -479,7 +491,9 @@ struct GlobalObjectId {
//
- (NSString *) activeSyncRepresentationInContext: (WOContext *) _context
{
NSAutoreleasePool *pool;
NSData *d, *globalObjId;
NSArray *attachmentKeys;
NSMutableString *s;
id value;
@@ -670,6 +684,10 @@ struct GlobalObjectId {
// Body - namespace 17
preferredBodyType = [[context objectForKey: @"BodyPreferenceType"] intValue];
// Make use of a local pool here as _preferredBodyDataUsingType:nativeType: will consume
// a significant amout of RAM and file descriptors
pool = [[NSAutoreleasePool alloc] init];
nativeBodyType = 1;
d = [self _preferredBodyDataUsingType: preferredBodyType nativeType: &nativeBodyType];
@@ -710,8 +728,10 @@ struct GlobalObjectId {
[s appendString: @"</Body>"];
}
DESTROY(pool);
// Attachments -namespace 16
NSArray *attachmentKeys = [self fetchFileAttachmentKeys];
attachmentKeys = [self fetchFileAttachmentKeys];
if ([attachmentKeys count])
{
int i;
+55
View File
@@ -0,0 +1,55 @@
/*
Copyright (c) 2014, Inverse inc.
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
* Neither the name of the Inverse inc. nor the
names of its contributors may be used to endorse or promote products
derived from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT HOLDER> BE LIABLE FOR ANY
DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#ifndef __SOGOSYNCCACHEOBJECT_H__
#define __SOGOSYNCCACHEOBJECT_H__
#import <Foundation/NSObject.h>
@class NSNull;
@interface SOGoSyncCacheObject : NSObject
{
id _uid;
id _sequence;
}
+ (id) syncCacheObjectWithUID: (id) theUID sequence: (id) theSequence;
- (id) uid;
- (void) setUID: (id) theUID;
- (id) sequence;
- (void) setSequence: (id) theSequence;
- (NSComparisonResult) compareUID: (SOGoSyncCacheObject *) theSyncCacheObject;
- (NSComparisonResult) compareSequence: (SOGoSyncCacheObject *) theSyncCacheObject;
@end
#endif
+122
View File
@@ -0,0 +1,122 @@
/*
Copyright (c) 2014, Inverse inc.
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
* Neither the name of the Inverse inc. nor the
names of its contributors may be used to endorse or promote products
derived from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT HOLDER> BE LIABLE FOR ANY
DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include "SOGoSyncCacheObject.h"
#import <Foundation/NSNull.h>
#import <Foundation/NSString.h>
@implementation SOGoSyncCacheObject
- (id) init
{
if ((self = [super init]))
{
_uid = nil;
_sequence = nil;
}
return self;
}
+ (id) syncCacheObjectWithUID: (id) theUID sequence: (id) theSequence;
{
id o;
o = [[self alloc] init];
[o setUID: theUID];
[o setSequence: theSequence];
return o;
}
- (void) dealloc
{
RELEASE(_uid);
RELEASE(_sequence);
[super dealloc];
}
- (id) uid
{
return _uid;
}
- (void) setUID: (id) theUID
{
ASSIGN(_uid, theUID);
}
- (id) sequence
{
return _sequence;
}
- (void) setSequence: (id) theSequence
{
ASSIGN(_sequence, theSequence);
}
- (NSComparisonResult) compareUID: (SOGoSyncCacheObject *) theSyncCacheObject
{
return [[self uid] compare: [theSyncCacheObject uid]];
}
//
// We might get NSNull values here, so if both are NSNull instances,
// we sort by UID. If both sequences are equal, we also sort by UID.
//
- (NSComparisonResult) compareSequence: (SOGoSyncCacheObject *) theSyncCacheObject
{
if ([[self sequence] isEqual: [NSNull null]] &&
[[theSyncCacheObject sequence] isEqual: [NSNull null]])
return [self compareUID: theSyncCacheObject];
if (![[self sequence] isEqual: [NSNull null]] && [[theSyncCacheObject sequence] isEqual: [NSNull null]])
return NSOrderedDescending;
if ([[self sequence] isEqual: [NSNull null]] && ![[theSyncCacheObject sequence] isEqual: [NSNull null]])
return NSOrderedAscending;
// Must check this here, to avoid comparing NSNull objects
if ([[self sequence] compare: [theSyncCacheObject sequence]] == NSOrderedSame)
return [self compareUID: theSyncCacheObject];
return [[self sequence] compare: [theSyncCacheObject sequence]];
}
- (NSString *) description
{
return [NSString stringWithFormat: @"%@-%@", _uid, _sequence];
}
@end
+1 -1
View File
@@ -160,7 +160,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
attendee = [attendees objectAtIndex: i];
[s appendFormat: @"<Attendee_Email xmlns=\"Calendar:\">%@</Attendee_Email>", [attendee rfc822Email]];
[s appendFormat: @"<Attendee_Name xmlns=\"Calendar:\">%@</Attendee_Name>", [attendee cn]];
[s appendFormat: @"<Attendee_Name xmlns=\"Calendar:\">%@</Attendee_Name>", [[attendee cn] activeSyncRepresentationInContext: context]];
attendee_status = [self _attendeeStatus: attendee];
+20 -3
View File
@@ -34,6 +34,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#import <Foundation/NSDictionary.h>
#import <Foundation/NSString.h>
#import <NGCards/iCalEvent.h>
#import <NGCards/iCalByDayMask.h>
#import "NSCalendarDate+ActiveSync.h"
@@ -77,10 +78,19 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
occurrences = [[self byDayMask] weekDayOccurrences];
v = 0;
for (i = 0; i < 7; i++)
if (occurrences)
{
if (occurrences[i])
v += (1 << i);
for (i = 0; i < 7; i++)
{
if (occurrences[0][i])
v += (1 << i);
}
}
else
{
// No byDayMask, we take the event's start date to compute the DayOfWeek
// 0 == Sunday, 6 == Saturday
v = (1 << [[[self parent] startDate] dayOfWeek]);
}
[s appendFormat: @"<Recurrence_DayOfWeek xmlns=\"Calendar:\">%d</Recurrence_DayOfWeek>", v];
@@ -125,6 +135,13 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
// FIXME
}
}
else
{
// Simple reccurrence rule of type "Monthly"
type = 2;
[s appendFormat: @"<Recurrence_DayOfMonth xmlns=\"Calendar:\">%d</Recurrence_DayOfMonth>",
[[[self parent] startDate] dayOfMonth]];
}
}
else if ([self frequency] == iCalRecurrenceFrequenceYearly)
{
+1677
View File
File diff suppressed because it is too large. Load diff
Binary file not shown.
+58
View File
@@ -1,3 +1,61 @@
2.2.5 (2014-06-05)
------------------
Enhancements
- new meta tag to tell IE to use the highest mode available
- updated Dutch, Finnish, German, and Polish translations
Bug fixes
- avoid crashing when we forward an email with no Subject header
- we no longer try to include attachments when replying to a mail
- fixed ActiveSync repetitive events issues with "Weekly" and "Monthly" ones
- fixed ActiveSync text/plain parts re-encoding issues for Outlook
2.2.4 (2014-05-29)
------------------
New features
- new print option in Calendar module
- now able to save unknown recipient emails to address book on send (#1496)
Enhancements
- Sieve folder encoding is now configurable (#2622)
- SOGo version is now displayed in preferences window (#2612)
- report Sieve error when saving preferences (#1046)
- added the SOGoMaximumSyncWindowSize system default to overwrite the
maximum number of items returned during an ActiveSync sync operation
- updated datepicker
- addressbooks properties are now accessible from a popup window
- extended events and tasks searches
- updated Czech, French, Hungarian, Polish, Russian, Slovak, Spanish (Argentina), and Spanish (Spain) translations
- added more sycned contact properties when using ActiveSync (#2775)
- now possible to configure the default subscribed resource name using SOGoSubscriptionFolderFormat
- now handle server-side folder updates using ActiveSync (#2688)
- updated CKEditor to version 4.4.1
Bug fixes
- fixed saved HTML content of draft when attaching a file
- fixed text nodes of HTML content handler by encoding HTML entities
- fixed iCal7 delegation issue with the "inbox" folder (#2489)
- fixed birth date validity checks (#1636)
- fixed URL handling (#2616)
- improved folder rename operations using ActiveSync (#2700)
- fixed SmartReply/Forward when ReplaceMime was omitted (#2680)
- fixed wrong generation of weekly repetitive events with ActiveSync (#2654)
- fixed incorrect XML data conversion with ActiveSync (#2695)
- fixed display of events having a category with HTML entities (#2703)
- fixed display of images in CSS background (#2437)
- fixed limitation of Sieve script size (#2745)
- fixed sync-token generation when no change was returned (#2492)
- fixed the IMAP copy/move operation between subfolders in different accounts
- fixed synchronization of seen/unseen status of msgs in Webmail (#2715)
- fixed focus of popup windows open through a contextual menu with Firefox on Windows 7
- fixed missing characters in shared folder names over ActiveSync (#2709)
- fixed reply and forward mail templates for Brazilian Portuguese (#2738)
- fixed newline in signature when forwarding a message as attachment in HTML mode (#2787)
- fixed restoration of options (priority & return receipt) when editing a draft (#193)
- fixed update of participation status via CalDAV (#2786)
2.2.3 (2014-04-03)
------------------
+1 -11
View File
@@ -46,11 +46,8 @@ $(SOGOBACKEND)_OBJC_FILES += \
MAPIStoreSamDBUtils.m \
MAPIStoreUserContext.m \
\
SOGoMAPIObject.m \
\
SOGoMAPIDBObject.m \
SOGoMAPIDBMessage.m \
SOGoMAPIDBFolder.m \
SOGoCacheGCSObject+MAPIStore.m \
\
MAPIStoreAppointmentWrapper.m \
MAPIStoreAttachment.m \
@@ -123,13 +120,6 @@ $(SOGOBACKEND)_OBJC_FILES += \
iCalEvent+MAPIStore.m \
iCalTimeZone+MAPIStore.m \
\
GCSSpecialQueries+OpenChange.m\
\
EOQualifier+MAPI.m \
\
EOBitmaskQualifier.m \
\
BSONCodec.m \
RTFHandler.m
+3
View File
@@ -3,3 +3,6 @@ all:: MAPIStorePropertySelectors.m MAPIStorePropertySelectors.h
MAPIStorePropertySelectors.m MAPIStorePropertySelectors.h: gen-property-selectors.py code-MAPIStorePropertySelectors.m code-MAPIStorePropertySelectors.h
@echo " Auto-generating MAPIStorePropertySelectors.[hm]..."
@$(PYTHON) ./gen-property-selectors.py -o MAPIStorePropertySelectors $(LIBMAPISTORE_CFLAGS)
distclean clean::
-rm -f MAPIStorePropertySelectors.m MAPIStorePropertySelectors.h
+6 -8
View File
@@ -1,8 +1,6 @@
/* MAPIStoreDBBaseContext.m - this file is part of SOGo
*
* Copyright (C) 2012 Inverse inc.
*
* Author: Wolfgang Sourdeau <wsourdeau@inverse.ca>
* Copyright (C) 2012-2014 Inverse inc.
*
* This file is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -32,7 +30,7 @@
#import "MAPIStoreDBFolder.h"
#import "MAPIStoreMapping.h"
#import "MAPIStoreUserContext.h"
#import "SOGoMAPIDBFolder.h"
#import <SOGo/SOGoCacheGCSFolder.h>
#import "MAPIStoreDBBaseContext.h"
@@ -60,7 +58,7 @@ static Class MAPIStoreDBFolderK;
- (void) ensureContextFolder
{
SOGoMAPIDBFolder *currentFolder;
SOGoCacheGCSFolder *currentFolder;
NSArray *parts;
NSMutableArray *folders;
NSString *folderName;
@@ -78,7 +76,7 @@ static Class MAPIStoreDBFolderK;
folderName = [parts objectAtIndex: count];
if ([folderName length] > 0)
{
currentFolder = [SOGoMAPIDBFolder objectWithName: folderName
currentFolder = [SOGoCacheGCSFolder objectWithName: folderName
inContainer: currentFolder];
[folders addObject: currentFolder];
}
@@ -98,11 +96,11 @@ static Class MAPIStoreDBFolderK;
- (id) rootSOGoFolder
{
SOGoMAPIDBFolder *folder;
SOGoCacheGCSFolder *folder;
[userContext ensureFolderTableExists];
folder = [SOGoMAPIDBFolder objectWithName: [isa MAPIModuleName]
folder = [SOGoCacheGCSFolder objectWithName: [isa MAPIModuleName]
inContainer: nil];
[folder setTableUrl: [userContext folderTableURL]];
// [folder reloadIfNeeded];
+9 -9
View File
@@ -32,7 +32,7 @@
#import <EOControl/EOQualifier.h>
#import <SOGo/SOGoFolder.h>
#import <SOGo/SOGoUser.h>
#import "EOQualifier+MAPI.h"
#import <SOGo/EOQualifier+SOGoCacheObject.h>
#import "MAPIStoreContext.h"
#import "MAPIStoreDBFolderTable.h"
#import "MAPIStoreDBMessage.h"
@@ -40,7 +40,7 @@
#import "MAPIStoreMapping.h"
#import "MAPIStoreTypes.h"
#import "MAPIStoreUserContext.h"
#import "SOGoMAPIDBFolder.h"
#import <SOGo/SOGoCacheGCSFolder.h>
#import "SOGoMAPIDBMessage.h"
#import "MAPIStoreDBFolder.h"
@@ -49,7 +49,7 @@
#include <mapistore/mapistore.h>
#include <mapistore/mapistore_errors.h>
static Class EOKeyValueQualifierK, SOGoMAPIDBFolderK, MAPIStoreDBFolderK;
static Class EOKeyValueQualifierK, SOGoCacheGCSFolderK, MAPIStoreDBFolderK;
static NSString *MAPIStoreRightReadItems = @"RightsReadItems";
static NSString *MAPIStoreRightCreateItems = @"RightsCreateItems";
@@ -66,7 +66,7 @@ static NSString *MAPIStoreRightFolderContact = @"RightsFolderContact";
+ (void) initialize
{
EOKeyValueQualifierK = [EOKeyValueQualifier class];
SOGoMAPIDBFolderK = [SOGoMAPIDBFolder class];
SOGoCacheGCSFolderK = [SOGoCacheGCSFolder class];
MAPIStoreDBFolderK = [MAPIStoreDBFolder class];
}
@@ -92,7 +92,7 @@ static NSString *MAPIStoreRightFolderContact = @"RightsFolderContact";
{
enum mapistore_error rc;
NSString *folderName, *nameInContainer;
SOGoMAPIDBFolder *newFolder;
SOGoCacheGCSFolder *newFolder;
struct SPropValue *value;
value = get_SPropValue_SRow (aRow, PidTagDisplayName);
@@ -111,7 +111,7 @@ static NSString *MAPIStoreRightFolderContact = @"RightsFolderContact";
{
nameInContainer = [NSString stringWithFormat: @"0x%.16"PRIx64,
(unsigned long long) newFID];
newFolder = [SOGoMAPIDBFolderK objectWithName: nameInContainer
newFolder = [SOGoCacheGCSFolderK objectWithName: nameInContainer
inContainer: sogoObject];
[newFolder reloadIfNeeded];
[[newFolder properties] setObject: folderName
@@ -181,7 +181,7 @@ static NSString *MAPIStoreRightFolderContact = @"RightsFolderContact";
[SOGoObject globallyUniqueObjectId]];
fsObject = [SOGoMAPIDBMessage objectWithName: newKey
inContainer: sogoObject];
[fsObject setObjectType: MAPIDBObjectTypeMessage];
[fsObject setObjectType: MAPIMessageCacheObject];
[fsObject reloadIfNeeded];
newMessage = [MAPIStoreDBMessage mapiStoreObjectWithSOGoObject: fsObject
inContainer: self];
@@ -198,7 +198,7 @@ static NSString *MAPIStoreRightFolderContact = @"RightsFolderContact";
ownerUser = [[self userContext] sogoUser];
if ([[context activeUser] isEqual: ownerUser]
|| [self subscriberCanReadMessages])
keys = [(SOGoMAPIDBFolder *) sogoObject childKeysOfType: MAPIDBObjectTypeMessage
keys = [(SOGoCacheGCSFolder *) sogoObject childKeysOfType: MAPIMessageCacheObject
includeDeleted: NO
matchingQualifier: qualifier
andSortOrderings: sortOrderings];
@@ -211,7 +211,7 @@ static NSString *MAPIStoreRightFolderContact = @"RightsFolderContact";
- (NSArray *) folderKeysMatchingQualifier: (EOQualifier *) qualifier
andSortOrderings: (NSArray *) sortOrderings
{
return [dbFolder childKeysOfType: MAPIDBObjectTypeFolder
return [dbFolder childKeysOfType: MAPIFolderCacheObject
includeDeleted: NO
matchingQualifier: qualifier
andSortOrderings: sortOrderings];
+4 -6
View File
@@ -1,8 +1,6 @@
/* MAPIStoreFallbackContext.m - this file is part of SOGo
*
* Copyright (C) 2011-2012 Inverse inc.
*
* Author: Wolfgang Sourdeau <wsourdeau@inverse.ca>
* Copyright (C) 2011-2014 Inverse inc.
*
* This file is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -26,7 +24,7 @@
#import "MAPIStoreUserContext.h"
#import "NSString+MAPIStore.h"
#import "SOGoMAPIDBFolder.h"
#import <SOGo/SOGoCacheGCSFolder.h>
#import "MAPIStoreFallbackContext.h"
@@ -51,7 +49,7 @@
inMemCtx: (TALLOC_CTX *) memCtx
{
struct mapistore_contexts_list *firstContext = NULL, *context;
SOGoMAPIDBFolder *root;
SOGoCacheGCSFolder *root;
NSArray *names;
NSUInteger count, max;
NSString *baseURL, *url, *name;
@@ -70,7 +68,7 @@
/* Maybe emsmdbp_provisioning should be fixed in order to only take the uri
returned above to avoid deleting its entries... */
root = [SOGoMAPIDBFolder objectWithName: [self MAPIModuleName]
root = [SOGoCacheGCSFolder objectWithName: [self MAPIModuleName]
inContainer: nil];
[root setOwner: userName];
userContext = [MAPIStoreUserContext userContextWithUsername: userName
+4 -6
View File
@@ -1,8 +1,6 @@
/* MAPIStoreFolder.h - this file is part of SOGo
*
* Copyright (C) 2011-2012 Inverse inc
*
* Author: Wolfgang Sourdeau <wsourdeau@inverse.ca>
* Copyright (C) 2011-2014 Inverse inc
*
* This file is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -38,7 +36,7 @@
@class MAPIStoreMessageTable;
@class MAPIStorePermissionsTable;
@class SOGoFolder;
@class SOGoMAPIDBFolder;
@class SOGoCacheGCSFolder;
@class SOGoMAPIDBMessage;
#import "MAPIStoreSOGoObject.h"
@@ -50,7 +48,7 @@
// NSArray *faiMessageKeys;
// NSArray *folderKeys;
SOGoMAPIDBFolder *dbFolder;
SOGoCacheGCSFolder *dbFolder;
// SOGoMAPIDBFolder *faiFolder;
// SOGoMAPIDBFolder *propsFolder;
// SOGoMAPIDBMessage *propsMessage;
@@ -60,7 +58,7 @@
- (void) setupAuxiliaryObjects;
- (SOGoMAPIDBFolder *) dbFolder;
- (SOGoCacheGCSFolder *) dbFolder;
- (NSArray *) activeMessageTables;
- (NSArray *) activeFAIMessageTables;
+8 -9
View File
@@ -1,8 +1,6 @@
/* MAPIStoreFolder.m - this file is part of SOGo
*
* Copyright (C) 2011-2012 Inverse inc
*
* Author: Wolfgang Sourdeau <wsourdeau@inverse.ca>
* Copyright (C) 2011-2014 Inverse inc
*
* This file is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -51,8 +49,9 @@
#import "NSDate+MAPIStore.h"
#import "NSString+MAPIStore.h"
#import "NSObject+MAPIStore.h"
#import "SOGoMAPIDBFolder.h"
#import <SOGo/SOGoCacheGCSFolder.h>
#import "SOGoMAPIDBMessage.h"
#import "SOGoCacheGCSObject+MAPIStore.h"
#include <gen_ndr/exchange.h>
@@ -120,7 +119,7 @@ Class NSExceptionK, MAPIStoreFAIMessageK, MAPIStoreMessageTableK, MAPIStoreFAIMe
[userContext ensureFolderTableExists];
ASSIGN (dbFolder,
[SOGoMAPIDBFolder objectWithName: folderName
[SOGoCacheGCSFolder objectWithName: folderName
inContainer: [container dbFolder]]);
[dbFolder setTableUrl: [userContext folderTableURL]];
if (!container && [path length] > 0)
@@ -138,7 +137,7 @@ Class NSExceptionK, MAPIStoreFAIMessageK, MAPIStoreMessageTableK, MAPIStoreFAIMe
// ASSIGN (propsMessage,
// [SOGoMAPIDBMessage objectWithName: @"properties.plist"
// inContainer: dbFolder]);
// [propsMessage setObjectType: MAPIDBObjectTypeInternal];
// [propsMessage setObjectType: MAPIInternalCacheObject];
// [propsMessage reloadIfNeeded];
[properties release];
properties = [dbFolder properties];
@@ -192,7 +191,7 @@ Class NSExceptionK, MAPIStoreFAIMessageK, MAPIStoreMessageTableK, MAPIStoreFAIMe
[super dealloc];
}
- (SOGoMAPIDBFolder *) dbFolder
- (SOGoCacheGCSFolder *) dbFolder
{
return dbFolder;
}
@@ -1221,7 +1220,7 @@ Class NSExceptionK, MAPIStoreFAIMessageK, MAPIStoreMessageTableK, MAPIStoreFAIMe
- (NSArray *) faiMessageKeysMatchingQualifier: (EOQualifier *) qualifier
andSortOrderings: (NSArray *) sortOrderings
{
return [dbFolder childKeysOfType: MAPIDBObjectTypeFAI
return [dbFolder childKeysOfType: MAPIFAICacheObject
includeDeleted: NO
matchingQualifier: qualifier
andSortOrderings: sortOrderings];
@@ -1531,7 +1530,7 @@ Class NSExceptionK, MAPIStoreFAIMessageK, MAPIStoreMessageTableK, MAPIStoreFAIMe
newKey = [NSString stringWithFormat: @"%@.plist",
[SOGoObject globallyUniqueObjectId]];
dbObject = [SOGoMAPIDBMessage objectWithName: newKey inContainer: dbFolder];
[dbObject setObjectType: MAPIDBObjectTypeFAI];
[dbObject setObjectType: MAPIFAICacheObject];
[dbObject setIsNew: YES];
newMessage = [MAPIStoreFAIMessageK mapiStoreObjectWithSOGoObject: dbObject
inContainer: self];
+1 -1
View File
@@ -74,7 +74,7 @@ static Class NSNumberK;
ASSIGN (versionsMessage,
[SOGoMAPIDBMessage objectWithName: @"versions.plist"
inContainer: dbFolder]);
[versionsMessage setObjectType: MAPIDBObjectTypeInternal];
[versionsMessage setObjectType: MAPIInternalCacheObject];
}
- (void) dealloc
+4 -4
View File
@@ -59,7 +59,7 @@
#import "NSData+MAPIStore.h"
#import "NSString+MAPIStore.h"
#import "SOGoMAPIDBMessage.h"
#import "SOGoMAPIDBFolder.h"
#import <SOGo/SOGoCacheGCSFolder.h>
#import "MAPIStoreMailVolatileMessage.h"
@@ -106,7 +106,7 @@ static Class SOGoMailFolderK, MAPIStoreMailFolderK, MAPIStoreOutboxFolderK;
ASSIGN (versionsMessage,
[SOGoMAPIDBMessage objectWithName: @"versions.plist"
inContainer: dbFolder]);
[versionsMessage setObjectType: MAPIDBObjectTypeInternal];
[versionsMessage setObjectType: MAPIInternalCacheObject];
}
- (BOOL) ensureFolderExists
@@ -1172,9 +1172,9 @@ _parseCOPYUID (NSString *line, NSArray **destUIDsP)
- (MAPIStoreMessage *) createMessage
{
SOGoMAPIObject *childObject;
SOGoCacheObject *childObject;
childObject = [SOGoMAPIObject objectWithName: [SOGoMAPIObject
childObject = [SOGoCacheObject objectWithName: [SOGoCacheObject
globallyUniqueObjectId]
inContainer: sogoObject];
return [MAPIStoreMailVolatileMessage
+1 -1
View File
@@ -62,7 +62,7 @@
#import "NSData+MAPIStore.h"
#import "NSObject+MAPIStore.h"
#import "NSString+MAPIStore.h"
#import "SOGoMAPIObject.h"
#import <SOGo/SOGoCacheObject.h>
#import "MAPIStoreMailVolatileMessage.h"
+1 -1
View File
@@ -25,7 +25,7 @@
#import <NGExtensions/NSObject+Logs.h>
#import <EOControl/EOQualifier.h>
#import "EOBitmaskQualifier.h"
#import <SOGo/EOBitmaskQualifier.h>
#import "MAPIStoreActiveTables.h"
#import "MAPIStoreObject.h"
#import "MAPIStoreTypes.h"
+3 -3
View File
@@ -42,7 +42,7 @@
#import <Mailer/SOGoMailAccount.h>
#import <Mailer/SOGoMailAccounts.h>
#import "GCSSpecialQueries+OpenChange.h"
#import <SOGo/GCSSpecialQueries+SOGoCacheObject.h>
#import "MAPIApplication.h"
#import "MAPIStoreAuthenticator.h"
#import "MAPIStoreMapping.h"
@@ -295,7 +295,7 @@ static NSMapTable *contextsTable = nil;
{
/* If "OCSFolderInfoURL" is properly configured, we must have 5
parts in this url. */
ocFSTableName = [NSString stringWithFormat: @"socfs_%@",
ocFSTableName = [NSString stringWithFormat: @"sogo_cache_folder_%@",
[username asCSSIdentifier]];
[parts replaceObjectAtIndex: 4 withObject: ocFSTableName];
folderTableURL
@@ -329,7 +329,7 @@ static NSMapTable *contextsTable = nil;
tableName]])
{
queries = [channel specialQueries];
query = [queries createOpenChangeFSTableWithName: tableName];
query = [queries createSOGoCacheGCSFolderTableWithName: tableName];
if ([channel evaluateExpressionX: query])
[NSException raise: @"MAPIStoreIOException"
format: @"could not create special table '%@'", tableName];
+2 -4
View File
@@ -1,8 +1,6 @@
/* dbmsgdump.m - this file is part of SOGo
*
* Copyright (C) 2011-2012 Inverse inc
*
* Author: Wolfgang Sourdeau <wsourdeau@inverse.ca>
* Copyright (C) 2011-2014 Inverse inc
*
* This file is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -33,7 +31,7 @@
#import <Foundation/NSValue.h>
#import <NGExtensions/NSNull+misc.h>
#import "BSONCodec.h"
#import <SOGo/BSONCodec.h>
const char *indentationStep = " ";
+32
View File
@@ -0,0 +1,32 @@
/* SOGoCacheGCSObject+MAPIStore.h - this file is part of SOGo
*
* Copyright (C) 2014 Inverse inc.
*
* This file is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3, or (at your option)
* any later version.
*
* This file is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; see the file COPYING. If not, write to
* the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
* Boston, MA 02111-1307, USA.
*/
#ifndef SOGOCACHEGCSOBJECTMAPISTORE
#define SOGOCACHEGCSOBJECTMAPISTORE
#include <SOGo/SOGoCacheGCSObject.h>
@interface SOGoCacheGCSObject (MAPIStore)
- (Class) mapistoreMessageClass;
@end
#endif // SOGOCACHEGCSOBJECTMAPISTORE
+68
View File
@@ -0,0 +1,68 @@
/* SOGoCacheGCSObject+MAPIStore.m - this file is part of SOGo
*
* Copyright (C) 2014 Inverse inc.
*
* This file is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3, or (at your option)
* any later version.
*
* This file is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; see the file COPYING. If not, write to
* the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
* Boston, MA 02111-1307, USA.
*/
#import <Foundation/NSDictionary.h>
#import <Foundation/NSException.h>
#import <Foundation/NSString.h>
#include "MAPIStoreTypes.h"
#include "SOGoCacheGCSObject+MAPIStore.h"
@implementation SOGoCacheGCSObject (MAPIStore)
- (Class) mapistoreMessageClass
{
NSString *className, *mapiMsgClass;
switch (objectType)
{
case MAPIMessageCacheObject:
mapiMsgClass = [properties
objectForKey: MAPIPropertyKey (PidTagMessageClass)];
if (mapiMsgClass)
{
if ([mapiMsgClass isEqualToString: @"IPM.StickyNote"])
className = @"MAPIStoreNotesMessage";
else
className = @"MAPIStoreDBMessage";
//[self logWithFormat: @"PidTagMessageClass = '%@', returning '%@'",
// mapiMsgClass, className];
}
else
{
//[self warnWithFormat: @"PidTagMessageClass is not set, falling back"
// @" to 'MAPIStoreDBMessage'"];
className = @"MAPIStoreDBMessage";
}
break;
case MAPIFAICacheObject:
className = @"MAPIStoreFAIMessage";
break;
default:
[NSException raise: @"MAPIStoreIOException"
format: @"message class should not be queried for objects"
@" of type '%d'", objectType];
}
return NSClassFromString (className);
}
@end
+3 -5
View File
@@ -1,8 +1,6 @@
/* SOGoMAPIDBMessage.h - this file is part of SOGo
*
* Copyright (C) 2012 Inverse inc.
*
* Author: Wolfgang Sourdeau <wsourdeau@inverse.ca>
* Copyright (C) 2012-2014 Inverse inc.
*
* This file is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -23,12 +21,12 @@
#ifndef SOGOMAPIDBMESSAGE_H
#define SOGOMAPIDBMESSAGE_H
#import "SOGoMAPIDBObject.h"
#import <SOGo/SOGoCacheGCSObject.h>
@class NSDate;
@class NSString;
@interface SOGoMAPIDBMessage : SOGoMAPIDBObject
@interface SOGoMAPIDBMessage : SOGoCacheGCSObject
@end
#endif /* SOGOMAPIDBMESSAGE_H */
+1 -1
View File
@@ -30,7 +30,7 @@
#import <NGExtensions/NSObject+Logs.h>
#import "SOGoMAPIDBFolder.h"
#import <SOGo/SOGoCacheGCSFolder.h>
#import "SOGoMAPIDBMessage.h"
+1 -1
View File
@@ -34,7 +34,7 @@
#import <SOGo/SOGoSystemDefaults.h>
#import "MAPIStoreUserContext.h"
#import "SOGoMAPIDBObject.h"
#import <SOGo/SOGoCacheGCSObject.h>
#import "NSObject+PropertyList.m"
+4 -4
View File
@@ -22,7 +22,7 @@ sogoUserDefaultsFile = os.path.expanduser("~sogo/GNUstep/Defaults/.GNUstepDefaul
# - removes the entry in samba's ldap tree via ldbedit (NOTYET)
# - remove the user's directory under mapistore/ and mapistore/SOGo
# - cleanup Junk Folders and Sync Issues imap folders
# - Delete the socfs_ table for the username.
# - Delete the sogo_cache_folder_ table for the username.
def usage():
print """
@@ -210,7 +210,7 @@ On RHEL, install it using 'yum install MySQL-python'"""
conn = MySQLdb.connect(host=dbhost, port=int(dbport), user=dbuser, passwd=dbpass, db=dbname)
c=conn.cursor()
tablename="socfs_%s" % (username)
tablename="sogo_cache_folder_%s" % (username)
c.execute("TRUNCATE TABLE %s" % tablename)
print "Table %s emptied"
@@ -225,7 +225,7 @@ On RHEL, install it using 'yum install python-pgsql'"""
raise Exception(msg)
conn = pg.connect(host=dbhost, port=int(dbport), user=dbuser, passwd=dbpass, dbname=dbname)
tablename = "socfs_%s" % username
tablename = "sogo_cache_folder_%s" % username
conn.query("DELETE FROM %s" % tablename)
print "Table '%s' emptied" % tablename
@@ -278,7 +278,7 @@ def sqlCleanup(username):
print "Starting SQL cleanup"
OCSFolderInfoURL = getOCSFolderInfoURL()
if OCSFolderInfoURL is None:
raise Exception("Couldn't fetch OCSFolderInfoURL or it is not set. the socfs_%s table should be truncated manually" % (username))
raise Exception("Couldn't fetch OCSFolderInfoURL or it is not set. the sogo_cache_folder_%s table should be truncated manually" % (username))
# postgresql://sogo:sogo@127.0.0.1:5432/sogo/sogo_folder_info
m = re.search("(.+)://(.+):(.+)@(.+):(\d+)/(.+)/(.+)", OCSFolderInfoURL)
+13 -6
View File
@@ -1,5 +1,5 @@
/*
Copyright (C) 2007-2013 Inverse inc.
Copyright (C) 2007-2014 Inverse inc.
Copyright (C) 2004-2005 SKYRIX Software AG
This file is part of SOGo.
@@ -1280,13 +1280,20 @@ firstInstanceCalendarDateRange: (NGCalendarDateRange *) fir
[baseWhere addObject: privacySQLString];
if ([title length])
[baseWhere
addObject: [NSString stringWithFormat: @"c_title isCaseInsensitiveLike: '%%%@%%'",
if ([filters length])
{
if ([filters isEqualToString:@"title_Category_Location"] || [filters isEqualToString:@"entireContent"])
{
[baseWhere addObject: [NSString stringWithFormat: @"(c_title isCaseInsensitiveLike: '%%%@%%' OR c_category isCaseInsensitiveLike: '%%%@%%' OR c_location isCaseInsensitiveLike: '%%%@%%')",
[title stringByReplacingString: @"'" withString: @"\\'\\'"],
[title stringByReplacingString: @"'" withString: @"\\'\\'"],
[title stringByReplacingString: @"'" withString: @"\\'\\'"]]];
}
}
else
[baseWhere addObject: [NSString stringWithFormat: @"c_title isCaseInsensitiveLike: '%%%@%%'",
[title stringByReplacingString: @"'" withString: @"\\'\\'"]]];
if ([filters length])
[baseWhere addObject: [NSString stringWithFormat: @"(%@)", filters]];
/* prepare mandatory fields */
fields = [NSMutableArray arrayWithArray: _fields];
@@ -1,7 +1,7 @@
/* SOGoAppointmentFolders.m - this file is part of SOGo
*
* Copyright (C) 2007-2013 Inverse inc.
* Copyright (C) 2007-2014 Inverse inc.
*
* This file is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -56,13 +56,6 @@
#import "SOGoAppointmentFolders.h"
@interface SOGoParentFolder (Private)
- (NSException *) _fetchPersonalFolders: (NSString *) sql
withChannel: (EOAdaptorChannel *) fc;
@end
static SoSecurityManager *sm = nil;
@implementation SOGoAppointmentFolders
@@ -596,8 +589,9 @@ static SoSecurityManager *sm = nil;
}
}
- (NSException *) _fetchPersonalFolders: (NSString *) sql
withChannel: (EOAdaptorChannel *) fc
- (NSException *) fetchSpecialFolders: (NSString *) sql
withChannel: (EOAdaptorChannel *) fc
andFolderType: (SOGoFolderType) folderType
{
BOOL isWebRequest;
NSException *error;
@@ -607,7 +601,7 @@ static SoSecurityManager *sm = nil;
SOGoWebAppointmentFolder *webFolder;
NSString *name;
error = [super _fetchPersonalFolders: sql withChannel: fc];
error = [super fetchSpecialFolders: sql withChannel: fc andFolderType: folderType];
if (!error)
{
isWebRequest = [[context request] handledByDefaultHandler];
@@ -1,8 +1,6 @@
/* SOGoAppointmentInboxFolder.m - this file is part of SOGo
*
* Copyright (C) 2010 Inverse inc.
*
* Author: Wolfgang Sourdeau <wsourdeau@inverse.ca>
* Copyright (C) 2010-2014 Inverse inc.
*
* This file is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -148,14 +148,14 @@
object = [folder lookupName: nameInContainer
inContext: context
acquire: NO];
if ([object isKindOfClass: [NSException class]])
if ([object isKindOfClass: [NSException class]] || [object isNew])
{
possibleName = [folder resourceNameForEventUID: eventUID];
if (possibleName)
{
object = [folder lookupName: possibleName
inContext: context acquire: NO];
if ([object isKindOfClass: [NSException class]])
if ([object isKindOfClass: [NSException class]] || [object isNew])
object = nil;
}
else
+1 -1
View File
@@ -67,7 +67,7 @@
SOGoAppointmentInboxFolder = {
superclass = "SOGoAppointmentFolder";
defaultRoles = {
"Access Contents Information" = ( "Owner" );
"Access Contents Information" = ( "Owner", "AuthorizedSubscriber" );
};
};
SOGoCalendarComponent = {
@@ -1 +1,2 @@
"Personal Address Book" = "Osobní kontakty";
"Collected Address Book" = "Sebrané kontakty";
@@ -1 +1,2 @@
"Personal Address Book" = "Persoonlijk adresboek";
"Collected Address Book" = "Verzamelde adressen";
@@ -1 +1,2 @@
"Personal Address Book" = "Personal Address Book";
"Collected Address Book" = "Collected Address Book";
@@ -1 +1,2 @@
"Personal Address Book" = "Henkilökohtainen osoitekirja";
"Collected Address Book" = "Kerätty osoitekirja";
@@ -1 +1,2 @@
"Personal Address Book" = "Carnet d'adresses personnel";
"Collected Address Book" = "Adresses collectées";
@@ -1 +1,2 @@
"Personal Address Book" = "Persönliches Adressbuch";
"Collected Address Book" = "Gesammelte Adressen";
@@ -1 +1,2 @@
"Personal Address Book" = "Személyes címjegyzék";
"Collected Address Book" = "Összegyűjtöt címek jegyzéke";
@@ -1 +1,2 @@
"Personal Address Book" = "Osobista książka adresowa";
"Collected Address Book" = "Książka zebranych adresów";
@@ -1 +1,2 @@
"Personal Address Book" = "Личная адресная книга";
"Collected Address Book" = "Собранные адреса";
+7
View File
@@ -25,12 +25,19 @@
@interface SOGoContactFolders : SOGoParentFolder
- (NSString *) defaultFolderName;
- (NSString *) collectedFolderName;
- (NSException *) renameLDAPAddressBook: (NSString *) sourceID
withDisplayName: (NSString *) newDisplayName;
- (NSException *) removeLDAPAddressBook: (NSString *) sourceID;
- (NSDictionary *) systemSources;
- (NSArray *) allContactsFromFilter: (NSString *) theFilter
excludeGroups: (BOOL) excludeGroups
excludeLists: (BOOL) excludeLists;
@end
#endif /* SOGOCONTACTFOLDERS_H */
+136 -1
View File
@@ -22,12 +22,18 @@
#import <Foundation/NSDictionary.h>
#import <Foundation/NSString.h>
#import <Foundation/NSEnumerator.h>
#import <Foundation/NSSortDescriptor.h>
#import <NGObjWeb/WOContext+SoObjects.h>
#import <NGObjWeb/NSException+HTTP.h>
#import <DOM/DOMElement.h>
#import <DOM/DOMProtocols.h>
#import <GDLContentStore/GCSChannelManager.h>
#import <GDLContentStore/GCSFolderManager.h>
#import <GDLContentStore/NSURL+GCS.h>
#import <GDLAccess/EOAdaptorChannel.h>
#import <SOGo/NSObject+DAV.h>
#import <SOGo/SOGoUser.h>
#import <SOGo/SOGoUserDefaults.h>
@@ -40,10 +46,17 @@
#import "SOGoContactFolders.h"
Class SOGoContactSourceFolderK;
#define XMLNS_INVERSEDAV @"urn:inverse:params:xml:ns:inverse-dav"
@implementation SOGoContactFolders
+ (void) initialize
{
SOGoContactSourceFolderK = [SOGoContactSourceFolder class];
}
+ (NSString *) gcsFolderType
{
return @"Contact";
@@ -110,6 +123,38 @@
return result;
}
- (NSException *) appendCollectedSources
{
GCSChannelManager *cm;
EOAdaptorChannel *fc;
NSURL *folderLocation;
NSString *sql, *gcsFolderType;
NSException *error;
cm = [GCSChannelManager defaultChannelManager];
folderLocation = [[GCSFolderManager defaultFolderManager] folderInfoLocation];
fc = [cm acquireOpenChannelForURL: folderLocation];
if ([fc isOpen])
{
gcsFolderType = [[self class] gcsFolderType];
sql = [NSString stringWithFormat: (@"SELECT c_path4 FROM %@"
@" WHERE c_path2 = '%@'"
@" AND c_folder_type = '%@'"),
[folderLocation gcsTableName], owner, gcsFolderType];
error = [super fetchSpecialFolders: sql withChannel: fc andFolderType: SOGoCollectedFolder];
[cm releaseChannel: fc];
}
else
error = [NSException exceptionWithName: @"SOGoDBException"
reason: @"database connection could not be open"
userInfo: nil];
return error;
}
- (NSDictionary *) systemSources
{
NSMutableDictionary *systemSources;
@@ -225,7 +270,7 @@
if ([sourceID isEqualToString: @"personal"])
result = [NSException exceptionWithHTTPStatus: 403
reason: @"folder 'personal' cannot be deleted"];
reason: (@"folder '%@' cannot be deleted", sourceID)];
else
{
result = nil;
@@ -250,6 +295,11 @@
return [self labelForKey: @"Personal Address Book"];
}
- (NSString *) collectedFolderName
{
return [self labelForKey: @"Collected Address Book"];
}
- (NSArray *) toManyRelationshipKeys
{
NSMutableArray *keys;
@@ -371,4 +421,89 @@
asWebDAVValue];
}
- (NSArray *) allContactsFromFilter: (NSString *) theFilter
excludeGroups: (BOOL) excludeGroups
excludeLists: (BOOL) excludeLists
{
SOGoFolder <SOGoContactFolder> *folder;
NSString *mail, *domain;
NSArray *folders, *contacts, *descriptors, *sortedContacts;
NSMutableArray *sortedFolders;
NSMutableDictionary *contact, *uniqueContacts;
unsigned int i, j, max;
NSSortDescriptor *commonNameDescriptor;
// NSLog(@"Search all contacts: %@", searchText);
domain = [[context activeUser] domain];
folders = nil;
NS_DURING
folders = [self subFolders];
NS_HANDLER
/* We need to specifically test for @"SOGoDBException", which is
raised explicitly in SOGoParentFolder. Any other exception should
be re-raised. */
if ([[localException name] isEqualToString: @"SOGoDBException"])
folders = nil;
else
[localException raise];
NS_ENDHANDLER;
max = [folders count];
sortedFolders = [NSMutableArray arrayWithCapacity: max];
uniqueContacts = [NSMutableDictionary dictionary];
for (i = 0; i < max; i++)
{
folder = [folders objectAtIndex: i];
/* We first search in LDAP folders (in case of duplicated entries in GCS folders) */
if ([folder isKindOfClass: SOGoContactSourceFolderK])
[sortedFolders insertObject: folder atIndex: 0];
else
[sortedFolders addObject: folder];
}
for (i = 0; i < max; i++)
{
folder = [sortedFolders objectAtIndex: i];
//NSLog(@" Address book: %@ (%@)", [folder displayName], [folder class]);
contacts = [folder lookupContactsWithFilter: theFilter
onCriteria: @"name_or_address"
sortBy: @"c_cn"
ordering: NSOrderedAscending
inDomain: domain];
for (j = 0; j < [contacts count]; j++)
{
contact = [contacts objectAtIndex: j];
mail = [contact objectForKey: @"c_mail"];
//NSLog(@" found %@ (%@) ? %@", [contact objectForKey: @"c_name"], mail,
// [contact description]);
if (!excludeLists && [[contact objectForKey: @"c_component"]
isEqualToString: @"vlist"])
{
[contact setObject: [folder nameInContainer]
forKey: @"container"];
[uniqueContacts setObject: contact
forKey: [contact objectForKey: @"c_name"]];
}
else if ([mail length]
&& [uniqueContacts objectForKey: mail] == nil
&& !(excludeGroups && [contact objectForKey: @"isGroup"]))
[uniqueContacts setObject: contact forKey: mail];
}
}
if ([uniqueContacts count] > 0)
{
// Sort the contacts by display name
commonNameDescriptor = [[NSSortDescriptor alloc] initWithKey: @"c_cn"
ascending:YES];
descriptors = [NSArray arrayWithObjects: commonNameDescriptor, nil];
[commonNameDescriptor release];
sortedContacts = [[uniqueContacts allValues]
sortedArrayUsingDescriptors: descriptors];
}
else
sortedContacts = [NSArray array];
return sortedContacts;
}
@end
@@ -1 +1,2 @@
"Personal Address Book" = "Osobné kontakty";
"Collected Address Book" = "Pozbierané osobné kontakty";
@@ -1 +1,2 @@
"Personal Address Book" = "Libreta personal de direcciones";
"Collected Address Book" = "Direcciones recopiladas";
@@ -1 +1,2 @@
"Personal Address Book" = "Libreta personal de direcciones";
"Collected Address Book" = "Libreta de direcciones recogidas";
@@ -1,2 +1,2 @@
"SieveFolderName" = "Filtry";
"OtherUsersFolderName" = "Ostatní uživatelé";
"SharedFoldersName" = "Sdílené složky";
+9 -1
View File
@@ -417,7 +417,15 @@
{
if (!ignoreContent)
{
[result appendString: [NSString stringWithCharacters: characters length: length]];
// Append a text node
if (ignoreContentTags)
// We are converting a HTML message to plain text (htmlToTextContentHandler):
// include the HTML tags in the text
[result appendString: [NSString stringWithCharacters: characters length: length]];
else
// We are sanitizing an HTML message (sanitizerContentHandler):
// escape the HTML entitites so they are visible
[result appendString: [[NSString stringWithCharacters: characters length: length] stringByEscapingHTMLString]];
}
}
@@ -1,2 +1,2 @@
"SieveFolderName" = "Filtry";
"OtherUsersFolderName" = "Inni użytkownicy";
"SharedFoldersName" = "Foldery współdzielone";
+10 -4
View File
@@ -1,20 +1,21 @@
/*
Copyright (C) 2007-2014 Inverse inc.
Copyright (C) 2004-2005 SKYRIX Software AG
This file is part of OpenGroupware.org.
This file is part of SOGo.
OGo is free software; you can redistribute it and/or modify it under
SOGo is free software; you can redistribute it and/or modify it under
the terms of the GNU Lesser General Public License as published by the
Free Software Foundation; either version 2, or (at your option) any
later version.
OGo is distributed in the hope that it will be useful, but WITHOUT ANY
SOGo is distributed in the hope that it will be useful, but WITHOUT ANY
WARRANTY; without even the implied warranty of MERCHANTABILITY or
FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
License for more details.
You should have received a copy of the GNU Lesser General Public
License along with OGo; see the file COPYING. If not, write to the
License along with SOGo; see the file COPYING. If not, write to the
Free Software Foundation, 59 Temple Place - Suite 330, Boston, MA
02111-1307, USA.
*/
@@ -78,6 +79,9 @@
- (BOOL) isHTML;
/* for replies and forwards */
- (NSString *) inReplyTo;
- (void) setInReplyTo: (NSString *) newInReplyTo;
- (void) setSourceURL: (NSString *) newSurceURL;
- (void) setSourceFlag: (NSString *) newSourceFlag;
- (void) setSourceFolder: (NSString *) newSourceFolder;
@@ -102,6 +106,8 @@
- (NSData *) mimeMessageAsData;
/* operations */
- (NSArray *) allRecipients;
- (NSArray *) allBareRecipients;
- (NSException *) delete;
- (NSException *) sendMail;
+156 -29
View File
@@ -45,6 +45,7 @@
#import <NGImap4/NGImap4Client.h>
#import <NGImap4/NGImap4Envelope.h>
#import <NGImap4/NGImap4EnvelopeAddress.h>
#import <NGMail/NGMailAddressParser.h>
#import <NGMail/NGMimeMessage.h>
#import <NGMail/NGMimeMessageGenerator.h>
#import <NGMime/NGMimeBodyPart.h>
@@ -63,6 +64,11 @@
#import <SOGo/SOGoUser.h>
#import <SOGo/SOGoUserDefaults.h>
#import <NGCards/NGVCard.h>
#import <Contacts/SOGoContactFolders.h>
#import <Contacts/SOGoContactGCSEntry.h>
#import "NSData+Mail.h"
#import "NSString+Mail.h"
#import "SOGoMailAccount.h"
@@ -260,7 +266,7 @@ static NSString *userAgent = nil;
{
id headerValue;
unsigned int count;
NSString *messageID, *priority, *pureSender, *replyTo;
NSString *messageID, *priority, *pureSender, *replyTo, *receipt;
for (count = 0; count < 8; count++)
{
@@ -279,26 +285,57 @@ static NSString *userAgent = nil;
[headers setObject: messageID forKey: @"message-id"];
}
priority = [newHeaders objectForKey: @"priority"];
if (!priority || [priority isEqualToString: @"NORMAL"])
priority = [newHeaders objectForKey: @"X-Priority"];
if (priority)
{
[headers removeObjectForKey: @"X-Priority"];
}
else if ([priority isEqualToString: @"HIGHEST"])
{
[headers setObject: @"1 (Highest)" forKey: @"X-Priority"];
}
else if ([priority isEqualToString: @"HIGH"])
{
[headers setObject: @"2 (High)" forKey: @"X-Priority"];
}
else if ([priority isEqualToString: @"LOW"])
{
[headers setObject: @"4 (Low)" forKey: @"X-Priority"];
// newHeaders come from MIME message; convert X-Priority to Web representation
[headers setObject: priority forKey: @"X-Priority"];
[headers removeObjectForKey: @"priority"];
if ([priority isEqualToString: @"1 (Highest)"])
{
[headers setObject: @"HIGHEST" forKey: @"priority"];
}
else if ([priority isEqualToString: @"2 (High)"])
{
[headers setObject: @"HIGH" forKey: @"priority"];
}
else if ([priority isEqualToString: @"4 (Low)"])
{
[headers setObject: @"LOW" forKey: @"priority"];
}
else if ([priority isEqualToString: @"5 (Lowest)"])
{
[headers setObject: @"LOWEST" forKey: @"priority"];
}
}
else
{
[headers setObject: @"5 (Lowest)" forKey: @"X-Priority"];
// newHeaders come from Web form; convert priority to MIME header representation
priority = [newHeaders objectForKey: @"priority"];
if (!priority || [priority isEqualToString: @"NORMAL"])
{
[headers removeObjectForKey: @"X-Priority"];
}
else if ([priority isEqualToString: @"HIGHEST"])
{
[headers setObject: @"1 (Highest)" forKey: @"X-Priority"];
}
else if ([priority isEqualToString: @"HIGH"])
{
[headers setObject: @"2 (High)" forKey: @"X-Priority"];
}
else if ([priority isEqualToString: @"LOW"])
{
[headers setObject: @"4 (Low)" forKey: @"X-Priority"];
}
else
{
[headers setObject: @"5 (Lowest)" forKey: @"X-Priority"];
}
if (priority)
{
[headers setObject: priority forKey: @"priority"];
}
}
replyTo = [headers objectForKey: @"replyTo"];
@@ -308,14 +345,30 @@ static NSString *userAgent = nil;
}
[headers removeObjectForKey: @"replyTo"];
if ([[newHeaders objectForKey: @"receipt"] isEqualToString: @"true"])
receipt = [newHeaders objectForKey: @"Disposition-Notification-To"];
if ([receipt length] > 0)
{
pureSender = [[newHeaders objectForKey: @"from"] pureEMailAddress];
if (pureSender)
[headers setObject: pureSender forKey: @"Disposition-Notification-To"];
[headers setObject: @"true" forKey: @"receipt"];
[headers setObject: receipt forKey: @"Disposition-Notification-To"];
}
else
[headers removeObjectForKey: @"Disposition-Notification-To"];
{
receipt = [newHeaders objectForKey: @"receipt"];
if ([receipt isEqualToString: @"true"])
{
[headers setObject: receipt forKey: @"receipt"];
pureSender = [[newHeaders objectForKey: @"from"] pureEMailAddress];
if (pureSender)
{
[headers setObject: pureSender forKey: @"Disposition-Notification-To"];
}
}
else
{
[headers removeObjectForKey: @"receipt"];
[headers removeObjectForKey: @"Disposition-Notification-To"];
}
}
}
- (NSDictionary *) headers
@@ -343,6 +396,11 @@ static NSString *userAgent = nil;
return isHTML;
}
- (NSString *) inReplyTo
{
return inReplyTo;
}
- (void) setInReplyTo: (NSString *) newInReplyTo
{
ASSIGN (inReplyTo, newInReplyTo);
@@ -799,8 +857,10 @@ static NSString *userAgent = nil;
{
NSString *subject, *msgid;
NSMutableDictionary *info;
NSDictionary *h;
NSMutableArray *addresses;
NGImap4Envelope *sourceEnvelope;
id priority, receipt;
[sourceMail fetchCoreInfos];
@@ -830,6 +890,15 @@ static NSString *userAgent = nil;
[self _addEMailsOfAddresses: [sourceEnvelope replyTo] toArray: addresses];
if ([addresses count] > 0)
[info setObject: addresses forKey: @"replyTo"];
h = [sourceMail mailHeaders];
priority = [h objectForKey: @"x-priority"];
if ([priority isNotEmpty] && [priority isKindOfClass: [NSString class]])
[info setObject: (NSString*)priority forKey: @"X-Priority"];
receipt = [h objectForKey: @"disposition-notification-to"];
if ([receipt isNotEmpty] && [receipt isKindOfClass: [NSString class]])
[info setObject: (NSString*)receipt forKey: @"Disposition-Notification-To"];
[self setHeaders: info];
[self setText: [sourceMail contentForEditing]];
@@ -857,7 +926,7 @@ static NSString *userAgent = nil;
sourceEnvelope = [sourceMail envelope];
[self _fillInReplyAddresses: info replyToAll: toAll
envelope: sourceEnvelope];
envelope: sourceEnvelope];
msgID = [sourceEnvelope messageID];
if ([msgID length] > 0)
[self setInReplyTo: msgID];
@@ -913,7 +982,7 @@ static NSString *userAgent = nil;
@"message/rfc822", @"mimetype",
nil];
[self saveAttachment: [sourceMail content]
withMetadata: attachment];
withMetadata: attachment];
}
[self storeInfo];
@@ -1538,10 +1607,11 @@ static NSString *userAgent = nil;
/* add subject */
if ([(s = [headers objectForKey: @"subject"]) length] > 0)
[map setObject: [s asQPSubjectString: @"utf-8"]
forKey: @"subject"];
[map setObject: [headers objectForKey: @"message-id"]
forKey: @"message-id"];
forKey: @"subject"];
if ([(s = [headers objectForKey: @"message-id"]) length] > 0)
[map setObject: s
forKey: @"message-id"];
/* add standard headers */
dateString = [[NSCalendarDate date] rfc822DateString];
@@ -1660,7 +1730,7 @@ static NSString *userAgent = nil;
{
recipients = [headers objectForKey: fieldNames[count]];
if ([recipients count] > 0)
[allRecipients addObjectsFromArray: recipients];
[allRecipients addObjectsFromArray: recipients];
}
return allRecipients;
@@ -1689,6 +1759,63 @@ static NSString *userAgent = nil;
//
- (NSException *) sendMail
{
SOGoUserDefaults *ud;
ud = [[context activeUser] userDefaults];
if ([ud mailAddOutgoingAddresses])
{
SOGoContactFolders *contactFolders;
NGMailAddressParser *parser;
id parsedRecipient;
SOGoContactFolder *folder;
SOGoContactGCSEntry *newContact;
NGVCard *card;
Class contactGCSEntry;
NSMutableArray *recipients;
NSString *recipient, *emailAddress, *addressBook, *uid;
NSArray *matchingContacts;
int i;
// Get all the addressbooks
contactFolders = [[[context activeUser] homeFolderInContext: context]
lookupName: @"Contacts"
inContext: context
acquire: NO];
// Get all the recipients from the current email
recipients = [self allRecipients];
for (i = 0; i < [recipients count]; i++)
{
// The address contains a string. ex: "John Doe <sogo1@exemple.com>"
recipient = [recipients objectAtIndex: i];
parser = [NGMailAddressParser mailAddressParserWithString: recipient];
parsedRecipient = [parser parse];
emailAddress = [parsedRecipient address];
matchingContacts = [contactFolders allContactsFromFilter: emailAddress
excludeGroups: YES
excludeLists: YES];
}
// If we don't get any results from the autocompletion code, we add it..
if ([matchingContacts count] == 0)
{
// Get the selected addressbook from the user preferences where the new address will be added
addressBook = [ud selectedAddressBook];
folder = [contactFolders lookupName: addressBook inContext: context acquire: NO];
uid = [folder globallyUniqueObjectId];
if (folder && uid)
{
card = [NGVCard cardWithUid: uid];
[card addEmail: emailAddress types: nil];
contactGCSEntry = NSClassFromString(@"SOGoContactGCSEntry");
newContact = [contactGCSEntry objectWithName: uid
inContainer: folder];
[newContact setIsNew: YES];
[newContact saveContentString: [card versitString]];
}
}
}
return [self sendMailAndCopyToSent: YES];
}
+1 -3
View File
@@ -1,8 +1,6 @@
/* SOGoDraftsFolder.m - this file is part of SOGo
*
* Copyright (C) 2007 Inverse inc.
*
* Author: Wolfgang Sourdeau <wsourdeau@inverse.ca>
* Copyright (C) 2007-2014 Inverse inc.
*
* This file is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
+2
View File
@@ -84,6 +84,8 @@ typedef enum {
- (NSArray *) allFolderPaths;
- (NSArray *) allFoldersMetadata;
- (NSDictionary *) imapFolderGUIDs;
- (BOOL) isInDraftsFolder;
/* special folders */
+58
View File
@@ -60,6 +60,8 @@
#import "SOGoUser+Mailer.h"
#import "SOGoMailAccount.h"
#import <Foundation/NSProcessInfo.h>
#define XMLNS_INVERSEDAV @"urn:inverse:params:xml:ns:inverse-dav"
@@ -659,6 +661,62 @@ static NSString *inboxFolderName = @"INBOX";
return password;
}
- (NSDictionary *) imapFolderGUIDs
{
NSDictionary *result, *nresult, *folderData;
NSMutableDictionary *folders;
NGImap4Client *client;
SOGoUserDefaults *ud;
NSArray *folderList;
NSEnumerator *e;
NSString *guid;
id object;
BOOL subscribedOnly;
ud = [[context activeUser] userDefaults];
subscribedOnly = [ud mailShowSubscribedFoldersOnly];
folderList = [[self imap4Connection] allFoldersForURL: [self imap4URL]
onlySubscribedFolders: subscribedOnly];
folders = [NSMutableDictionary dictionary];
client = [[self imap4Connection] client];
result = [client annotation: @"*" entryName: @"/comment" attributeName: @"value.priv"];
e = [folderList objectEnumerator];
while (object = [e nextObject])
{
guid = [[[[result objectForKey: @"FolderList"] objectForKey: [object substringFromIndex: 1]] objectForKey: @"/comment"] objectForKey: @"value.priv"];
if (!guid)
{
guid = [[NSProcessInfo processInfo] globallyUniqueString];
nresult = [client annotation: [object substringFromIndex: 1] entryName: @"/comment" attributeName: @"value.priv" attributeValue: guid];
if (![[nresult objectForKey: @"result"] boolValue])
{
// Need to implement X-GUID query for Dovecot - this requires modification in SOPE to support following command:
// 1 list "" "*" return (status (x-guid)) -> this would avoid firing a command per folder to IMAP
nresult = [client status: [object substringFromIndex: 1] flags: [NSArray arrayWithObject: @"x-guid"]];
guid = [nresult objectForKey: @"x-guid"];
if (!guid)
{
guid = [NSString stringWithFormat: @"%@", [object substringFromIndex: 1]];
}
}
}
[folders setObject: guid forKey: [object substringFromIndex: 1]];
}
return folders;
}
/* name lookup */
- (id) lookupName: (NSString *) _key
@@ -13,6 +13,11 @@ from: WOString {
escapeHTML = NO;
}
newLine: WOString {
value = newLine;
escapeHTML = NO;
}
hasReplyTo: WOConditional {
condition = hasReplyTo;
}
@@ -22,6 +22,11 @@ from: WOString {
escapeHTML = NO;
}
newLine: WOString {
value = newLine;
escapeHTML = NO;
}
hasReplyTo: WOConditional {
condition = hasReplyTo;
}
+1 -2
View File
@@ -1,5 +1,5 @@
/*
Copyright (C) 2009-2011 Inverse inc.
Copyright (C) 2009-2014 Inverse inc.
Copyright (C) 2004-2005 SKYRIX Software AG
This file is part of SOGo
@@ -94,7 +94,6 @@
- (NSCalendarDate *) mostRecentMessageDate;
- (NSString *) davCollectionTagFromId: (NSString *) theId;
- (NSString *) davCollectionTag;
- (NSArray *) syncTokenFieldsWithProperties: (NSDictionary *) properties
+98 -28
View File
@@ -72,6 +72,28 @@
static NSString *defaultUserID = @"anyone";
static NSComparisonResult
_compareFetchResultsByMODSEQ (id entry1, id entry2, void *data)
{
static NSNumber *zeroNumber = nil;
NSNumber *modseq1, *modseq2;
if (!zeroNumber)
{
zeroNumber = [NSNumber numberWithUnsignedLongLong: 0];
[zeroNumber retain];
}
modseq1 = [entry1 objectForKey: @"modseq"];
if (!modseq1)
modseq1 = zeroNumber;
modseq2 = [entry2 objectForKey: @"modseq"];
if (!modseq2)
modseq2 = zeroNumber;
return [modseq1 compare: modseq2];
}
@interface NGImap4Connection (PrivateMethods)
- (NSString *) imap4FolderNameForURL: (NSURL *) url;
@@ -291,10 +313,18 @@ static NSString *defaultUserID = @"anyone";
path = [[imap4URL path] stringByDeletingLastPathComponent];
if (![path hasSuffix: @"/"])
path = [path stringByAppendingString: @"/"];
destURL = [[NSURL alloc] initWithScheme: [imap4URL scheme]
host: [imap4URL host]
path: [NSString stringWithFormat: @"%@%@",
path, [newName stringByEncodingImap4FolderName]]];
// If new name contains the path - dont't need to add
if ([newName rangeOfString: @"/"].location == NSNotFound)
destURL = [[NSURL alloc] initWithScheme: [imap4URL scheme]
host: [imap4URL host]
path: [NSString stringWithFormat: @"%@%@",
path, [newName stringByEncodingImap4FolderName]]];
else
destURL = [[NSURL alloc] initWithScheme: [imap4URL scheme]
host: [imap4URL host]
path: [NSString stringWithFormat: @"%@",
[newName stringByEncodingImap4FolderName]]];
[destURL autorelease];
error = [imap4 moveMailboxAtURL: imap4URL
toURL: destURL];
@@ -652,8 +682,12 @@ static NSString *defaultUserID = @"anyone";
// Destination folder is in a different account
SOGoMailAccounts *accounts;
SOGoMailAccount *account;
accounts = [[self container] container];
SOGoUserFolder *userFolder;
userFolder = [[context activeUser] homeFolderInContext: context];
accounts = [userFolder lookupName: @"Mail" inContext: context acquire: NO];
account = [accounts lookupName: [folders objectAtIndex: 1] inContext: localContext acquire: NO];
if ([account isKindOfClass: [NSException class]])
{
result = [NSException exceptionWithHTTPStatus: 500
@@ -796,8 +830,9 @@ static NSString *defaultUserID = @"anyone";
- (NSArray *) fetchUIDs: (NSArray *) _uids
parts: (NSArray *) _parts
{
return [[self imap4Connection] fetchUIDs: _uids inURL: [self imap4URL]
parts: _parts];
return [[self imap4Connection] fetchUIDs: _uids
inURL: [self imap4URL]
parts: _parts];
}
- (NSArray *) fetchUIDsOfVanishedItems: (uint64_t) modseq
@@ -823,7 +858,7 @@ static NSString *defaultUserID = @"anyone";
toFolderURL: [self imap4URL]];
return [NSException exceptionWithHTTPStatus: 502 /* Bad Gateway */
reason: [NSString stringWithFormat: @"%@ is not an IMAP4 folder", [self relativeImap4Name]]];
reason: [NSString stringWithFormat: @"%@ is not an IMAP4 folder", [self relativeImap4Name]]];
}
- (NSException *) expunge
@@ -2001,16 +2036,45 @@ static NSString *defaultUserID = @"anyone";
//
// Check updated items
//
// . UID FETCH 1:* (UID) (CHANGEDSINCE 1)
// * 1 FETCH (UID 542 MODSEQ (7))
// * 2 FETCH (UID 553 MODSEQ (14))
// * 3 FETCH (UID 554 MODSEQ (16))
// * 4 FETCH (UID 555 MODSEQ (15))
// * 5 FETCH (UID 559 MODSEQ (17))
// * 6 FETCH (UID 560 MODSEQ (18))
// * 7 FETCH (UID 561 MODSEQ (19))
//
// . uid fetch 1:* (UID) (changedsince 171)
// SORT + MODSEQ: http://www.watersprings.org/pub/id/draft-melnikov-condstore-sort-00.txt
// With date, not modseq
// . UID SORT (DATE) UTF-8 (NOT DELETED) (SINCE "15-Mar-2014")
// * SORT 553 542 555 554 601 559 560 561 565 602 603 605 611 610 612 613 614 615 616 617 618 621 619 620 622 623
//
// . UID SORT (DATE) UTF-8 ((MODSEQ 64) (NOT DELETED)) (SINCE "15-Mar-2014")
// * SORT 623 624 (MODSEQ 65)
// . OK Completed (2 msgs in 0.000 secs)
//
// ".. the server MUST also append (to the end of the untagged SORT response) the highest mod-sequence for all messages being returned."
//
// To get the modseq of a specific message:
//
// . uid fetch 124569:124569 uid (changedsince 1)
// . UID FETCH 124569:124569 (UID MODSEQ)
// * 4900 FETCH (UID 124569 MODSEQ (2))
//
//
// Deleted: "UID FETCH 1:* (UID) (CHANGEDSINCE 171 VANISHED)"
// To get deleted messages
//
// . UID FETCH 1:* (UID) (CHANGEDSINCE 1 VANISHED)
// * VANISHED (EARLIER) 1:541,543:552,556:558,562:564,566:600,604,606:609
// * 1 FETCH (UID 542 MODSEQ (7))
// * 2 FETCH (UID 553 MODSEQ (14))
// * 3 FETCH (UID 554 MODSEQ (16))
// * 4 FETCH (UID 555 MODSEQ (15))
// * 5 FETCH (UID 559 MODSEQ (17))
// * 6 FETCH (UID 560 MODSEQ (18))
// * 7 FETCH (UID 561 MODSEQ (19))
//
// fetchUIDsOfVanishedItems ..
//
// . uid fetch 1:* (FLAGS) (changedsince 176 vanished)
@@ -2027,6 +2091,7 @@ static NSString *defaultUserID = @"anyone";
NSMutableArray *allTokens;
NSArray *a, *uids;
NSDictionary *d;
id fetchResults;
int uidnext, highestmodseq, i;
@@ -2053,7 +2118,7 @@ static NSString *defaultUserID = @"anyone";
EOKeyValueQualifier *kvQualifier;
NSNumber *nextModseq;
nextModseq = [NSNumber numberWithUnsignedLongLong: highestmodseq + 1];
nextModseq = [NSNumber numberWithUnsignedLongLong: highestmodseq];
kvQualifier = [[EOKeyValueQualifier alloc]
initWithKey: @"modseq"
operatorSelector: EOQualifierOperatorGreaterThanOrEqualTo
@@ -2071,9 +2136,10 @@ static NSString *defaultUserID = @"anyone";
if (theStartDate)
{
EOQualifier *sinceDateQualifier = [EOQualifier qualifierWithQualifierFormat:
@"(DATE >= %@)", theStartDate];
EOQualifier *sinceDateQualifier;
sinceDateQualifier = [EOQualifier qualifierWithQualifierFormat:
@"(DATE >= %@)", theStartDate];
searchQualifier = [[EOAndQualifier alloc] initWithQualifiers: searchQualifier, sinceDateQualifier,
nil];
[searchQualifier autorelease];
@@ -2084,24 +2150,28 @@ static NSString *defaultUserID = @"anyone";
uids = [self fetchUIDsMatchingQualifier: searchQualifier
sortOrdering: nil];
for (i = 0; i < [uids count]; i++)
{
// New messages
if ([[uids objectAtIndex: i] intValue] >= uidnext)
{
d = [NSDictionary dictionaryWithObject: @"added" forKey: [uids objectAtIndex: i]];
}
// Changed messages
else
{
d = [NSDictionary dictionaryWithObject: @"changed" forKey: [uids objectAtIndex: i]];
}
fetchResults = [(NSDictionary *)[self fetchUIDs: uids
parts: [NSArray arrayWithObject: @"modseq"]]
objectForKey: @"fetch"];
/* NOTE: we sort items manually because Cyrus does not properly sort
entries with a MODSEQ of 0 */
fetchResults
= [fetchResults sortedArrayUsingFunction: _compareFetchResultsByMODSEQ
context: NULL];
for (i = 0; i < [fetchResults count]; i++)
{
d = [NSDictionary dictionaryWithObject: [[fetchResults objectAtIndex: i] objectForKey: @"modseq"]
forKey: [[[fetchResults objectAtIndex: i] objectForKey: @"uid"] stringValue]];
[allTokens addObject: d];
}
// We fetch deleted ones
if (highestmodseq == 0)
highestmodseq = 1;
if (highestmodseq > 0)
{
id uid;
@@ -2110,8 +2180,8 @@ static NSString *defaultUserID = @"anyone";
for (i = 0; i < [uids count]; i++)
{
uid = [uids objectAtIndex: i];
d = [NSDictionary dictionaryWithObject: @"deleted" forKey: uid];
uid = [[uids objectAtIndex: i] stringValue];
d = [NSDictionary dictionaryWithObject: [NSNull null] forKey: uid];
[allTokens addObject: d];
}
}
+1 -1
View File
@@ -25,7 +25,7 @@
#import <Foundation/NSArray.h>
#import <Foundation/NSString.h>
#import "../../UI/SOGoUI/UIxComponent.h";
#import "../../UI/SOGoUI/UIxComponent.h"
@interface SOGoMailLabel : NSObject
{
+3
View File
@@ -770,6 +770,9 @@ static BOOL debugSoParts = NO;
[mimeType hasPrefix: @"image/"] ||
[mimeType hasPrefix: @"video/"])
filename = [NSString stringWithFormat: @"unknown_%@", path];
else if ([mimeType isEqualToString: @"message/rfc822"])
filename = [NSString stringWithFormat: @"email_%@.eml", path];
if (filename)
{
@@ -1,2 +1,2 @@
"SieveFolderName" = "Filtre";
"OtherUsersFolderName" = "Ostatní užívatelia";
"SharedFoldersName" = "Zdielané adresáre";
@@ -1,2 +1,2 @@
"SieveFolderName" = "Filtros";
"OtherUsersFolderName" = "Otros usuarios";
"SharedFoldersName" = "Carpetas compartidas";
File renamed without changes.
File renamed without changes.
@@ -1,8 +1,6 @@
/* EOBitmaskQualifier.h - this file is part of SOGo
*
* Copyright (C) 2010-2012 Inverse inc
*
* Author: Wolfgang Sourdeau <wsourdeau@inverse.ca>
* Copyright (C) 2010-2014 Inverse inc
*
* This file is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -1,8 +1,6 @@
/* EOBitmaskQualifier.m - this file is part of SOGo
*
* Copyright (C) 2010-2012 Inverse inc
*
* Author: Wolfgang Sourdeau <wsourdeau@inverse.ca>
* Copyright (C) 2010-2014 Inverse inc
*
* This file is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -1,8 +1,6 @@
/* EOQualifier+MAPI.h - this file is part of SOGo
/* EOQualifier+SOGoCacheObject.h - this file is part of SOGo
*
* Copyright (C) 2010-2012 Inverse inc.
*
* Author: Wolfgang Sourdeau <wsourdeau@inverse.ca>
* Copyright (C) 2010-2014 Inverse inc.
*
* This file is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -20,17 +18,17 @@
* Boston, MA 02111-1307, USA.
*/
#ifndef EOQUALIFIER_MAPI_H
#define EOQUALIFIER_MAPI_H
#ifndef EOQUALIFIER_SOGOCACHEOBJECT_H
#define EOQUALIFIER_SOGOCACHEOBJECT_H
#import <EOControl/EOQualifier.h>
@class SOGoMAPIDBObject;
@class SOGoCacheGCSObject;
@interface EOQualifier (MAPIStoreRestrictions)
@interface EOQualifier (SOGoCacheObjectRestrictions)
- (BOOL) evaluateSOGoMAPIDBObject: (SOGoMAPIDBObject *) object;
- (BOOL) evaluateSOGoMAPIDBObject: (SOGoCacheGCSObject *) object;
@end
#endif /* EOQUALIFIER_MAPI_H */
#endif /* EOQUALIFIER_CACHEOBJECT_H */
@@ -1,8 +1,6 @@
/* EOQualifier+MAPI.m - this file is part of SOGo
/* EOQualifier+SOGoCacheObject.m - this file is part of SOGo
*
* Copyright (C) 2010-2012 Inverse inc
*
* Author: Wolfgang Sourdeau <wsourdeau@inverse.ca>
* Copyright (C) 2010-2014 Inverse inc
*
* This file is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -29,11 +27,11 @@
#import <NGExtensions/NSObject+Logs.h>
#import "EOBitmaskQualifier.h"
#import "SOGoMAPIDBObject.h"
#import "SOGoCacheGCSObject.h"
#import "EOQualifier+MAPI.h"
#import "EOQualifier+SOGoCacheObject.h"
@implementation EOQualifier (MAPIStoreRestrictions)
@implementation EOQualifier (SOGoCacheObjectRestrictions)
- (BOOL) _evaluateSOGoMAPIDBObject: (NSDictionary *) properties
{
@@ -41,7 +39,7 @@
return NO;
}
- (BOOL) evaluateSOGoMAPIDBObject: (SOGoMAPIDBObject *) object
- (BOOL) evaluateSOGoMAPIDBObject: (SOGoCacheGCSObject *) object
{
NSDictionary *properties;
BOOL rc;
@@ -58,7 +56,7 @@
@end
@implementation EOAndQualifier (MAPIStoreRestrictionsPrivate)
@implementation EOAndQualifier (SOGoCacheRestrictionsPrivate)
- (BOOL) _evaluateSOGoMAPIDBObject: (NSDictionary *) properties
{
@@ -76,7 +74,7 @@
@end
@implementation EOOrQualifier (MAPIStoreRestrictionsPrivate)
@implementation EOOrQualifier (SOGoCacheObjectRestrictionsPrivate)
- (BOOL) _evaluateSOGoMAPIDBObject: (NSDictionary *) properties
{
@@ -94,7 +92,7 @@
@end
@implementation EONotQualifier (MAPIStoreRestrictionsPrivate)
@implementation EONotQualifier (SOGoCacheObjectRestrictionsPrivate)
- (BOOL) _evaluateSOGoMAPIDBObject: (NSDictionary *) properties
{
@@ -103,7 +101,7 @@
@end
@implementation EOKeyValueQualifier (MAPIStoreRestrictionsPrivate)
@implementation EOKeyValueQualifier (SOGoCacheObjectRestrictionsPrivate)
typedef BOOL (*EOComparator) (id, SEL, id);
@@ -134,7 +132,7 @@ typedef BOOL (*EOComparator) (id, SEL, id);
@end
@implementation EOBitmaskQualifier (MAPIStoreRestrictionsPrivate)
@implementation EOBitmaskQualifier (SOGoCacheObjectRestrictionsPrivate)
- (BOOL) _evaluateSOGoMAPIDBObject: (NSDictionary *) properties
{
@@ -1,8 +1,6 @@
/* GCSSpecialQueries+OpenChange.h - this file is part of SOGo
/* GCSSpecialQueries+SOGoCacheObject.h - this file is part of SOGo
*
* Copyright (C) 2012 Inverse inc
*
* Author: Wolfgang Sourdeau <wsourdeau@inverse.ca>
* Copyright (C) 2012-2014 Inverse inc
*
* This file is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -20,15 +18,15 @@
* Boston, MA 02111-1307, USA.
*/
#ifndef GCSSPECIALQUERIES_OPENCHANGE_H
#define GCSSPECIALQUERIES_OPENCHANGE_H
#ifndef GCSSPECIALQUERIES_SOGOCACHEOBJECT_H
#define GCSSPECIALQUERIES_SOGOCACHEOBJECT_H
#import <GDLContentStore/GCSSpecialQueries.h>
@interface GCSSpecialQueries (OpenChangeHelpers)
@interface GCSSpecialQueries (SOGoCacheObject)
- (NSString *) createOpenChangeFSTableWithName: (NSString *) tableName;
- (NSString *) createSOGoCacheGCSFolderTableWithName: (NSString *) tableName;
@end
#endif /* GCSSPECIALQUERIES_OPENCHANGE_H */
#endif /* GCSSPECIALQUERIES_SOGOCACHEOBJECT_H */
@@ -1,8 +1,6 @@
/* GCSSpecialQueries+OpenChange.m - this file is part of SOGo
/* GCSSpecialQueries+SOGoCacheObject.m - this file is part of SOGo
*
* Copyright (C) 2012 Inverse inc
*
* Author: Wolfgang Sourdeau <wsourdeau@inverse.ca>
* Copyright (C) 2012-2014 Inverse inc
*
* This file is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -22,22 +20,22 @@
#import <Foundation/NSString.h>
#import "GCSSpecialQueries+OpenChange.h"
#import "GCSSpecialQueries+SOGoCacheObject.h"
@interface GCSPostgreSQLSpecialQueries (OpenChangeHelpers)
@interface GCSPostgreSQLSpecialQueries (SOGoObjectCache)
@end
@interface GCSMySQLSpecialQueries (OpenChangeHelpers)
@interface GCSMySQLSpecialQueries (SOGoObjectCache)
@end
@interface GCSOracleSpecialQueries (OpenChangeHelpers)
@interface GCSOracleSpecialQueries (SOGoObjectCache)
@end
@implementation GCSSpecialQueries (OpenChangeHelpers)
@implementation GCSSpecialQueries (SOGoObjectCache)
/* FIXME: c_parent_path should be indexed */
- (NSString *) createOpenChangeFSTableWithName: (NSString *) tableName
- (NSString *) createSOGoCacheGCSFolderTableWithName: (NSString *) tableName
{
[self subclassResponsibility: _cmd];
@@ -46,9 +44,9 @@
@end
@implementation GCSPostgreSQLSpecialQueries (OpenChangeHelpers)
@implementation GCSPostgreSQLSpecialQueries (SOGoObjectCache)
- (NSString *) createOpenChangeFSTableWithName: (NSString *) tableName
- (NSString *) createSOGoCacheGCSFolderTableWithName: (NSString *) tableName
{
static NSString *sqlFolderFormat
= (@"CREATE TABLE %@ ("
@@ -66,9 +64,9 @@
@end
@implementation GCSMySQLSpecialQueries (OpenChangeHelpers)
@implementation GCSMySQLSpecialQueries (SOGoObjectCache)
- (NSString *) createOpenChangeFSTableWithName: (NSString *) tableName
- (NSString *) createSOGoCacheGCSFolderTableWithName: (NSString *) tableName
{
static NSString *sqlFolderFormat
= (@"CREATE TABLE %@ ("
@@ -86,9 +84,9 @@
@end
@implementation GCSOracleSpecialQueries (OpenChangeHelpers)
@implementation GCSOracleSpecialQueries (SOGoObjectCache)
- (NSString *) createOpenChangeFSTableWithName: (NSString *) tableName
- (NSString *) createSOGoCacheGCSFolderTableWithName: (NSString *) tableName
{
static NSString *sqlFolderFormat
= (@"CREATE TABLE %@ ("
+17
View File
@@ -15,7 +15,14 @@ SOGo_HEADER_FILES = \
SOGoBuild.h \
SOGoProductLoader.h \
\
BSONCodec.h \
EOBitmaskQualifier.h \
EOQualifier+SOGoCacheObject.h \
GCSSpecialQueries+SOGoCacheObject.h \
SOGoCache.h \
SOGoCacheGCSFolder.h \
SOGoCacheGCSObject.h \
SOGoCacheObject.h \
SOGoConstants.h \
SOGoObject.h \
SOGoContentObject.h \
@@ -80,7 +87,14 @@ SOGo_OBJC_FILES = \
SOGoBuild.m \
SOGoProductLoader.m \
\
BSONCodec.m \
EOBitmaskQualifier.m \
EOQualifier+SOGoCacheObject.m \
GCSSpecialQueries+SOGoCacheObject.m \
SOGoCache.m \
SOGoCacheGCSFolder.m \
SOGoCacheGCSObject.m \
SOGoCacheObject.m \
SOGoConstants.m \
SOGoObject.m \
SOGoContentObject.m \
@@ -158,6 +172,9 @@ ifeq ($(saml2_config), yes)
SOGoSAML2Exceptions.h SOGoSAML2Exceptions.m: gen-saml2-exceptions.py
$(ECHO_CREATING) ./gen-saml2-exceptions.py $(LASSO_CFLAGS) $(END_ECHO)
distclean clean::
-rm -f SOGoSAML2Exceptions.h SOGoSAML2Exceptions.m
endif
ifeq ($(ldap_config),yes)
+3 -3
View File
@@ -46,7 +46,7 @@ static NSString **cssEscapingStrings = NULL;
static unichar *cssEscapingCharacters = NULL;
static int cssEscapingCount;
static unichar thisCharCode[29];
static unichar thisCharCode[30];
static NSString *controlCharString = nil;
static NSCharacterSet *controlCharSet = nil;
@@ -285,8 +285,8 @@ static NSCharacterSet *controlCharSet = nil;
int i, j;
// Create an array of chars for all control characters between 0x00 and 0x1F,
// apart from \t, \n, \f and \r (0x08, 0x09, 0x0A, 0x0C and 0x0D)
for (i = 0, j = 0x00; j < 0x08; i++, j++) {
// apart from \t, \n, \f and \r (0x09, 0x0A, 0x0C and 0x0D)
for (i = 0, j = 0x00; j <= 0x08; i++, j++) {
thisCharCode[i] = j;
}
thisCharCode[i++] = 0x0B;
@@ -1,8 +1,6 @@
/* SOGoMAPIDBFolder.h - this file is part of SOGo
/* SOGoCacheGCSFolder.h - this file is part of SOGo
*
* Copyright (C) 2012 Inverse inc.
*
* Author: Wolfgang Sourdeau <wsourdeau@inverse.ca>
* Copyright (C) 2012-2014 Inverse inc.
*
* This file is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -20,10 +18,10 @@
* Boston, MA 02111-1307, USA.
*/
#ifndef SOGOMAPIDBFOLDER_H
#define SOGOMAPIDBFOLDER_H
#ifndef SOGOCACHEGCSFOLDER_H
#define SOGOCACHEGCSFOLDER_H
#import "SOGoMAPIDBObject.h"
#import "SOGoCacheGCSObject.h"
@class NSArray;
@class NSMutableString;
@@ -32,12 +30,10 @@
@class EOQualifier;
@class SOGoMAPIDBMessage;
@interface SOGoMAPIDBFolder : SOGoMAPIDBObject
@interface SOGoCacheGCSFolder : SOGoCacheGCSObject
{
NSString *pathPrefix; /* for root folders */
SOGoMAPIDBObject *aclMessage;
SOGoCacheGCSObject *aclMessage;
}
- (void) setPathPrefix: (NSString *) newPathPrefix;
@@ -47,7 +43,7 @@
- (NSArray *) toOneRelationshipKeys;
- (NSArray *) toManyRelationshipKeys;
- (NSArray *) childKeysOfType: (MAPIDBObjectType) type
- (NSArray *) childKeysOfType: (SOGoCacheObjectType) type
includeDeleted: (BOOL) includeDeleted
matchingQualifier: (EOQualifier *) qualifier
andSortOrderings: (NSArray *) sortOrderings;
@@ -56,4 +52,4 @@
@end
#endif /* SOGOMAPIDBFOLDER_H */
#endif /* SOGOCACHEGCSFOLDER_H */
@@ -1,8 +1,6 @@
/* SOGoMAPIDBFolder.m - this file is part of SOGo
/* SOGoCacheGCSFolder.m - this file is part of SOGo
*
* Copyright (C) 2012 Inverse inc.
*
* Author: Wolfgang Sourdeau <wsourdeau@inverse.ca>
* Copyright (C) 2012-2014 Inverse inc.
*
* This file is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -33,34 +31,32 @@
#import <NGExtensions/NSNull+misc.h>
#import <GDLAccess/EOAdaptorChannel.h>
#import <GDLContentStore/GCSChannelManager.h>
// #import <GDLContentStore/EOQualifier+GCS.m>
#import <SOGo/NSArray+Utilities.h>
#import <SOGo/NSString+Utilities.h>
#import <SOGo/SOGoDomainDefaults.h>
#import <SOGo/SOGoUser.h>
#import "EOQualifier+MAPI.h"
#import "GCSSpecialQueries+OpenChange.h"
#import "SOGoMAPIDBMessage.h"
#import "EOQualifier+SOGoCacheObject.h"
#import "GCSSpecialQueries+SOGoCacheObject.h"
#import "SOGoMAPIDBFolder.h"
#import "SOGoCacheGCSFolder.h"
#undef DEBUG
#include <stdbool.h>
#include <talloc.h>
#include <util/time.h>
#include <mapistore/mapistore.h>
#include <mapistore/mapistore_errors.h>
#include <libmapiproxy.h>
#include <param.h>
//#include <stdbool.h>
//#include <talloc.h>
//#include <util/time.h>
//#include <mapistore/mapistore.h>
//#include <mapistore/mapistore_errors.h>
//#include <libmapiproxy.h>
//#include <param.h>
Class SOGoMAPIDBObjectK = Nil;
Class SOGoCacheGCSObjectK = Nil;
@implementation SOGoMAPIDBFolder
@implementation SOGoCacheGCSFolder
+ (void) initialize
{
SOGoMAPIDBObjectK = [SOGoMAPIDBObject class];
SOGoCacheGCSObjectK = [SOGoCacheGCSObject class];
}
- (id) init
@@ -77,10 +73,10 @@ Class SOGoMAPIDBObjectK = Nil;
{
if ((self = [super initWithName: name inContainer: newContainer]))
{
objectType = MAPIDBObjectTypeFolder;
aclMessage = [SOGoMAPIDBObject objectWithName: @"permissions"
objectType = MAPIFolderCacheObject;
aclMessage = [SOGoCacheGCSObject objectWithName: @"permissions"
inContainer: self];
[aclMessage setObjectType: MAPIDBObjectTypeInternal];
[aclMessage setObjectType: MAPIInternalCacheObject];
[aclMessage retain];
}
@@ -135,7 +131,7 @@ Class SOGoMAPIDBObjectK = Nil;
// return [SOGoMAPIDBMessage objectWithName: filename inContainer: self];
// }
- (NSArray *) childKeysOfType: (MAPIDBObjectType) type
- (NSArray *) childKeysOfType: (SOGoCacheObjectType) type
includeDeleted: (BOOL) includeDeleted
matchingQualifier: (EOQualifier *) qualifier
andSortOrderings: (NSArray *) sortOrderings
@@ -148,7 +144,7 @@ Class SOGoMAPIDBObjectK = Nil;
NSArray *records;
NSDictionary *record;
NSUInteger childPathPrefixLen, count, max;
SOGoMAPIDBObject *currentChild;
SOGoCacheGCSObject *currentChild;
/* query construction */
sql = [NSMutableString stringWithCapacity: 256];
@@ -182,7 +178,7 @@ Class SOGoMAPIDBObjectK = Nil;
{
if (qualifier)
{
currentChild = [SOGoMAPIDBObject objectWithName: childKey
currentChild = [SOGoCacheGCSObject objectWithName: childKey
inContainer: self];
[currentChild setupFromRecord: record];
if ([qualifier evaluateSOGoMAPIDBObject: currentChild])
@@ -201,7 +197,7 @@ Class SOGoMAPIDBObjectK = Nil;
- (NSArray *) toManyRelationshipKeys
{
return [self childKeysOfType: MAPIDBObjectTypeFolder
return [self childKeysOfType: MAPIFolderCacheObject
includeDeleted: NO
matchingQualifier: nil
andSortOrderings: nil];
@@ -209,7 +205,7 @@ Class SOGoMAPIDBObjectK = Nil;
- (NSArray *) toOneRelationshipKeys
{
return [self childKeysOfType: MAPIDBObjectTypeMessage
return [self childKeysOfType: MAPIMessageCacheObject
includeDeleted: NO
matchingQualifier: nil
andSortOrderings: nil];
@@ -359,10 +355,10 @@ Class SOGoMAPIDBObjectK = Nil;
record = [self lookupRecord: childPath newerThanVersion: -1];
if (record)
{
if ([[record objectForKey: @"c_type"] intValue] == MAPIDBObjectTypeFolder)
if ([[record objectForKey: @"c_type"] intValue] == MAPIFolderCacheObject)
objectClass = isa;
else
objectClass = SOGoMAPIDBObjectK;
objectClass = SOGoCacheGCSObjectK;
object = [objectClass objectWithName: childName
inContainer: self];
@@ -379,7 +375,7 @@ Class SOGoMAPIDBObjectK = Nil;
{
id object;
object = [SOGoMAPIDBFolder objectWithName: folderName
object = [SOGoCacheGCSFolder objectWithName: folderName
inContainer: self];
[object reloadIfNeeded];
@@ -1,8 +1,6 @@
/* SOGoMAPIDBObject.h - this file is part of SOGo
/* SOGoCacheGCSObject.h - this file is part of SOGo
*
* Copyright (C) 2012 Inverse inc
*
* Author: Wolfgang Sourdeau <wsourdeau@inverse.ca>
* Copyright (C) 2012-2014 Inverse inc
*
* This file is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -20,10 +18,10 @@
* Boston, MA 02111-1307, USA.
*/
#ifndef SOGOMAPIDBOBJECT_H
#define SOGOMAPIDBOBJECT_H
#ifndef SOGOCACHEGCSOBJECT_H
#define SOGOCACHEGCSOBJECT_H
#import "SOGoMAPIObject.h"
#import "SOGoCacheObject.h"
@class NSArray;
@class NSMutableDictionary;
@@ -34,18 +32,20 @@
@class EOAdaptor;
typedef enum {
MAPIDBObjectTypeFolder = 1,
MAPIDBObjectTypeMessage = 2,
MAPIDBObjectTypeFAI = 3,
MAPIDBObjectTypeInternal = 99 /* object = property list */
} MAPIDBObjectType;
MAPIFolderCacheObject = 1,
MAPIMessageCacheObject = 2,
MAPIFAICacheObject = 3,
MAPIInternalCacheObject = 99, /* object = property list */
ActiveSyncGlobalCacheObject = 200,
ActiveSyncFolderCacheObject = 201
} SOGoCacheObjectType;
@interface SOGoMAPIDBObject : SOGoMAPIObject
@interface SOGoCacheGCSObject : SOGoCacheObject
{
NSURL *tableUrl;
BOOL initialized; /* safe guard */
MAPIDBObjectType objectType;
SOGoCacheObjectType objectType;
NSInteger version;
BOOL deleted;
}
@@ -67,8 +67,11 @@ typedef enum {
- (NSDictionary *) lookupRecord: (NSString *) path
newerThanVersion: (NSInteger) startVersion;
- (void) setObjectType: (MAPIDBObjectType) newObjectType;
- (MAPIDBObjectType) objectType; /* message, fai, folder */
- (NSArray *) folderList: (NSString *) deviceId
newerThanVersion: (NSInteger) startVersion;
- (void) setObjectType: (SOGoCacheObjectType) newObjectType;
- (SOGoCacheObjectType) objectType; /* message, fai, folder */
/* automatically set from actions */
- (BOOL) deleted;
@@ -82,4 +85,4 @@ typedef enum {
@end
#endif /* SOGOMAPIDBOBJECT_H */
#endif /* SOGOCACHEGCSOBJECT_H */
@@ -1,8 +1,6 @@
/* SOGoMAPIDBObject.m - this file is part of SOGo
/* SOGoCacheGCSObject.m - this file is part of SOGo
*
* Copyright (C) 2012 Inverse inc
*
* Author: Wolfgang Sourdeau <wsourdeau@inverse.ca>
* Copyright (C) 2012-2014 Inverse inc
*
* This file is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -43,38 +41,20 @@
#import <SOGo/SOGoDomainDefaults.h>
#import <SOGo/SOGoUser.h>
#import "GCSSpecialQueries+OpenChange.h"
#import "MAPIStoreTypes.h"
#import "SOGoMAPIDBFolder.h"
#import "GCSSpecialQueries+SOGoCacheObject.h"
#import "SOGoCacheGCSFolder.h"
#import "BSONCodec.h"
#import "SOGoMAPIDBObject.h"
//
// This defines the storage for internal properly list, stored in the
// database. Possible values are:
//
// NSPropertyListGNUstepFormat = 1000
// NSPropertyListGNUstepBinaryFormat = 1001
// NSPropertyListOpenStepFormat = 1
// NSPropertyListXMLFormat_v1_0 = 100
// NSPropertyListBinaryFormat_v1_0 = 200
//
static NSPropertyListFormat plistFormat;
#import "SOGoCacheGCSObject.h"
static EOAttribute *textColumn = nil;
@implementation SOGoMAPIDBObject
@implementation SOGoCacheGCSObject
+ (void) initialize
{
NSDictionary *description;
plistFormat = [[NSUserDefaults standardUserDefaults] integerForKey: @"SOGoPropertyListFormat"];
if (!plistFormat)
plistFormat = NSPropertyListGNUstepBinaryFormat;
if (!textColumn)
{
/* TODO: this is a hack for providing an EOAttribute definition that is
@@ -92,19 +72,14 @@ static EOAttribute *textColumn = nil;
/*
= (@"CREATE TABLE %@ ("
@" c_path VARCHAR(255) PRIMARY KEY,"
@" c_type VARCHAR(20) NOT NULL,"
@" c_parent_path VARCHAR(255),"
@" c_type SMALLINT NOT NULL,"
@" c_creationdate INT4 NOT NULL,"
@" c_lastmodified INT4 NOT NULL,"
@" c_version INT4 NOT NULL DEFAULT 0,"
@" c_deleted SMALLINT NOT NULL DEFAULT 0,"
@" c_content TEXT)");
*/
/* indexes:
c_path (primary key)
c_counter
c_path, c_type
c_path, c_creationdate */
- (id) init
{
if ((self = [super init]))
@@ -137,7 +112,7 @@ static EOAttribute *textColumn = nil;
tableUrl = [container tableUrl];
[tableUrl retain];
if (!tableUrl)
[NSException raise: @"MAPIStoreIOException"
[NSException raise: @"SOGoCacheIOException"
format: @"table url is not set for object '%@'", self];
}
@@ -157,9 +132,8 @@ static EOAttribute *textColumn = nil;
- (void) setupFromRecord: (NSDictionary *) record
{
NSInteger intValue;
NSString *propsValue;//, *error;
NSString *propsValue;
NSDictionary *newValues;
//NSPropertyListFormat format;
objectType = [[record objectForKey: @"c_type"] intValue];
intValue = [[record objectForKey: @"c_creationdate"] intValue];
@@ -195,7 +169,7 @@ static EOAttribute *textColumn = nil;
path = [NSMutableString stringWithFormat: @"/%@", nameInContainer];
if ([path rangeOfString: @"//"].location != NSNotFound)
[NSException raise: @"MAPIStoreIOException"
[NSException raise: @"SOGoCacheIOException"
format: @"object path has not been properly set for"
" folder '%@' (%@)",
self, path];
@@ -203,12 +177,12 @@ static EOAttribute *textColumn = nil;
return path;
}
- (void) setObjectType: (MAPIDBObjectType) newObjectType
- (void) setObjectType: (SOGoCacheObjectType) newObjectType
{
objectType = newObjectType;
}
- (MAPIDBObjectType) objectType /* message, fai, folder */
- (SOGoCacheObjectType) objectType /* message, fai, folder */
{
return objectType;
}
@@ -216,7 +190,7 @@ static EOAttribute *textColumn = nil;
- (NSCalendarDate *) creationDate
{
if (!initialized)
[NSException raise: @"MAPIStoreIOException"
[NSException raise: @"SOGoCacheIOException"
format: @"record has not been initialized: %@", self];
return creationDate;
@@ -225,7 +199,7 @@ static EOAttribute *textColumn = nil;
- (NSCalendarDate *) lastModified
{
if (!initialized)
[NSException raise: @"MAPIStoreIOException"
[NSException raise: @"SOGoCacheIOException"
format: @"record has not been initialized: %@", self];
return lastModified;
@@ -236,43 +210,6 @@ static EOAttribute *textColumn = nil;
return deleted;
}
- (Class) mapistoreMessageClass
{
NSString *className, *mapiMsgClass;
switch (objectType)
{
case MAPIDBObjectTypeMessage:
mapiMsgClass = [properties
objectForKey: MAPIPropertyKey (PidTagMessageClass)];
if (mapiMsgClass)
{
if ([mapiMsgClass isEqualToString: @"IPM.StickyNote"])
className = @"MAPIStoreNotesMessage";
else
className = @"MAPIStoreDBMessage";
//[self logWithFormat: @"PidTagMessageClass = '%@', returning '%@'",
// mapiMsgClass, className];
}
else
{
//[self warnWithFormat: @"PidTagMessageClass is not set, falling back"
// @" to 'MAPIStoreDBMessage'"];
className = @"MAPIStoreDBMessage";
}
break;
case MAPIDBObjectTypeFAI:
className = @"MAPIStoreFAIMessage";
break;
default:
[NSException raise: @"MAPIStoreIOException"
format: @"message class should not be queried for objects"
@" of type '%d'", objectType];
}
return NSClassFromString (className);
}
/* actions */
- (void) setNameInContainer: (NSString *) newNameInContainer
{
@@ -413,7 +350,7 @@ static EOAttribute *textColumn = nil;
EOAdaptor *adaptor;
if ([path hasSuffix: @"/"])
[NSException raise: @"MAPIStoreIOException"
[NSException raise: @"SOGoCacheIOException"
format: @"path ends with a slash: %@", path];
tableName = [self tableName];
@@ -438,6 +375,47 @@ static EOAttribute *textColumn = nil;
return record;
}
// get a list of all folders
- (NSArray *) folderList: (NSString *) deviceId
newerThanVersion: (NSInteger) startVersion
{
NSMutableArray *recordsOut;
NSArray *records;
NSString *tableName, *pathValue;
NSMutableString *sql;
EOAdaptor *adaptor;
NSUInteger count, max;
if ([deviceId hasSuffix: @"/"])
[NSException raise: @"SOGoCacheIOException"
format: @"path ends with a slash: %@", deviceId];
tableName = [self tableName];
adaptor = [self tableChannelAdaptor];
pathValue = [adaptor formatValue: [NSString stringWithFormat: @"/%@+folder%", deviceId]
forAttribute: textColumn];
/* query */
sql = [NSMutableString stringWithFormat:
@"SELECT * FROM %@ WHERE c_path LIKE %@ AND c_deleted <> 1",
tableName, pathValue];
if (startVersion > -1)
[sql appendFormat: @" AND c_version > %d", startVersion];
/* execution */
records = [self performSQLQuery: sql];
max = [records count];
recordsOut = [[NSMutableArray alloc] init];
for (count = 0; count < max; count++)
{
[recordsOut addObject: [[records objectAtIndex: count] objectForKey: @"c_path"]];
}
return recordsOut;
}
- (void) reloadIfNeeded
{
/* if object is uninitialized: reload without condition, otherwise, load if
@@ -491,7 +469,7 @@ static EOAttribute *textColumn = nil;
NSException *result;
if (!initialized)
[NSException raise: @"MAPIStoreIOException"
[NSException raise: @"SOGoCacheIOException"
format: @"record has not been initialized: %@", self];
cm = [GCSChannelManager defaultChannelManager];
@@ -503,12 +481,6 @@ static EOAttribute *textColumn = nil;
now = [NSCalendarDate date];
ASSIGN (lastModified, now);
/*
- (NSException *)insertRowX:(NSDictionary *)_row forEntity:(EOEntity *)_entity;
- (NSException *)updateRowX:(NSDictionary*)aRow
describedByQualifier:(EOSQLQualifier*)aQualifier;
*/
adaptor = [[channel adaptorContext] adaptor];
pathValue = [adaptor formatValue: [self path]
forAttribute: textColumn];
@@ -516,7 +488,7 @@ static EOAttribute *textColumn = nil;
lastModifiedValue = (NSInteger) [lastModified timeIntervalSince1970];
if (objectType == -1)
[NSException raise: @"MAPIStoreIOException"
[NSException raise: @"SOGoCacheIOException"
format: @"object type has not been set for object '%@'",
self];
@@ -567,12 +539,6 @@ static EOAttribute *textColumn = nil;
if (result)
[self errorWithFormat: @"could not insert/update record for record %@"
@" in %@: %@", pathValue, tableName, result];
// @" c_path VARCHAR(255) PRIMARY KEY,"
// @" c_type SMALLINT NOT NULL,"
// @" c_creationdate INT4 NOT NULL,"
// @" c_lastmodified INT4 NOT NULL,"
// @" c_deleted SMALLINT NOT NULL DEFAULT 0,"
// @" c_content BLOB");
[cm releaseChannel: channel];
}
@@ -1,8 +1,6 @@
/* SOGoMAPIObject.h - this file is part of SOGo
/* SOGoCacheObject.h - this file is part of SOGo
*
* Copyright (C) 2012 Inverse inc
*
* Author: Wolfgang Sourdeau <wsourdeau@inverse.ca>
* Copyright (C) 2012-2014 Inverse inc
*
* This file is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -20,14 +18,14 @@
* Boston, MA 02111-1307, USA.
*/
#ifndef SOGOMAPIOBJECT_H
#define SOGOMAPIOBJECT_H
#ifndef SOGOCACHEOBJECT_H
#define SOGOCACHEOBJECT_H
#import <SOGo/SOGoObject.h>
@class NSMutableDictionary;
@interface SOGoMAPIObject : SOGoObject
@interface SOGoCacheObject : SOGoObject
{
BOOL isNew;
NSMutableDictionary *properties;
@@ -46,4 +44,4 @@
@end
#endif /* SOGOMAPIOBJECT_H */
#endif /* SOGOCACHEOBJECT_H */
@@ -1,8 +1,6 @@
/* SOGoMAPIObject.m - this file is part of SOGo
/* SOGoCacgeObject.m - this file is part of SOGo
*
* Copyright (C) 2012 Inverse inc
*
* Author: Wolfgang Sourdeau <wsourdeau@inverse.ca>
* Copyright (C) 2012-2014 Inverse inc
*
* This file is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -20,12 +18,12 @@
* Boston, MA 02111-1307, USA.
*/
#import <Foundation/NSDictionary.h>
#import <Foundation/NSCalendarDate.h>
#import <Foundation/NSDictionary.h>
#import "SOGoMAPIObject.h"
#import "SOGoCacheObject.h"
@implementation SOGoMAPIObject
@implementation SOGoCacheObject
- (id) init
{
+6
View File
@@ -51,4 +51,10 @@ typedef enum
EventUpdated = 2,
} SOGoEventOperation;
typedef enum
{
SOGoPersonalFolder = 0,
SOGoCollectedFolder = 1,
} SOGoFolderType;
#endif /* _SOGOCONSTANTS_H_ */
+4
View File
@@ -14,6 +14,7 @@
SOGoZipPath = "/usr/bin/zip";
SOGoEncryptionKey = "MySOGoEncryptionKey";
SOGoSieveFolderEncoding = "UTF-7";
WOUseRelativeURLs = YES;
WOMessageUseUTF8 = YES;
@@ -53,6 +54,7 @@
SOGoIMAPServer = "localhost";
SOGoMailDomain = "localhost";
SOGoSelectedAddressBook = "collected";
SOGoMailMessageCheck = "manually";
SOGoMailMessageForwarding = "inline";
SOGoMailReplyPlacement = "below";
@@ -83,4 +85,6 @@
$label4 = ("To Do", "#3333FF");
$label5 = ("Later", "#993399");
};
SOGoSubscriptionFolderFormat = "%{FolderName} (%{UserName} <%{Email}>)";
}
+1
View File
@@ -67,6 +67,7 @@
- (NSArray *) calendarDefaultRoles;
- (NSArray *) contactsDefaultRoles;
- (NSArray *) mailPollingIntervals;
- (NSString *) subscriptionFolderFormat;
- (NSString *) calendarDefaultCategoryColor;
+6 -1
View File
@@ -1,6 +1,6 @@
/* SOGoDomainDefaults.m - this file is part of SOGo
*
* Copyright (C) 2009-2012 Inverse inc.
* Copyright (C) 2009-2014 Inverse inc.
*
* Author: Wolfgang Sourdeau <wsourdeau@inverse.ca>
*
@@ -176,6 +176,11 @@
return [self stringArrayForKey: @"SOGoContactsDefaultRoles"];
}
- (NSString *) subscriptionFolderFormat
{
return [self stringForKey: @"SOGoSubscriptionFolderFormat"];
}
//
// In v2.0.4, SOGoForceIMAPLoginWithEmail was renamed to SOGoForceExternalLoginWithEmail
// but we keep backward compatbility for now with previous versions.
+1
View File
@@ -53,6 +53,7 @@
NSString *ocsPath;
GCSFolder *ocsFolder;
NSMutableDictionary *childRecords;
NSMutableDictionary *folderSubscriptionValues;
BOOL userCanAccessAllObjects; /* i.e. user obtains 'Access Object' on
subobjects */
}
+36 -23
View File
@@ -1,9 +1,7 @@
/* SOGoGCSFolder.m - this file is part of SOGo
*
* Copyright (C) 2004-2005 SKYRIX Software AG
* Copyright (C) 2006-2012 Inverse inc.
*
* Author: Wolfgang Sourdeau <wsourdeau@inverse.ca>
* Copyright (C) 2006-2014 Inverse inc.
*
* This file is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -198,6 +196,7 @@ static NSArray *childRecordFields = nil;
ocsPath = nil;
ocsFolder = nil;
childRecords = [NSMutableDictionary new];
folderSubscriptionValues = nil;
userCanAccessAllObjects = NO;
}
@@ -209,6 +208,7 @@ static NSArray *childRecordFields = nil;
[ocsFolder release];
[ocsPath release];
[childRecords release];
[folderSubscriptionValues release];
[super dealloc];
}
@@ -277,29 +277,38 @@ static NSArray *childRecordFields = nil;
{
NSString *primaryDN;
NSDictionary *ownerIdentity;
NSString *subjectFormat;
SOGoDomainDefaults *dd;
primaryDN = [row objectForKey: @"c_foldername"];
if ([primaryDN length])
{
displayName = [NSMutableString new];
if ([primaryDN isEqualToString: [container defaultFolderName]])
[displayName appendString: [self labelForKey: primaryDN
inContext: context]];
else
[displayName appendString: primaryDN];
if (!activeUserIsOwner)
{
displayName = [NSMutableString new];
if ([primaryDN isEqualToString: [container defaultFolderName]])
[displayName appendString: [self labelForKey: primaryDN
inContext: context]];
else
[displayName appendString: primaryDN];
// We MUST NOT use SOGoUser instances here (by calling -primaryIdentity)
// as it'll load user defaults and user settings which is _very costly_
// since it involves JSON parsing and database requests
ownerIdentity = [[SOGoUserManager sharedUserManager]
contactInfosForUserWithUIDorEmail: owner];
if (!activeUserIsOwner)
{
// We MUST NOT use SOGoUser instances here (by calling -primaryIdentity)
// as it'll load user defaults and user settings which is _very costly_
// since it involves JSON parsing and database requests
ownerIdentity = [[SOGoUserManager sharedUserManager]
contactInfosForUserWithUIDorEmail: owner];
[displayName appendFormat: @" (%@ <%@>)", [ownerIdentity objectForKey: @"cn"],
[ownerIdentity objectForKey: @"c_email"]];
}
folderSubscriptionValues = [[NSMutableDictionary alloc] initWithObjectsAndKeys: displayName, @"FolderName",
[ownerIdentity objectForKey: @"cn"], @"UserName",
[ownerIdentity objectForKey: @"c_email"], @"Email", nil];
dd = [[context activeUser] domainDefaults];
subjectFormat = [dd subscriptionFolderFormat];
displayName = [folderSubscriptionValues keysWithFormat: subjectFormat];
[displayName retain];
}
}
}
/* This method fetches the display name defined by the owner, but is also the
@@ -1358,11 +1367,15 @@ static NSArray *childRecordFields = nil;
}
NSZoneFree (NULL, selectors);
/* If we haven't gotten any result to return, let's use the previously
supplied sync-token */
if (max == 0)
newToken = syncToken;
/* If the most recent c_lastmodified is "now", we need to return "now - 1"
in order to make sure during the next sync that every records that might
get added at the same moment are not lost. */
if (!newToken || newToken == now)
else if (!newToken || newToken == now)
newToken = now - 1;
newTokenStr = [NSString stringWithFormat: @"%d", newToken];
+8 -1
View File
@@ -1,6 +1,6 @@
/* SOGoParentFolder.h - this file is part of SOGo
*
* Copyright (C) 2006-2013 Inverse inc.
* Copyright (C) 2006-2014 Inverse inc.
*
* This file is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -22,7 +22,9 @@
#define SOGOPARENTFOLDERS_H
#import "SOGoFolder.h"
#import "SOGoConstants.h"
@class EOAdaptorChannel;
@class NSMutableDictionary;
@class NSString;
@class WOResponse;
@@ -39,6 +41,7 @@
+ (Class) subFolderClass;
- (NSString *) defaultFolderName;
- (NSString *) collectedFolderName;
- (NSException *) appendPersonalSources;
- (void) removeSubFolder: (NSString *) subfolderName;
@@ -58,6 +61,10 @@
- (id) lookupPersonalFolder: (NSString *) name
ignoringRights: (BOOL) ignoreRights;
- (NSException *) fetchSpecialFolders: (NSString *) sql
withChannel: (EOAdaptorChannel *) fc
andFolderType: (SOGoFolderType) folderType;
@end
#endif /* SOGOPARENTFOLDERS_H */
+73 -47
View File
@@ -38,7 +38,7 @@
#import <DOM/DOMElement.h>
#import <DOM/DOMProtocols.h>
#import <SaxObjC/XMLNamespaces.h>
#import <SOGo/SOGoUserDefaults.h>
#import <SOGo/SOGoUserSettings.h>
#import "NSObject+DAV.h"
@@ -153,9 +153,15 @@ static SoSecurityManager *sm = nil;
return @"Personal";
}
- (void) _createPersonalFolder
- (NSString *) collectedFolderName
{
return @"Collected";
}
- (void) createSpecialFolder: (SOGoFolderType) folderType
{
NSArray *roles;
NSString *folderName;
SOGoGCSFolder *folder;
SOGoUser *folderOwner;
@@ -165,50 +171,70 @@ static SoSecurityManager *sm = nil;
// We autocreate the calendars if the user is the owner, a superuser or
// if it's a resource as we won't necessarily want to login as a resource
// in order to create its database tables.
// FolderType is an enum where 0 = Personal and 1 = collected
if ([roles containsObject: SoRole_Owner] ||
(folderOwner && [folderOwner isResource]))
{
folder = [subFolderClass objectWithName: @"personal" inContainer: self];
[folder setDisplayName: [self defaultFolderName]];
[folder
setOCSPath: [NSString stringWithFormat: @"%@/personal", OCSPath]];
if (folderType == SOGoPersonalFolder)
{
folderName = @"personal";
folder = [subFolderClass objectWithName: folderName inContainer: self];
[folder setDisplayName: [self defaultFolderName]];
}
else if (folderType == SOGoCollectedFolder)
{
folderName = @"collected";
folder = [subFolderClass objectWithName: folderName inContainer: self];
[folder setDisplayName: [self collectedFolderName]];
}
[folder setOCSPath: [NSString stringWithFormat: @"%@/%@", OCSPath, folderName]];
if ([folder create])
[subFolders setObject: folder forKey: @"personal"];
[subFolders setObject: folder forKey: folderName];
}
}
- (NSException *) _fetchPersonalFolders: (NSString *) sql
withChannel: (EOAdaptorChannel *) fc
- (NSException *) fetchSpecialFolders: (NSString *) sql
withChannel: (EOAdaptorChannel *) fc
andFolderType: (SOGoFolderType) folderType
{
NSArray *attrs;
NSDictionary *row;
SOGoGCSFolder *folder;
NSString *key;
NSException *error;
SOGoUserDefaults *ud;
ud = [[context activeUser] userDefaults];
if (!subFolderClass)
subFolderClass = [[self class] subFolderClass];
error = [fc evaluateExpressionX: sql];
if (!error)
{
attrs = [fc describeResults: NO];
while ((row = [fc fetchAttributes: attrs withZone: NULL]))
{
attrs = [fc describeResults: NO];
while ((row = [fc fetchAttributes: attrs withZone: NULL]))
{
key = [row objectForKey: @"c_path4"];
if ([key isKindOfClass: [NSString class]])
{
folder = [subFolderClass objectWithName: key inContainer: self];
[folder setOCSPath: [NSString stringWithFormat: @"%@/%@",
OCSPath, key]];
[subFolders setObject: folder forKey: key];
}
}
if (![subFolders objectForKey: @"personal"])
[self _createPersonalFolder];
key = [row objectForKey: @"c_path4"];
if ([key isKindOfClass: [NSString class]])
{
folder = [subFolderClass objectWithName: key inContainer: self];
[folder setOCSPath: [NSString stringWithFormat: @"%@/%@", OCSPath, key]];
[subFolders setObject: folder forKey: key];
}
}
if (folderType == SOGoPersonalFolder)
{
if (![subFolders objectForKey: @"personal"])
[self createSpecialFolder: SOGoPersonalFolder];
}
else if (folderType == SOGoCollectedFolder)
{
if (![subFolders objectForKey: @"collected"])
if ([[ud selectedAddressBook] isEqualToString:@"collected"])
[self createSpecialFolder: SOGoCollectedFolder];
}
}
return error;
}
@@ -221,25 +247,21 @@ static SoSecurityManager *sm = nil;
NSException *error;
cm = [GCSChannelManager defaultChannelManager];
folderLocation
= [[GCSFolderManager defaultFolderManager] folderInfoLocation];
folderLocation = [[GCSFolderManager defaultFolderManager] folderInfoLocation];
fc = [cm acquireOpenChannelForURL: folderLocation];
if ([fc isOpen])
{
gcsFolderType = [[self class] gcsFolderType];
sql
= [NSString stringWithFormat: (@"SELECT c_path4 FROM %@"
@" WHERE c_path2 = '%@'"
@" AND c_folder_type = '%@'"),
[folderLocation gcsTableName],
owner,
gcsFolderType];
error = [self _fetchPersonalFolders: sql withChannel: fc];
[cm releaseChannel: fc];
// sql = [sql stringByAppendingFormat:@" WHERE %@ = '%@'",
// uidColumnName, [self uid]];
}
{
gcsFolderType = [[self class] gcsFolderType];
sql = [NSString stringWithFormat: (@"SELECT c_path4 FROM %@"
@" WHERE c_path2 = '%@'"
@" AND c_folder_type = '%@'"),
[folderLocation gcsTableName], owner, gcsFolderType];
error = [self fetchSpecialFolders: sql withChannel: fc andFolderType: SOGoPersonalFolder];
[cm releaseChannel: fc];
}
else
error = [NSException exceptionWithName: @"SOGoDBException"
reason: @"database connection could not be open"
@@ -248,6 +270,7 @@ static SoSecurityManager *sm = nil;
return error;
}
- (NSException *) appendSystemSources
{
return nil;
@@ -388,12 +411,15 @@ static SoSecurityManager *sm = nil;
subFolders = [NSMutableDictionary new];
error = [self appendPersonalSources];
if (!error)
error = [self appendSystemSources];
if ([self respondsToSelector:@selector(appendCollectedSources)])
error = [self appendCollectedSources];
if (!error)
error = [self appendSystemSources]; // TODO : Not really a testcase, see function
if (error)
{
[subFolders release];
subFolders = nil;
}
{
[subFolders release];
subFolders = nil;
}
}
else
error = nil;
+1 -3
View File
@@ -1,8 +1,6 @@
/* SOGoPermissions.m - this file is part of SOGo
*
* Copyright (C) 2006 Inverse inc.
*
* Author: Wolfgang Sourdeau <wsourdeau@inverse.ca>
* Copyright (C) 2006-2014 Inverse inc.
*
* This file is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
+2 -2
View File
@@ -584,7 +584,7 @@ static NSString *sieveScriptName = @"sogo";
int count, max;
NSDictionary *currentScript;
sieveScript = [NSMutableString stringWithCapacity: 8192];
sieveScript = [NSMutableString string];
ASSIGN (requirements, newRequirements);
[scriptError release];
@@ -755,7 +755,7 @@ static NSString *sieveScriptName = @"sogo";
return nil;
}
return client;
return [client autorelease];
}
+2
View File
@@ -42,6 +42,7 @@
- (BOOL) trustProxyAuthentication;
- (NSString *) encryptionKey;
- (BOOL) useRelativeURLs;
- (NSString *) sieveFolderEncoding;
- (BOOL) isWebAccessEnabled;
- (BOOL) isCalendarDAVAccessEnabled;
@@ -95,6 +96,7 @@
- (int) maximumPingInterval;
- (int) maximumSyncInterval;
- (int) internalSyncInterval;
- (int) maximumSyncWindowSize;
@end
Loaded 100 of 271 files, more files were not shown because too many files have changed in this diff. Show more