Compare commits
1
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
1798dd1f0a |
No files matched your search
@@ -53,6 +53,3 @@ Tests/node_modules
|
||||
Tests/lib/config.js
|
||||
Tests/results.xml
|
||||
Tests/results
|
||||
|
||||
# SAML2 autogenerated files
|
||||
SoObjects/SOGo/SOGoSAML2Exceptions.*
|
||||
@@ -2627,17 +2627,17 @@ FIXME
|
||||
// We first check of any of the collections we want to sync are already
|
||||
// in an other sync process. If that's the case, we do not do anything
|
||||
// and we return immediately. So we'll let the other sync process terminate
|
||||
globalMetadata = [self globalMetadataForDevice];
|
||||
for (j = 0; j < [allCollections count]; j++)
|
||||
{
|
||||
aCollection = [allCollections objectAtIndex: j];
|
||||
globalMetadata = [self globalMetadataForDevice];
|
||||
|
||||
key = [NSString stringWithFormat: @"SyncRequest+%@", [[[(id)[aCollection getElementsByTagName: @"CollectionId"] lastObject] textValue] stringByUnescapingURL]];
|
||||
|
||||
if (!([[globalMetadata objectForKey: key] isEqual: processIdentifier]))
|
||||
{
|
||||
if (debugOn)
|
||||
[self logWithFormat: @"EAS - Discard response %@", globalMetadata];
|
||||
[self logWithFormat: @"EAS - Discard response %@", [self globalMetadataForDevice]];
|
||||
|
||||
[output appendString: @"<Status>13</Status>"];
|
||||
[output appendString: @"</Sync>"];
|
||||
|
||||
-195
@@ -2,201 +2,6 @@
|
||||
|
||||
All notable changes to this project will be documented in this file. See [standard-version](https://github.com/conventional-changelog/standard-version) for commit guidelines.
|
||||
|
||||
## [5.9.1](https://github.com/Alinto/sogo/compare/SOGo-5.9.0...SOGo-5.9.1) (2023-12-12)
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* **addressbook:** Add global address book autocomplete on vlist ([11ad203](https://github.com/Alinto/sogo/commit/11ad203388ad33a22bd300047745869a27872cd9) [0b1f2b0](https://github.com/Alinto/sogo/commit/0b1f2b0405410628cb7ba8602b9286d71256d8e6) [0560efd](https://github.com/Alinto/sogo/commit/0560efd1f6a94bcb256d970ebe5535954e36cafe), [f85c531](https://github.com/Alinto/sogo/commit/f85c53190c38c4e28536f78e43623f6776248291))
|
||||
* **core:** Add mobile provisioning download for Apple's devices ([19915e3](https://github.com/Alinto/sogo/commit/19915e3dffc14c90b03277f9f97b4e6b1158aa87) [6d16ee7](https://github.com/Alinto/sogo/commit/6d16ee798863a7f196c14915c47888adf0758f1a) [d38cc86](https://github.com/Alinto/sogo/commit/d38cc86d29ffce580bf26dd278a6b68556471c14) [cd50ee0](https://github.com/Alinto/sogo/commit/cd50ee017656f3e6cfd46cff7147258d4893706b) [65f8336](https://github.com/Alinto/sogo/commit/65f8336255577566282039f4058fe4f3c38a16d4) [4fe01f9](https://github.com/Alinto/sogo/commit/4fe01f96d73690bf1a2d60755bb552d25324ccaf) [a273f7a](https://github.com/Alinto/sogo/commit/a273f7aca5df120a50af0486161880040ee20519))
|
||||
* **core:** add SOGoDisableExport option to disable export for mail, calendar and contacts ([d0b4b50](https://github.com/Alinto/sogo/commit/d0b4b50456cf3e23aa12d5cf7165531a7670edf6))
|
||||
* **core:** Add SOGoDisableSharingAnyAuthUser option to disable sharing to any user authenticated for mail, contacts and calendar ([e0ef59d](https://github.com/Alinto/sogo/commit/e0ef59d5f212aced72a17e3af9e72a434eedd68e))
|
||||
* **mail:** Add an option in Preferences to display full email instead of name alone in mailboxes ([2e670f7](https://github.com/Alinto/sogo/commit/2e670f7e6293b703f8cfe23ebf923ce13b463a19))
|
||||
* **mail:** Add SOGoMailHideInlineAttachments option to hide attachments when inline. Fixes [#5490](https://bugs.sogo.nu/view.php?id=5490). ([f41ab2e](https://github.com/Alinto/sogo/commit/f41ab2ed0c71903b80bda66adc54cc94b0cf70d6) [abce5f4](https://github.com/Alinto/sogo/commit/abce5f42cf4daef721ebf6b48d727eb957e20b14) [aa53524](https://github.com/Alinto/sogo/commit/aa535247513b06804fce666fe20ebcc9bd231c1f) [ca2c5c1](https://github.com/Alinto/sogo/commit/ca2c5c188ed980629f494e5b8e23ba42cdd54f58))
|
||||
* **password:** Add specials characters to POLICY_MIN_SPECIAL_SYMBOLS ([7d66699](https://github.com/Alinto/sogo/commit/7d666992198e5e91e29081b99443607b1c96d354))
|
||||
* **preferences:** can set SOGoForwardConstraints to 3 to accept both internal and external domains from SOGoForwardConstraintsDomains ([c872fb4](https://github.com/Alinto/sogo/commit/c872fb4d9c68382431a52181073ba1964cf44c22))
|
||||
* **reply:** add references header when replying ([b396a27](https://github.com/Alinto/sogo/commit/b396a277f00d879d2c179db780d6f621a9057d0c))
|
||||
* **ux:** select all button will unselect all if click again ([6e60b10](https://github.com/Alinto/sogo/commit/6e60b107556212123f41d95e4399d9605a4c16f9) [1ae02eb](https://github.com/Alinto/sogo/commit/1ae02ebd708da0a53ae588531e122adddaa7a5de))
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **addressbook:** autocomplete correctly return two contacts from two ldap with same id ([1d3da92](https://github.com/Alinto/sogo/commit/1d3da92bab972ec7ec935eba72128e72387216c0))
|
||||
* **calendar:** do not let DURATION and DTEND be both present in vevent ([0023a9c](https://github.com/Alinto/sogo/commit/0023a9ce3c4027bb32699efc5529a4a53d7699f6))
|
||||
* **calendar:** If content is NSData, properly convert it to NSString ([21571ab](https://github.com/Alinto/sogo/commit/21571ab1e000da47b91b3f21bfaba89d1abec71f))
|
||||
* **calendar:** properly set the start date range for yearly events ([1e8127b](https://github.com/Alinto/sogo/commit/1e8127b46122e83df02c394536950288b0ee16aa))
|
||||
* **core:** Add cache for CDefaultsSize to avoid spamming SQL request ([87e1240](https://github.com/Alinto/sogo/commit/87e1240ace297645559e698a8c1f1a76b9901895) [81f2d88](https://github.com/Alinto/sogo/commit/81f2d88db362cc21c50858295d0e9d375db61f67))
|
||||
* **core:** Fix calendar issues with Thunderbird related to vlist exclusion. Fix error log on photo. Closes [#5885](https://bugs.sogo.nu/view.php?id=5885) ([6b08f2e](https://github.com/Alinto/sogo/commit/6b08f2e4decc87ee57609355dee5a6657bca6110))
|
||||
* **core:** Fix user profile retrieval when using utf8mb4_bin collation - in this case the SQL results may return NSdata instead of NSString. Fixes [#5902](https://bugs.sogo.nu/view.php?id=5902). ([3cd9910](https://github.com/Alinto/sogo/commit/3cd991063805f88a5672b10fbcc04a4149e13e26))
|
||||
* **hmtl:** prevent html injection of tag form ([7481ccf](https://github.com/Alinto/sogo/commit/7481ccf37087c3f456d7e5a844da01d0f8883098))
|
||||
* **http:** set correct Accept header when dowloading eml file ([c569ed2](https://github.com/Alinto/sogo/commit/c569ed2d77cf23c274c3dfc7cd3533cf268d09af))
|
||||
* **mail:** Clean temporary files when mail is sent ([f35a37b](https://github.com/Alinto/sogo/commit/f35a37bbdd2005a63b42f9bab1588ce9a935b112) [d163405](https://github.com/Alinto/sogo/commit/d1634054d6f4dd18bdb82a7903b3bbbceae5b0e8))
|
||||
* **mail:** Fix duplication of images when saving draft. Fixes [#5888](https://bugs.sogo.nu/view.php?id=5888) ([ee9899f](https://github.com/Alinto/sogo/commit/ee9899fe3d6aa7bf694f959a8e4f09faa39e737a) [9c09485](https://github.com/Alinto/sogo/commit/9c0948518b1e8365d6fadb3fccae1a0257b518ed))
|
||||
* **scripts:** wrong type for c_value in script for complete unicode coverage ([65374e4](https://github.com/Alinto/sogo/commit/65374e44fc2a861d3a7c9f1f9ba25505601d79f1))
|
||||
* **sogo-tool:** add protection to expire-sessions if the input is not an integer ([bb943e4](https://github.com/Alinto/sogo/commit/bb943e414d3fc8b71ebb169e31c261cd2dcd6d37))
|
||||
* **translation:** incorret value causing trouble to transifex ([4b45dc7](https://github.com/Alinto/sogo/commit/4b45dc7584984602071efe99b9638c6231efba8c))
|
||||
|
||||
|
||||
### Localization
|
||||
|
||||
* **ar:** Update Arabic translations ([28d883d](https://github.com/Alinto/sogo/commit/28d883dac1ccbafb878b9e13ba38f570f44ea325))
|
||||
* **bg_BG:** Update Bulgarian translations ([3815bc1](https://github.com/Alinto/sogo/commit/3815bc1c5e6fe37aaad5492c246697fe34e4286d))
|
||||
* **bs_BA:** Update Bosnian translations ([2afbcbe](https://github.com/Alinto/sogo/commit/2afbcbeaed24571785437aa5af1c092ba129ce97))
|
||||
* **ca:** Update Catalan translations ([3040543](https://github.com/Alinto/sogo/commit/3040543c797fe9176df048b8a72a2c6f7dbfd3b7))
|
||||
* **cs:** Update Czech translations ([029a821](https://github.com/Alinto/sogo/commit/029a82143f1d0d82ca8d6a5c9e5a14ac351718bf))
|
||||
* **cy:** Update Welsh translations ([06a6ed7](https://github.com/Alinto/sogo/commit/06a6ed776824f47e5606c29a162903380e72fbd9))
|
||||
* **da_DK:** Update Danish translations ([76911fd](https://github.com/Alinto/sogo/commit/76911fd4454c9f0213f11b4e2e95b29010eed9a4))
|
||||
* **de:** Update German translations ([8a4bc2a](https://github.com/Alinto/sogo/commit/8a4bc2a71642a2a431008c02214ffdd0eb2ace23))
|
||||
* **es_AR:** Update SpanishArgentina translations ([527fded](https://github.com/Alinto/sogo/commit/527fded19ceaf995963e997659b79c5e1290b8c5))
|
||||
* **es_ES:** Update SpanishSpain translations ([057a1ba](https://github.com/Alinto/sogo/commit/057a1ba1053f462cd0633b86c3ee86d76acbd6bb))
|
||||
* **eu:** Update Basque translations ([33f3a1b](https://github.com/Alinto/sogo/commit/33f3a1b716a006df8a28c416fc947e44fc285e37))
|
||||
* **fi:** Update Finnish translations ([dba2332](https://github.com/Alinto/sogo/commit/dba2332864fa38116615c9b58eb3590fe251a37a))
|
||||
* **gl:** Change Galician name to Galego ([6eae3f2](https://github.com/Alinto/sogo/commit/6eae3f2b1601033558a791c27bc4e27acf0825e7))
|
||||
* **gl:** Update Galician translations ([5aa7ebf](https://github.com/Alinto/sogo/commit/5aa7ebf2aefef6e45423b2193c136fadced51375))
|
||||
* **he:** Update Hebrew translations ([694c21d](https://github.com/Alinto/sogo/commit/694c21de99522733c95304d0e775e81493ff7200))
|
||||
* **hr_HR:** Update Croatian translations ([715d25d](https://github.com/Alinto/sogo/commit/715d25d40a84b518b1d8f97b4946045a4be761b5))
|
||||
* **hu:** Update Hungarian translations ([0b22fa6](https://github.com/Alinto/sogo/commit/0b22fa6cd5b55db5d021bbbfcc53d411ff4bb9d7))
|
||||
* **id_ID:** Update Indonesian translations ([a16d29b](https://github.com/Alinto/sogo/commit/a16d29b70b50eeee14a3ad1b627e8f8a59216461))
|
||||
* **is:** Update Icelandic translations ([2de6241](https://github.com/Alinto/sogo/commit/2de624122045d9df877f39f665d271056af0fba8))
|
||||
* **it:** Update Italian translations ([53036b3](https://github.com/Alinto/sogo/commit/53036b323fe65b4bf40c838ff0578fa076bfea93))
|
||||
* **ja:** Update Japanese translations ([41fb85e](https://github.com/Alinto/sogo/commit/41fb85e6a320b25776ce63672ef4251e932f82f0))
|
||||
* **kk:** Update Kazakh translations ([2174195](https://github.com/Alinto/sogo/commit/21741954dd2762b3f03c0b5b21b8feb4d826cfb1))
|
||||
* **lt:** Update Lithuanian translations ([6e9e7f9](https://github.com/Alinto/sogo/commit/6e9e7f98ef379ce26937e00daedfad88383615a4))
|
||||
* **lv:** Update Latvian translations ([a9ecaed](https://github.com/Alinto/sogo/commit/a9ecaed5fa6fe8fecf08ec0354bbb187824d2fbb))
|
||||
* **mk_MK:** Update Macedonian translations ([d6c9ecd](https://github.com/Alinto/sogo/commit/d6c9ecdeb0490fa44bc1c444819b5c82ccf89073))
|
||||
* **nb_NO:** Update NorwegianBokmal translations ([3d5f352](https://github.com/Alinto/sogo/commit/3d5f35211189802ef031be688cadddc2ab684d37))
|
||||
* **nl:** Update Dutch translations ([402c3d0](https://github.com/Alinto/sogo/commit/402c3d0a1f83d32af2fca2f3e5abafbe8850de08))
|
||||
* **nn_NO:** Update NorwegianNynorsk translations ([6d270fe](https://github.com/Alinto/sogo/commit/6d270fe7a4bad2253f7e8427be660405460b2139))
|
||||
* **pl:** Update Polish translations ([c75f210](https://github.com/Alinto/sogo/commit/c75f210864de1a7bb313c1ba21be987143a2f7ca))
|
||||
* **pt_BR:** Update BrazilianPortuguese translations ([cfab856](https://github.com/Alinto/sogo/commit/cfab85624b3e725f121ea61d181340189e969229))
|
||||
* **pt:** Update Portuguese translations ([e4f07c4](https://github.com/Alinto/sogo/commit/e4f07c466e195d59f72b934567cb3aa4d18cb9f3))
|
||||
* **ro_RO:** Update Romanian translations ([d9c716c](https://github.com/Alinto/sogo/commit/d9c716c1b679a792876c9bc7ebed2d530ab8d89f))
|
||||
* **ru:** Update Russian translations ([3809ec3](https://github.com/Alinto/sogo/commit/3809ec3904a7882239e50c671aa72c9f7f013c62))
|
||||
* **sk:** Update Slovak translations ([cd7e06c](https://github.com/Alinto/sogo/commit/cd7e06c2ede339d078e54b76b6078523b78383fc))
|
||||
* **sl_SI:** Update Slovenian translations ([ca21bf4](https://github.com/Alinto/sogo/commit/ca21bf4ce1758717eac8ec961788d067e9428456))
|
||||
* **sr_ME@latin:** Update Montenegrin translations ([fe7369e](https://github.com/Alinto/sogo/commit/fe7369e427d7cc6f4efb23a78437bd2b2c65e6cd))
|
||||
* **sr@latin:** Update SerbianLatin translations ([9126cae](https://github.com/Alinto/sogo/commit/9126cae27d49b7f199f2a6b259eb40589b011a55))
|
||||
* **sr:** Update Serbian translations ([ed444fd](https://github.com/Alinto/sogo/commit/ed444fdc2266888f9d15d557e1ba92896158d870))
|
||||
* **sv:** Update Swedish translations ([10369aa](https://github.com/Alinto/sogo/commit/10369aa654058b50285ae7239a9356f736c7ac1c))
|
||||
* **tr_TR:** Update TurkishTurkey translations ([8d1fc1e](https://github.com/Alinto/sogo/commit/8d1fc1e306d926b3efeab2e3017823201254caf0))
|
||||
* **uk:** Update Ukrainian translations ([0e50d37](https://github.com/Alinto/sogo/commit/0e50d37531d850b46009af2af5049a6b46541da0))
|
||||
* **zh_CN:** Update ChineseChina translations ([cf87022](https://github.com/Alinto/sogo/commit/cf87022348296b9659e83e3a3985b48d89f5e01f))
|
||||
* **zh_TW:** Update ChineseTaiwan translations ([fc2a82c](https://github.com/Alinto/sogo/commit/fc2a82c3b0f4b780df3b4d27ad0da78e34f665c6))
|
||||
|
||||
## [5.9.0](https://github.com/Alinto/sogo/compare/SOGo-5.8.3...SOGo-5.9.0) (2023-09-27)
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* **calendar:** Add CALDAV Mac OS X Sonoma (14) support ([68cef9a](https://github.com/Alinto/sogo/commit/68cef9ae039e3040348a6a16733f4017a7e8ba42) [87bbba0](https://github.com/Alinto/sogo/commit/87bbba0c8d7893a776ff9ccafbe8227e98aa14ed))
|
||||
* **core:** Add in user settings to force password change for a user when logging ([9918f75](https://github.com/Alinto/sogo/commit/9918f7558028f7a9d403d93c034f62a384ede4f2) [d1a51e8](https://github.com/Alinto/sogo/commit/d1a51e8340a74e7803cb808f7ff8a7bf959759c1))
|
||||
* **notification:** add enotify sieve script in preferences ([82b69d1](https://github.com/Alinto/sogo/commit/82b69d14149c4ef3f6e913232bed478635009ed5) [6b64705](https://github.com/Alinto/sogo/commit/6b647050fc64b9beb024379ed7d37b6a54356798))
|
||||
* **preferences:** Add user preferences to enable / disable signature for new mail, reply and forward ([8582e42](https://github.com/Alinto/sogo/commit/8582e423bdd69e200ff8cdec7f1d94634455e519))
|
||||
|
||||
|
||||
### Enhancements
|
||||
|
||||
* **calendar:** add copy to clipboard button in "links to calendar" panel ([5c4d467](https://github.com/Alinto/sogo/commit/5c4d467d06161324826ee1ef0a713fc340dfaed5))
|
||||
* **preferences:** Add subtext in vacation filter to explain the behavior ([09d0326](https://github.com/Alinto/sogo/commit/09d03261af82071b451fcba32a431f5c0432fa5d))
|
||||
* **preferences:** show special characters when changing password ([6050c4e](https://github.com/Alinto/sogo/commit/6050c4ee61811b989e39b3c38a2e86360213be28))
|
||||
* **packaging:** Add debian 10 support ([528d178](https://github.com/Alinto/sogo/commit/528d1782563c10bb0905ab159eb3577973615ae4))
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **addressbook:** Fix asynchronous selection of contacts - cards are not added as they're not yet loaded. ([1df2586](https://github.com/Alinto/sogo/commit/1df25867455b46ff634e3a53cdf10fbb8f2a6a4b))
|
||||
* **addressbook:** Fix broken image in shared address book when image is null ([5d315ac](https://github.com/Alinto/sogo/commit/5d315ac18066bd952fc45121ad1fe431a7b0305c))
|
||||
* **addressbook:** Remove vlist from dav request for Thunderbird only. In Thunderbird 115, using vlist results in contact duplication ([f0719ee](https://github.com/Alinto/sogo/commit/f0719ee409dee693e7b4792bd127ccdd4b3043f1))
|
||||
* **calendar:** Add negative timestamp support to use date before 1970-01-01. Fixes [#4914](https://bugs.sogo.nu/view.php?id=4914) (partial) ([8a8dcae](https://github.com/Alinto/sogo/commit/8a8dcae0298b81a486ad454bd58b90821eec6894))
|
||||
* **calendar:** Avoid event duplication when adding an ics with an older sequence number and event already added in calendar ([cab5bc0](https://github.com/Alinto/sogo/commit/cab5bc011eb3afb5ff36e0f49f9919380c5835e9))
|
||||
* **calendar:** deleted occurence of repeated event could cause some troubles with other imrbicated occurences ([f3981c4](https://github.com/Alinto/sogo/commit/f3981c42765f04431dd34af35d446aca03a1b5bc))
|
||||
* **calendar:** Do not send tentative participation mail when user open mail or add ics to calendar app ([382b8a0](https://github.com/Alinto/sogo/commit/382b8a0a1a37fe89d268224a28570134bd2d2481))
|
||||
* **calendar:** Fix calendar green dot stucks in original day when browser is opened for days and day change. Fixes [#5783](https://bugs.sogo.nu/view.php?id=5783) ([22372a7](https://github.com/Alinto/sogo/commit/22372a7598adbcf9c229f174f58842a89ada963f))
|
||||
* **calendar:** Fix calendar green dot stucks in original day when browser is opened for days and day changed ([f22672c](https://github.com/Alinto/sogo/commit/f22672cef852116d08ba6a8720721fd0ce2f717f) [650ca16](https://github.com/Alinto/sogo/commit/650ca16ff78ecbd0dbf23bdf381461d75dda864e))
|
||||
* **calendar:** Fix duplicates in series appointments for Apple's Calendar ([57ba539](https://github.com/Alinto/sogo/commit/57ba5397e5c14b3cb0a31c5617718c2866fca72f) [7600e9e](https://github.com/Alinto/sogo/commit/7600e9e1798062db167b1206829d1ca8be8c8881))
|
||||
* **calendar:** Fix MacOS X Ventura calendar support. Fixes [#5639](https://bugs.sogo.nu/view.php?id=5639) ([4f7c731](https://github.com/Alinto/sogo/commit/4f7c73143f38b1e7e00a51b9457b55ce609a02a9))
|
||||
* **calendar:** Fix NAME:NSInvalidArgumentException REASON:SOGoAppointmentFolder(instance) does not recognize davCalendarProxyReadFor ([82178ee](https://github.com/Alinto/sogo/commit/82178eeba95d19842afcd309d1a291e971d04bbe))
|
||||
* **calendar:** Fix Out of range value for column 'c_startdate' SQL error ([32a0499](https://github.com/Alinto/sogo/commit/32a04998e50cc4adbc72b7dc36de67b8e9e4b0f2) [158a5be](https://github.com/Alinto/sogo/commit/158a5becc902b1d241575ac5098ceb527a0011ff) [1a257c7](https://github.com/Alinto/sogo/commit/1a257c7768fb9010db32e050650693d26f3e5a29))
|
||||
* **calendar:** Fix regression on calendar event modification. Start date was set to 1970. Fixes [#5777](https://bugs.sogo.nu/view.php?id=5777). ([b68a215](https://github.com/Alinto/sogo/commit/b68a215ea29807db688e2f9a46a4a8d0907f83b8))
|
||||
* **calendar:** Fix search issue on repeated events when one title is changed the event could not be found ([139ef15](https://github.com/Alinto/sogo/commit/139ef1528456d416ec5e99b56eba4f064d20738b))
|
||||
* **calendar:** Increase timeout to 60 seconds for large ics calendar. Closes [#5781](https://bugs.sogo.nu/view.php?id=5781). ([fa5264e](https://github.com/Alinto/sogo/commit/fa5264e822867549aa5bc23fb905fd18c065c087))
|
||||
* **calendar:** recurrenceid wasn't working properly ([c5814d6](https://github.com/Alinto/sogo/commit/c5814d60a4b3bb632a5ef50121453f592e9b4bd6))
|
||||
* **calendar:** Repeated events didn't use the correct timezone in some cases ([bd8b49d](https://github.com/Alinto/sogo/commit/bd8b49d2c95a187b218bd7c4b8689a85faa3dda2))
|
||||
* **calendar:** Revert commit e47566e3a and change HTTP status from 409 to 403 when event has a sequence number lower than than the new one. This should fix some Apple's duplication events ([fce02db](https://github.com/Alinto/sogo/commit/fce02db7d15c827c24a89d67a457f23379199e4e))
|
||||
* **calendar:** some kinds of events are not shown ([f2df405](https://github.com/Alinto/sogo/commit/f2df4056161a96da750cba4a083a05ce1390ad55))
|
||||
* **calendar:** When an event has no date (weird case), it is not possible to remove the event (NSException) ([4d2c771](https://github.com/Alinto/sogo/commit/4d2c77195e72def1e39caab6c402c9b297d844ff))
|
||||
* **cas:** correction on pgtUrl ([a1528bc](https://github.com/Alinto/sogo/commit/a1528bcc0304ca4cce1a088c557e0e7d806309d9))
|
||||
* **CAS:** key based on tciket was too long for memecached ([68b1c1e](https://github.com/Alinto/sogo/commit/68b1c1e5dfac8383e6cbafadf639105d25680cb4))
|
||||
* **contacts:** Fix 5875 ([ff263b9](https://github.com/Alinto/sogo/commit/ff263b99fe5234752e875cd25101948c3fd177a5))
|
||||
* **js:** Fix invalid sass version ([5e471c5](https://github.com/Alinto/sogo/commit/5e471c5d66f48d01cc1a75419dbdbaed57600331))
|
||||
* **js:** Fix url redirection to null when disconnected. Closes [#5844](https://bugs.sogo.nu/view.php?id=5844). ([dc23721](https://github.com/Alinto/sogo/commit/dc23721b75405ff27624210598799366bdad91a9))
|
||||
* **login:** Fix language list sorting on login page ([e0d76fb](https://github.com/Alinto/sogo/commit/e0d76fba65d46a8299bb00aa677308521f7cfc03))
|
||||
* **mail:** autocomplete can't find two lists named the same but in different address books ([854e83c](https://github.com/Alinto/sogo/commit/854e83c22c5ca47915e0f426482d004e5ecc9868))
|
||||
* **mail:** Fix a bug when pressing the key DEL more than once to delete mails ([af59e57](https://github.com/Alinto/sogo/commit/af59e57fc03f0bb5b5908510ad5da8ad11a601d9))
|
||||
* **mail:** Fix email without subject according to RFC 2822. Fixes [#5743](https://bugs.sogo.nu/view.php?id=5743) ([ab954a1](https://github.com/Alinto/sogo/commit/ab954a150e99b4b35317a836791e831d460a1059))
|
||||
* **mail:** Recipients with umlaut (ü) trigerred the end of string ([9c7ef08](https://github.com/Alinto/sogo/commit/9c7ef089c28df73ff3171efa1ca7f34d0d8b3a6c))
|
||||
* **mail:** Removed invalid HTML tags in mail body. Fixes [#5755](https://bugs.sogo.nu/view.php?id=5755) ([f9fdad2](https://github.com/Alinto/sogo/commit/f9fdad251da20617430e1626a72c574221215ff1))
|
||||
* **mail:** Signature images are not handled properly in forwarded or replied to messages. Save extracted body parts (for signature) into file system. Fixes [#5768](https://bugs.sogo.nu/view.php?id=5768) ([23da4cc](https://github.com/Alinto/sogo/commit/23da4cca7bbf5907f499d218c65b063d875a3120) [d436afb](https://github.com/Alinto/sogo/commit/d436afb05433bde00040b8b9cdd7432bf5d00bb0))
|
||||
* **mail:** Templates namespace is correctly returned ([e959583](https://github.com/Alinto/sogo/commit/e959583390f50339f02f4870ef88a16c63b0dbd5))
|
||||
* **mail:** When signature is too big and changing identity, a 'regex too big' error occurs. In this case just add signature at the end. Fixes [#5695](https://bugs.sogo.nu/view.php?id=5695). ([b7e529d](https://github.com/Alinto/sogo/commit/b7e529dd490c493a726878df0c712670fbd3fa3f))
|
||||
* **notification:** body was not send + missing tooltip ([5534d4b](https://github.com/Alinto/sogo/commit/5534d4b6eed4b7b2343bb711fa9b7441f1d13ed7))
|
||||
* **notifications:** bug with older user profile ([16af360](https://github.com/Alinto/sogo/commit/16af360006dd67e246590a41574634a669cf6070))
|
||||
* **preferences:** add autocomplete for recipients in automatic forward ([ba4c7a5](https://github.com/Alinto/sogo/commit/ba4c7a5300d7eb5ea15c13a8b3fd9b27aa4cf53a))
|
||||
* **preferences:** Disable auxiliary mail accounts creation when SOGoCreateIdentitiesDisabled is enabled ([b666137](https://github.com/Alinto/sogo/commit/b666137a7036a1fb095b17c767f342028df8211c))
|
||||
* **preferences:** Fix language list sorting on preferences page ([f5f74f7](https://github.com/Alinto/sogo/commit/f5f74f751a63f6de3e247db7fff7da5ca215bdff))
|
||||
* **preferences:** Fix NSException issue when SOGoCreateIdentitiesDisabled is set ([eb42e17](https://github.com/Alinto/sogo/commit/eb42e1787c427c8ac2bae9ecb9129fcf430eaaca))
|
||||
* **preferences:** Fix the issue when signature is too long and preferences are not saved. Note that MySQL profile table should be updated with a migration script ([326bc68](https://github.com/Alinto/sogo/commit/326bc68f4c928ed2be8cea6d57d62a6e2ca04f6b))
|
||||
* **preferences:** Signature could not be saved when SOGoCreateIdentitiesDisabled is set to YES and no signature is on identity ([4abf12b](https://github.com/Alinto/sogo/commit/4abf12b4bb27cfafdb3cc725f677e8f66fe5709e))
|
||||
* **sieve:** sieve availibility was checked even if we disable it ([bba1da1](https://github.com/Alinto/sogo/commit/bba1da1b2b8d4babef7ab1a523741877cf58d61f))
|
||||
* **translation:** french syntax ([8604389](https://github.com/Alinto/sogo/commit/860438915212a06f983da8c283d386a48745d049))
|
||||
* **ui:** Disable send button while mail is sending. Fixes [#5825](https://bugs.sogo.nu/view.php?id=5825). ([a484407](https://github.com/Alinto/sogo/commit/a48440754cb9e4ea1bc13c5bc68c05bd93f89d51))
|
||||
* **user:** Replace length with count for NSArray ([ec27f7e](https://github.com/Alinto/sogo/commit/ec27f7e7141c8ddae3450b36344415e98ae8f794))
|
||||
|
||||
|
||||
### Localization
|
||||
|
||||
* **ar:** Update Arabic translations ([132912b](https://github.com/Alinto/sogo/commit/132912b56610141d6bcd6a5ce9170837796b5991))
|
||||
* **bg_BG:** Update Bulgarian translations ([6606f98](https://github.com/Alinto/sogo/commit/6606f9865a619cecf8f37e0c4c39ca8fa0aa31cc))
|
||||
* **bs_BA:** Update Bosnian translations ([fc93272](https://github.com/Alinto/sogo/commit/fc93272d70661db3eafcf2c089d2f8cae65bd799))
|
||||
* **ca:** Update Catalan translations ([c82a48f](https://github.com/Alinto/sogo/commit/c82a48f96f3f114d3ac771f13fe0687bb24308f1))
|
||||
* **cs:** Update Czech translations ([1a5408c](https://github.com/Alinto/sogo/commit/1a5408c2b323f46d03f2078ac17895493b0cd986))
|
||||
* **cy:** Update French translations ([487ad1a](https://github.com/Alinto/sogo/commit/487ad1a849290a21807e2bca55d884116b9991bf))
|
||||
* **cy:** Update Welsh translations ([0e2deac](https://github.com/Alinto/sogo/commit/0e2deac164b16b6bd105bd9d629275ae1147a902))
|
||||
* **da_DK:** Update Danish translations ([9ef58e7](https://github.com/Alinto/sogo/commit/9ef58e7c6d143e2ac7d13719ec2e2be281de377f))
|
||||
* **de:** Update German translations ([0f0ecf7](https://github.com/Alinto/sogo/commit/0f0ecf71c30ce7b5742ce7e6b6e9cb55b1b85a84))
|
||||
* **es_AR:** Update SpanishArgentina translations ([64865aa](https://github.com/Alinto/sogo/commit/64865aa85a715095788452b1132bafe8f8fea018))
|
||||
* **es_ES:** Update SpanishSpain translations ([ea8f04a](https://github.com/Alinto/sogo/commit/ea8f04ac7c5baf6c70bf4741298cb7e0b7147750))
|
||||
* **eu:** Update Basque translations ([6293f62](https://github.com/Alinto/sogo/commit/6293f62d83bb4e855ec199cf92dce8e8f9086134))
|
||||
* **fi:** Update Finnish translations ([7ff0699](https://github.com/Alinto/sogo/commit/7ff0699648a9531270f2c6299bced91d2d8256b5))
|
||||
* **gl:** Update Galician translations ([86e5872](https://github.com/Alinto/sogo/commit/86e5872dc1303cfc5302ef728be40df73c0894d2))
|
||||
* **he:** Update Hebrew translations ([41842e2](https://github.com/Alinto/sogo/commit/41842e2d3956fc6c34e74c9b79294e11233aa70f))
|
||||
* **hr_HR:** Update Croatian translations ([b1df5d4](https://github.com/Alinto/sogo/commit/b1df5d4d142da783e0dc87bc509bba6e71ac1b88))
|
||||
* **hu:** Update Hungarian translations ([e3d392b](https://github.com/Alinto/sogo/commit/e3d392b12ab4d3ff03cadba52f0848e1063d1573))
|
||||
* **id_ID:** Update Indonesian translations ([e50b66b](https://github.com/Alinto/sogo/commit/e50b66b62e494564306b58157112e2c71a75c5cf))
|
||||
* **is:** Update Icelandic translations ([ee3ed07](https://github.com/Alinto/sogo/commit/ee3ed0779cc29121d04267cab44998a4296deed6))
|
||||
* **it:** Update Italian translations ([2fee2f8](https://github.com/Alinto/sogo/commit/2fee2f8782e0db1c7ba5fa0d3305149b7ce25506))
|
||||
* **ja:** Update Japanese translations ([97d01fb](https://github.com/Alinto/sogo/commit/97d01fb14e45b6e2f4cda977de462ab5fa5a782f))
|
||||
* **kk:** Update Kazakh translations ([ed5199a](https://github.com/Alinto/sogo/commit/ed5199aee71d53ccf7cd1dec7e08fb33ab3ac31b))
|
||||
* **lt:** Update Lithuanian translations ([4974648](https://github.com/Alinto/sogo/commit/4974648c3323e853ca0ac440105020fb8a215805))
|
||||
* **lv:** Update Latvian translations ([493f5f6](https://github.com/Alinto/sogo/commit/493f5f625fa87bab69b1d6b13b6ed9d0278b00f2))
|
||||
* **mk_MK:** Update Macedonian translations ([bbb0955](https://github.com/Alinto/sogo/commit/bbb09555f0c07e2ea57ca12cd1ec8a218c475e59))
|
||||
* **nb_NO:** Update NorwegianBokmal translations ([0368bad](https://github.com/Alinto/sogo/commit/0368bad3c5bd90107703f469b7605fa0c1a1c277))
|
||||
* **nl:** Update Dutch translations ([03d4307](https://github.com/Alinto/sogo/commit/03d43079d3934a6ef20098df425e4c15d30aca6f))
|
||||
* **nn_NO:** Update NorwegianNynorsk translations ([02d32d5](https://github.com/Alinto/sogo/commit/02d32d5f6ced2c2411b549e198cda59352942142))
|
||||
* **pl:** Update Polish translations ([92d38c1](https://github.com/Alinto/sogo/commit/92d38c108e627520a5143a8059ff013a4a0d6080))
|
||||
* **pt_BR:** Update BrazilianPortuguese translations ([ce6efaf](https://github.com/Alinto/sogo/commit/ce6efafe54673f55ae02e348c7844f7258f5cb3a))
|
||||
* **pt:** Update Portuguese translations ([46d5779](https://github.com/Alinto/sogo/commit/46d5779088c4ceb34578c3233fea34cf8885d858))
|
||||
* **ro_RO:** Update Romanian translations ([9aebf9b](https://github.com/Alinto/sogo/commit/9aebf9bf022b6ad49b14e5980ef4b8d22ff4f59d))
|
||||
* **ru:** Update Russian translations ([da609c4](https://github.com/Alinto/sogo/commit/da609c448e57168b6e691b5c756eb06c49e4c50c))
|
||||
* **sk:** Update Slovak translations ([8a10213](https://github.com/Alinto/sogo/commit/8a10213b751fc04a3d3f08b96eb58f87dc794663))
|
||||
* **sl_SI:** Update Slovenian translations ([b08b5dd](https://github.com/Alinto/sogo/commit/b08b5dd79a68eadd7e5482d55bf0ace54d96d3b1))
|
||||
* **sr_ME@latin:** Update Montenegrin translations ([865aba3](https://github.com/Alinto/sogo/commit/865aba32486c3bacdd6ca78e32ef4cf37a123219))
|
||||
* **sr@latin:** Update SerbianLatin translations ([1710112](https://github.com/Alinto/sogo/commit/1710112c4e7d7997d277fbb22413d28cec4388a0))
|
||||
* **sr:** Update Serbian translations ([2242c21](https://github.com/Alinto/sogo/commit/2242c213238c88318fbda0a7fc2ffccea3d42c32))
|
||||
* **sv:** Update Swedish translations ([d5c1b74](https://github.com/Alinto/sogo/commit/d5c1b742f45130cd3a9efd6bbacb8ce53a1db7ca))
|
||||
* **tr_TR:** Update TurkishTurkey translations ([dfc1ab7](https://github.com/Alinto/sogo/commit/dfc1ab7e0ecf7d304d62a5a5a1bfd9c583ca070d))
|
||||
* **uk:** Update Ukrainian translations ([5ee12a4](https://github.com/Alinto/sogo/commit/5ee12a45074f679aa90c8596d0e95d7e6cdb345d))
|
||||
* **zh_CN:** Update ChineseChina translations ([300cfe4](https://github.com/Alinto/sogo/commit/300cfe4e27e60b0b6f9ba80ff1ce9dc88a58efdb))
|
||||
* **zh_TW:** Update ChineseTaiwan translations ([e330c55](https://github.com/Alinto/sogo/commit/e330c552c3917cf662fe4b124686d504904e4e13))
|
||||
|
||||
### [5.8.4](https://github.com/Alinto/sogo/compare/SOGo-5.8.3...SOGo-5.8.4) (2023-06-20)
|
||||
|
||||
|
||||
@@ -21,7 +21,7 @@ devices with SOGo.
|
||||
The instructions are based on version {release_version} of SOGo.
|
||||
|
||||
The latest version of this guide is available
|
||||
at https://www.sogo.nu/support.html#/documentation.
|
||||
at https://sogo.nu/downloads/documentation.html.
|
||||
|
||||
Introduction
|
||||
------------
|
||||
@@ -164,7 +164,7 @@ The following operating systems are currently supported by SOGo:
|
||||
|
||||
* Red Hat Enterprise Linux (RHEL) Server 7, 8 and 9
|
||||
* Community ENTerprise Operating System (CentOS) 7, 8 and 9
|
||||
* Debian GNU/Linux 7.0 (Wheezy), 8.0 (Jessie), 9.0 (Stretch), 10 (Buster), 11 (Bullseye) and 12 (Bookworm)
|
||||
* Debian GNU/Linux 7.0 (Wheezy), 8.0 (Jessie), 9.0 (Stretch) and 10.0 (Buster)
|
||||
* Ubuntu 14.04 (Trusty), 16.04 (Xenial), 18.04 (Bionic), 20.04 (Focal) and 22.04 (Jammy)
|
||||
|
||||
Make sure the required components are started automatically at boot time
|
||||
@@ -693,13 +693,6 @@ Defaults to `NO` when unset.
|
||||
|S |SOGoDisableSharing
|
||||
|List of modules where sharing should be disabled, for example `(Mail, Calendar)`. Modules can be `Mail`, `Contacts` and `Calendar`. Default value empty list (sharing enabled for everybody).
|
||||
|
||||
|S |SOGoDisableSharingAnyAuthUser
|
||||
|List of modules where sharing with any authenticated user option should be disabled, for example `(Mail, Calendar)`. Modules can be `Mail`, `Contacts` and `Calendar`. Default value empty list (sharing enabled for everybody).
|
||||
|
||||
|S |SOGoDisableExport
|
||||
|List of modules where export should be disabled, for example `(Mail, Calendar)`. Modules can be `Mail`, `Contacts` and `Calendar`. Default value empty list (export enabled for everybody).
|
||||
|
||||
|
||||
|S |SOGoPasswordChangeEnabled
|
||||
|Parameter used to allow or not users to change their passwords from
|
||||
SOGo.
|
||||
@@ -826,17 +819,7 @@ specified as an array of dictionaries.
|
||||
|D |SOGoCreateIdentitiesDisabled
|
||||
|Disable identity creation for users in preferences. If `YES`, users won't be able to add new identities and will allow to change only full name, signature and default identity. Default value is `NO`. Note : If this settings is set to `YES`, it will not be possible to crete auxiliary mail accounts.
|
||||
|
||||
|S |SOGoURLEncryptionEnabled
|
||||
|Enable URL encryption to make SOGo GDPR compatible. Setting this parameter to `YES` will encrypt username in URL. The encryption data are cached to avoid high cpu usage, ig the value of thie parameter is changed, memcached server needs to be restarted. If the encryption is enabled, the DAV url will change. Default value is `NO`.
|
||||
|
||||
|S |SOGoURLEncryptionPassphrase
|
||||
|Passphrase for `SOGoURLEncryptionEnabled`. The string must be 128 bits (16 characters). If this settings change, the cache server must be restarted, and the DAV url will change. Default value is `SOGoSuperSecret0`.
|
||||
|
||||
|S |SOGoGlobalAddressBookFirstEntries
|
||||
|Display first entries in Global Address Book. Default value is `NO`. If source is LDAP, the LDAP overlay `sssvlv` must be enabled on the system for server side sorting.
|
||||
|
||||
|S |SOGoGlobalAddressBookFirstEntriesCount
|
||||
|Number of entries displayed when `SOGoGlobalAddressBookFirstEntries` is enabled. Default value is `100`.
|
||||
|
||||
|=======================================================================
|
||||
|
||||
@@ -2290,27 +2273,18 @@ Defaults to `NO` when unset.
|
||||
|D |SOGoForwardConstraints
|
||||
|Parameter used to set constraints on possible addresses used when
|
||||
automatically forwarding mails. When set to `0` (default), no constraint
|
||||
is enforced. When set to `1`, only internal domains can be used.
|
||||
When set to `2`, only external domains defined in `SOGoForwardConstraintsDomains`
|
||||
can be used. When set to `3`, internal domains and other domains defined
|
||||
in `SOGoForwardConstraintsDomains` can be used.
|
||||
is enforced. When set to `1`, only internal domains can be used. When
|
||||
set to `2`, only external domains can be used.
|
||||
|
||||
|D |SOGoForwardConstraintsDomains
|
||||
|Parameter used to set which external domains are allowed
|
||||
when SOGoForwardConstraints is set to `2` or `3`. For example, setting:
|
||||
|Parameter used to set which domains are allowed as external domains
|
||||
when SOGoForwardConstraints is set to `2`. For example, setting:
|
||||
|
||||
SOGoForwardConstraintsDomains = ("gmail.com", "googlemail.com");
|
||||
|
||||
will allow users to forward emails to only `gmail.com` and `googlemail.com` domains.
|
||||
When empty or undefined, no constraints are imposed.
|
||||
|
||||
|D |SOGoNotificationEnabled
|
||||
|Parameter used to activate the edition from the preferences window of
|
||||
notifications for emails. Requires Sieve script support on the IMAP
|
||||
host.
|
||||
|
||||
Defaults to `NO` when unset.
|
||||
|
||||
|D |SOGoSieveScriptsEnabled
|
||||
|Parameter used to activate the edition from the preferences windows of
|
||||
server-side mail filters. Requires Sieve script support on the IMAP
|
||||
@@ -2500,13 +2474,6 @@ Defaults to `NO` when unset.
|
||||
|
||||
Defaults to `inline` when unset.
|
||||
|
||||
|U |SOGoMailDisplayFullEmail
|
||||
|Show recipients or sender full email in mailboxes if set to `YES`. Default value is `NO`.
|
||||
|
||||
|U |SOGoMailHideInlineAttachments
|
||||
|Hide inline message as attachements if set to `YES`. Default value is `NO`.
|
||||
|
||||
|
||||
|U |SOGoMailCustomFullName
|
||||
|The string to use as full name when composing an email, if
|
||||
_SOGoMailCustomFromEnabled_ is set in the user's domain defaults.
|
||||
@@ -3232,7 +3199,7 @@ your site. However, be aware that in order to use the SOGo Integrator
|
||||
plug in, you will need to repackage it with specific modifications.
|
||||
Please refer to the documentation published online:
|
||||
|
||||
https://www.sogo.nu/support.html#/documentation
|
||||
https://sogo.nu/downloads/documentation.html
|
||||
|
||||
If you only use the SOGo Connector plug in, you can still easily access
|
||||
your data.
|
||||
@@ -3269,12 +3236,6 @@ http://127.0.0.1/SOGo/dav/jdoe/
|
||||
|
||||
Note that the trailing slash is important for the old Apple iCal 3 application.
|
||||
|
||||
Since SOGo 5.9.1, calendar can be installed using provisioning profiles. On the calendar interface next to the calendar name (eg `Personal`), click on the three dots button and click on `Download iOS - MacOS configuration profile`.
|
||||
You have to add the profile to your device :
|
||||
|
||||
* Go to Settings / Profiles / Click on + / Continue
|
||||
* Type your SOGo account password and click on install
|
||||
|
||||
Apple AddressBook
|
||||
~~~~~~~~~~~~~~~~~
|
||||
|
||||
@@ -3318,12 +3279,6 @@ The virtual host should be defined like:
|
||||
This configuration is also required if you want to configure a CardDAV
|
||||
account on an Apple iOS device (version 4.0 and later).
|
||||
|
||||
Since SOGo 5.9.1, address book can be installed using provisioning profiles. On the address book interface next to the address book name (eg `Personal`), click on the three dots button and click on `Download iOS - MacOS configuration profile`.
|
||||
You have to add the profile to your device :
|
||||
|
||||
* Go to Settings / Profiles / Click on + / Continue
|
||||
* Type your SOGo account password and click on install
|
||||
|
||||
Microsoft ActiveSync
|
||||
~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
@@ -3334,488 +3289,6 @@ any mobile devices that support Microsoft ActiveSync. Microsoft Outlook
|
||||
The Microsoft ActiveSync server URL is generally something
|
||||
like: `http://127.0.0.1/Microsoft-Server-ActiveSync`.
|
||||
|
||||
Using sogo-tool
|
||||
---------------
|
||||
The command _sogo-tool_ allows to do some operations on database and sieve filter. It is included with
|
||||
the sogo package on Debian/Ubuntu but must be installed manually on RHEl/CentOS:
|
||||
|
||||
yum install sogo-tool
|
||||
|
||||
*_WARNING_: Use sogo-tool with full awareness of what you are doing. This is an admin tool that can cause loss of data
|
||||
or completely make the webmail unusable by a user.*
|
||||
|
||||
sogo-tool backup/restore
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
The backup tool saves the information of a user into a file. The information saved are its preferences, its events and its contacts.
|
||||
|
||||
sogo-tool backup directory ALL|user1 [user2] ...
|
||||
|
||||
* First argument, *directory* must be a path, only the last subdirectory will be created, the previous ones must exist.
|
||||
* Second argument is *ALL* to backup all users or put each user space-separated.
|
||||
* Each user's info will take one file, the filename will be the username.
|
||||
* The files are readable but in a specific format for _sogo-tool restore_.
|
||||
|
||||
Example:
|
||||
----
|
||||
sogo-tool backup /tmp/foo ALL # Will save all users into /tmp/foo
|
||||
sogo-tool backup /etc/sogo_backup/foo user1 user2 # Will save only user1 and user2. The directory /etc/sogo_backup must exist.
|
||||
----
|
||||
|
||||
Using the files produced by the backup, you can restore all or some information of a user with the _restore_ command:
|
||||
|
||||
sogo-tool restore [-l|-p|-f/-F folder/ALL] [-c credentialFile] directory user
|
||||
|
||||
* First argument must be one of the four mode options *-l*, *-p*, *-f* or *-F*.
|
||||
* Second, optional, is *-c* with the credential file. Only useful with *-p* mode.
|
||||
* *directory* is the path where the backup file is.
|
||||
* *user* is the name of the backup file, which is the username if you use the _backup_ tool.
|
||||
|
||||
The differents mode are:
|
||||
|
||||
* *-l* will only list the folders (meaning calendar and address book) that would be restored with *-f* or *-F*.
|
||||
|
||||
Example:
|
||||
----
|
||||
sogo-tool restore -l /tmp/backup user1
|
||||
----
|
||||
Result:
|
||||
----
|
||||
Calendar/60C8-65323D80-7-4D9F7D80 (new_calendar)
|
||||
Calendar/personal (Personal Calendar)
|
||||
Contacts/personal (Personal Address Book)
|
||||
----
|
||||
|
||||
* *-p* will restore the user's preferences. If the user has an active sieve script (filter, vacation, forward...) you must provide a credential file with
|
||||
the parameters *-c*. The credential file is a simple one-line file that contains the "username:password" of an admin account of your imap/sieve server.
|
||||
|
||||
Example:
|
||||
----
|
||||
sogo-tool restore -p /tmp/backup user1
|
||||
sogo-tool restore -p -c /var/sogo/cred /tmp/backup user1
|
||||
----
|
||||
|
||||
* *-f* will restore the events/contacts of folders (calendar and address book) from the backup file that don't exist in the database. If the event/contact
|
||||
was deleted but is in the backup file, it will be restored. If the event/contact exist, nothing will be done even if it has been modified compared to the
|
||||
backup file. If a whole folder has been deleted but is in the backup file, it will be restored. *-f* expect a value that can be ALL to restore all folders,
|
||||
or the name of the folder to restore. You can list them with the *-l* mode.
|
||||
|
||||
Example:
|
||||
----
|
||||
sogo-tool restore -f ALL /tmp/backup user1
|
||||
sogo-tool restore -f "Contacts/personal" tmp/backup user1
|
||||
sogo-tool restore -f "Calendar/60C8-65323D80-7-4D9F7D80" tmp/backup user1
|
||||
----
|
||||
The commands will either prints out nothing or any events/contacts restored:
|
||||
----
|
||||
restoring record '60CA-65323D00-1-680B0A00.ics'
|
||||
restoring record '60C8-65323D00-1-4D9F7D80.vcf'
|
||||
restoring record '60C8-65323D80-9-4D9F7D80.ics'
|
||||
----
|
||||
|
||||
* *-F* has the same behavior as *-f* but it will first delete all contacts and events in database before restoring the backup.
|
||||
So if an event/contact is not in the backup file, it will be lost. If a folder exists but is not in the backup file, nothing will happen
|
||||
to it and its content.
|
||||
|
||||
*-F* expect a value that can be ALL to restore all folders, or the name of the folder to restore. You can list them with the *-l* mode.
|
||||
|
||||
Example:
|
||||
----
|
||||
sogo-tool restore -F ALL /tmp/backup user1
|
||||
sogo-tool restore -F "Contacts/personal" tmp/backup user1
|
||||
sogo-tool restore -F "Calendar/60C8-65323D80-7-4D9F7D80" tmp/backup user1
|
||||
----
|
||||
|
||||
sogo-tool checkup
|
||||
~~~~~~~~~~~~~~~~~
|
||||
|
||||
Check the events and contacts data's integrity of a user
|
||||
|
||||
sogo-tool checkup [-d] user...
|
||||
|
||||
* *-d*, optional, will delete all corrupted data.
|
||||
* *user* is a single username or multiple user each space-separated.
|
||||
|
||||
Will print out nothing if no corrupted data were found or one of the following messages:
|
||||
|
||||
* Corrupted calendar item (missing tags) in path <path> with c_name = <ics_id>
|
||||
* Corrupted calendar item (unparsable) in path <path> with c_name = <ics_id>
|
||||
* Missing start date of event in path <path> with c_name = <ics_id> (<event_name>)"
|
||||
* Missing end date of event in path <path> with c_name = <ics_id> (<event_name>)"
|
||||
* Start date (<start_date>) is not before end date (<end_date>) for event in path <path> with c_name = <ics_id> (<event_name>)
|
||||
* Corrupted card item (unparsable) in path <path> with c_name = <vcard_id>
|
||||
|
||||
It can also print log from SOGo if it raise errors:
|
||||
|
||||
* <0x0x55e0956c4d00[VSCardSaxDriver]> serious inconsistency among begin/end tags
|
||||
|
||||
Example:
|
||||
----
|
||||
sogo-tool checkup user1
|
||||
sogo-tool checkup -d user1
|
||||
----
|
||||
|
||||
sogo-tool cleanup
|
||||
~~~~~~~~~~~~~~~~~
|
||||
|
||||
Will purge all user's deleted events and contacts which the deletion is older than a number of days.
|
||||
|
||||
sogo-tool cleanup [days] ALL|user1 [user2]
|
||||
|
||||
* *days* the age of deleted records to purge in days
|
||||
* Second argument is *ALL* to purge events of all users or put each user space-separated.
|
||||
|
||||
Example:
|
||||
----
|
||||
sogo-tool cleanup 30 ALL
|
||||
sogo-tool cleanup 30 user1 user2
|
||||
----
|
||||
Outputs:
|
||||
----
|
||||
Purged 3 records from folder /Users/user1/Calendar/60C7-65325300-9-18F39AA0
|
||||
Purged 1 records from folder /Users/user1/Calendar/60C8-65323D80-7-4D9F7D80
|
||||
Purged 0 records from folder /Users/user1/Calendar/personal
|
||||
Purged 0 records from folder /Users/user1/Contacts/personal
|
||||
Purged 5 records from folder /Users/user2/Calendar/personal
|
||||
----
|
||||
|
||||
|
||||
sogo-tool create-folder
|
||||
~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
Create a folder (Calendar or Address Book) for a user.
|
||||
|
||||
create-folder user type [displayname ...]
|
||||
|
||||
* *user* is the name of the user
|
||||
* *type* is either "Calendar" or "Contacts"
|
||||
* *displayname* is the folder's name. If the name is already taken, a new folder will still be made but with another uid.
|
||||
|
||||
Example:
|
||||
----
|
||||
sogo-tool create-folder user1 Contacts Pro_Contacts Ext_Contacts
|
||||
sogo-tool create-folder user2 Calendar Pro_Calendar
|
||||
----
|
||||
|
||||
|
||||
sogo-tool dump-defaults
|
||||
~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
Output all current defaults value of GNUstep and SOGo (sogo.conf)
|
||||
|
||||
sogo-tool dump-defaults [-f <filename>|[all]]
|
||||
|
||||
* Can be used without arguments and will output the defaults for gnustep domain 'sogod'.
|
||||
* *all* option will output all defaults value found from sogo and gnustep in property list format.
|
||||
* *-f* expect a filepath of an .xml file and will output it in property list format (sogo.conf). May need some tweaks, though.
|
||||
|
||||
Example:
|
||||
----
|
||||
sogo-tool dump-defaults
|
||||
sogo-tool dump-defaults all
|
||||
sogo-tool dump-defaults -f /tmp/foo/conf.xml
|
||||
----
|
||||
|
||||
sogo-tool expire-sessions
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
Expires user sessions from database without activity for specified number of minutes. When a user log in to sogo for
|
||||
the first time, sogo will create a entry in database's table OCSSessionsFolderURL with the user's information. Sogo will
|
||||
also set a cookie with an encoded value to fetch this database table. That way, each time the user make a request with that cookie,
|
||||
sogo will know which connected user it is. This command will remove all user's session from database
|
||||
without activity for specified number of minutes. Those users will have to log in again.
|
||||
|
||||
sogo-tool expire-sessions [nbMinutes]
|
||||
|
||||
* *nbMinutes* Integer, number of minutes. All session without activity in these last minutes will be removed.
|
||||
|
||||
Example:
|
||||
----
|
||||
sogo-tool expire-sessions #Will print usage.
|
||||
sogo-tool expire-sessions 160 #Will remove session which last activity is older than 160 minutes.
|
||||
sogo-tool expire-sessions 0 #Will remove session which last activity is older than 0 minutes.
|
||||
----
|
||||
|
||||
sogo-tool manage-acl
|
||||
~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
Manage access-control list (ACL) of a user for folders (Calendar and Address Book).
|
||||
|
||||
sogo-tool manage-acl get|add|remove|subscribe|unsubscribe owner folder user|group <rights>
|
||||
|
||||
* First argument is the action among *get*, *add*, *remove*, *subscribe* and *unsubscribe*.
|
||||
* Second argument is the username of the folder's owner.
|
||||
* Third argument is the name of the folder.
|
||||
* Fourth argument is the user whom to manage its acl. It also can be _ALL_, _anonymous_ and _<defaults>_.
|
||||
* Fifth argument, only needed with *add* action, is the rights to set for the user.
|
||||
|
||||
The actions:
|
||||
|
||||
* *get* will print out the folder's rights of the user or nothing if the user is not found or doesn't have any rights.
|
||||
|
||||
Example:
|
||||
----
|
||||
sogo-tool manage-acl get sogo-tests1 Calendar/5E1F-653FC400-1-38155940 sogo-tests2
|
||||
----
|
||||
Result:
|
||||
----
|
||||
Rights for sogo-tests2 ["PublicModifier", "ConfidentialModifier", "PrivateModifier", "ObjectCreator", "ObjectEraser"]
|
||||
----
|
||||
|
||||
* *add* will add the given rights to the user for the given folder. the value right is a json string of an array with each right to set.
|
||||
Be Careful as there is no check for the rights already set. It could be better to first remove them, then add them.
|
||||
|
||||
The rights for Address Book are:
|
||||
|
||||
* _ObjectCreator_: can create cards (contact of list)
|
||||
* _ObjectEditor_: can modify cards
|
||||
* _ObjectViewer_: can view cards
|
||||
* _ObjectEraser_: can delete cards
|
||||
|
||||
The rights for Calendar are:
|
||||
|
||||
* _ObjectCreator_: can create event or task
|
||||
* _ObjectEraser_: can delete event or task
|
||||
* _<type>_ can be _Public_, _Confidential_ or _Private_
|
||||
* _<type>Viewer_: can view the whole events (ex: PrivateViewer)
|
||||
* _<type>DAndTViewer_: can only view the date and time of events
|
||||
* _<type>Modifier_: can view and edit the events
|
||||
* _<type>Responder_: can view the events
|
||||
|
||||
Example:
|
||||
----
|
||||
sogo-tool manage-acl add sogo-tests1 Contacts/5E1D-653FC400-1-1A330C40 sogo-tests2 '["ObjectViewer", "ObjectEraser"]'
|
||||
----
|
||||
|
||||
* *remove* all user's rights to the folder.
|
||||
|
||||
Example:
|
||||
----
|
||||
sogo-tool manage-acl remove sogo-tests1 Contacts/5E1D-653FC400-1-1A330C40 sogo-tests2
|
||||
----
|
||||
|
||||
* *subscribe* the user to the folder.
|
||||
|
||||
Example:
|
||||
----
|
||||
sogo-tool manage-acl subscribe sogo-tests1 Contacts/5E1D-653FC400-1-1A330C40 sogo-tests2
|
||||
----
|
||||
|
||||
* *unsubscribe* the user to the folder.
|
||||
|
||||
Example:
|
||||
----
|
||||
sogo-tool manage-acl unsubscribe sogo-tests1 Contacts/5E1D-653FC400-1-1A330C40 sogo-tests2
|
||||
----
|
||||
|
||||
sogo-tool manage-eas
|
||||
~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
Manage EAS folders
|
||||
|
||||
sogo-tool manage-eas listdevices|listfolders|resetdevice|resetfolder|mergevcard|mergevevent user <deviceId | folderId> <YES | NO>
|
||||
|
||||
* First argument is the action among *listdevices*, *listfolders*, *resetdevice*, *resetfolder*, *mergevcard* and *mergevevent*.
|
||||
* Second argument is the *user* whom to perform the action.
|
||||
* Third argument is the *deviceId* of, if the action is *resetFolder*, the *folderId*. No need of this argument if the action is *listdevices*
|
||||
* Fourth argument, only use with *mergevcard* and *mergevevent* is either YES to merge and *NO* to un-merge.
|
||||
|
||||
The actions:
|
||||
|
||||
* *listdevices*: list the devices belonging to user.
|
||||
* *listfolders*: list all folders of deviceId for user.
|
||||
* *resetdevice*: force deviceId of user to resync everything.
|
||||
* *resetfolder*: force folderId of user to resync everything.
|
||||
* *mergevcard*: merge/un-merge all addressbooks into one for deviceId of user.
|
||||
* *mergevevent*: merge/un-merge all calendars into one for deviceId of user.
|
||||
|
||||
Examples:
|
||||
----
|
||||
sogo-tool manage-eas listdevices janedoe
|
||||
sogo-tool manage-eas listfolders janedoe androidc316986417
|
||||
sogo-tool manage-eas resetdevice janedoe androidc316986417
|
||||
sogo-tool manage-eas resetfolder janedow androidc316986417 folderlala-dada-sasa_7a13_1a2386e0_e
|
||||
sogo-tool manage-eas mergevcard janedow androidc316986417 YES
|
||||
sogo-tool manage-eas mergevevent janedow androidc316986417 YES
|
||||
----
|
||||
|
||||
sogo-tool remove
|
||||
~~~~~~~~~~~~~~~~
|
||||
|
||||
Remove all folders (Calendar and Address Book) and Preference settings of a user. The personal Calendar and Address Book
|
||||
will stay but be emptied of all entries. The Preferences will go back to defaults values as for a new user.
|
||||
|
||||
sogo-tool remove user1 [user2]
|
||||
|
||||
* Arguments one user or several space-separated.
|
||||
* You can add -v after sogo*tool to list all removed folders
|
||||
|
||||
Example:
|
||||
----
|
||||
sogo-tool remove user1
|
||||
sogo-tool -v remove user2
|
||||
----
|
||||
Output example with -v options:
|
||||
----
|
||||
Deleting /Users/user2/Calendar/B088-65363400-3-2B9DF0C0
|
||||
Deleting /Users/user2/Calendar/personal
|
||||
Deleting /Users/user2/Contacts/B087-65363380-5-374DA380
|
||||
Deleting /Users/user2/Contacts/B087-65363400-7-374DA380
|
||||
Deleting /Users/user2/Contacts/personal
|
||||
----
|
||||
|
||||
sogo-tool remove-doubles
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
remove duplicate contacts from the specified user addressbook
|
||||
|
||||
sogo-tool remove-doubles USER FOLDER
|
||||
|
||||
* *USER* is the name of the user to perform the removal
|
||||
* *FOLDER* is the name of the folder to clean from duplicates
|
||||
|
||||
Two contacts are considered duplicates when they have the same mails or display name. When two or more contacts
|
||||
are duplicates, each one will get a score. The ones with the highest score will be kept and the others
|
||||
will be discarded. If two records have the same score, the first one to have reach it will be kept.
|
||||
The scores are distributed as such:
|
||||
* Record which has been the last modified: +1
|
||||
* Record has the most content: +2
|
||||
* Record has the most quick field set: +3
|
||||
* Record is in a contact list: +6
|
||||
|
||||
Example:
|
||||
----
|
||||
sogo-tool remove-doubles user1 personal
|
||||
sogo-tool remove-doubles user2 489-65376E80-1-2D2BA000
|
||||
----
|
||||
|
||||
sogo-tool rename-user
|
||||
~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
Update records pertaining to a user after a change of user id. Will change all folders path, subscriptions from others users,
|
||||
all mention of user id in database.
|
||||
|
||||
sogo-tool rename-user fromuserid touserid
|
||||
|
||||
* *fromuserid* Previous user id
|
||||
* *touserid* New user id
|
||||
|
||||
If the new user id already exist, the command will output a message and do nothing.
|
||||
**WARNING** This command only change database of sogo, if you use ldap or others databases they will still keep the old id.
|
||||
|
||||
Example:
|
||||
----
|
||||
sogo-tool rename-user old_username new_username
|
||||
----
|
||||
|
||||
sogo-tool truncate-calendar
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
Remove old calendar entries from the specified user calendar.
|
||||
|
||||
sogo-tool truncate-calendar [-r] USER FOLDER DATE
|
||||
|
||||
* *-r* optional, to also delete recurrent events that have occurrence after *DATE*
|
||||
* *USER* is the name of the owner of the calendar
|
||||
* *FOLDER* is the name of the calendar folder to clean up
|
||||
* *DATE* UTC datetime, event older than this date will be removed
|
||||
|
||||
Without *-r*, only reccurrent events which all occurrences are older than *DATE* will be removed.
|
||||
With *-r*, event the if there are occurrences after *DATE*, the recurrent event will be removed.
|
||||
*DATE* can take the value `date +%FT%T` which is the current time
|
||||
|
||||
Example:
|
||||
----
|
||||
sogo-tool truncate-calendar sogo-tests1 personal 2023-10-25T00:00:00
|
||||
sogo-tool truncate-calendar -r sogo-tests2 5E38-6538BE80-5-AFF8310 2022-01-01T15:30:00
|
||||
sogo-tool truncate-calendar -r sogo-tests2 personal `date +%FT%T` #will delete all event older than now
|
||||
----
|
||||
Result:
|
||||
----
|
||||
No record to remove. All records kept.
|
||||
----
|
||||
----
|
||||
Removing 1 records...
|
||||
Removed 1 records.
|
||||
----
|
||||
|
||||
sogo-tool update-autoreply
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
_This command is only useful if your sieve server doesn't have the capabilities *date* or *relational*_
|
||||
In that case this command will check vacation's setting of all the users. If vacation is enabled/disabled,
|
||||
the starting and ending date, the body and others parameters. If needed, the command
|
||||
will write the sieve script accordingly.
|
||||
|
||||
sogo-tool update-autoreply -p credentialFile
|
||||
|
||||
* *-p- The credential file, a simple one-line file that contains "username:password" of an admin account of your imap/sieve server.
|
||||
|
||||
Example:
|
||||
----
|
||||
sogo-tool update-autoreply -p /var/sogo/cred
|
||||
----
|
||||
|
||||
sogo-tool user-preferences
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
Get, set or unset user defaults / settings in the database.
|
||||
|
||||
_Settings_ stores parameters for user's calendar, mail and contacts. It can be graphic
|
||||
as month view or week view for calendar but it also stores subscription, delegation...
|
||||
It also stores the private Salt for TOTP and the parameter ForceResetPassword to force the
|
||||
user to change password.
|
||||
|
||||
_Defaults_ stores all parameters that can be found in Preferences panel. So it goes from default language to
|
||||
autoreply to many others options.
|
||||
|
||||
|
||||
user-preferences get|set|unset defaults|settings user key [value|-f filename] [-p credentialFile]
|
||||
|
||||
* First argument is the action *get*, *set* or *unset*
|
||||
* Second argument is parameter type *settings* or *defaults*
|
||||
* Third argument *user* is the name of the user where to make the action
|
||||
* Fourth argument *key* is the name of the parameters where to make the action
|
||||
* If the action is *set*, you should provide either *value* as fifth argument or a file with the value inside with *-f* filename/path
|
||||
* If the action is *set* or *unset* and the *key* concerns a sieve script (filter, vacation, forward...), you
|
||||
should provide a credential file, a one-line file that contains "username:password" of an admin account of your imap/sieve server.
|
||||
|
||||
As there is a lot of parameters, this documentation will not go into details for each one. To know what are
|
||||
the key names and their value you should got to your database in table defined by _SOGoProfileURL_ in your sogo.conf.
|
||||
Here, for each row (user) you will find c_defaults and c_settings which are json with the keys. However, if
|
||||
a parameter has never been set, it won't appears in those json. The clean way to know the missing keys is to
|
||||
set it up in one of your dummy/dev/test account then see the values in _SOGoProfileURL_. Be careful, a value
|
||||
can be a json itself, only the primary key can be get/set/unset.
|
||||
|
||||
Example with forwarding, *get* action:
|
||||
----
|
||||
sogo-tool user-preferences get defaults user1 Forward
|
||||
----
|
||||
If forward has never been set the result will be:
|
||||
----
|
||||
Value for key "Forward" not found in defaults
|
||||
----
|
||||
Else it will be
|
||||
----
|
||||
{"forwardAddress":["sogo-tests2@sogo.alinto"],"enabled":1,"keepCopy":1}
|
||||
----
|
||||
|
||||
*unset* action:
|
||||
----
|
||||
sogo-tool user-preferences unset defaults user1 Forward -p /etc/sogo/cred
|
||||
----
|
||||
|
||||
*set* action:
|
||||
----
|
||||
sogo-tool user-preferences set defaults user1 Forward '{"forwardAddress":["sogo-tests2@sogo.alinto"],"enabled":1,"keepCopy":0}' -p cred
|
||||
----
|
||||
or create a json file which contains _{"forwardAddress":["sogo-tests2@sogo.alinto"],"enabled":1,"keepCopy":0}_ then:
|
||||
----
|
||||
sogo-tool user-preferences set defaults user1 Forward -f /path/filename -p cred
|
||||
----
|
||||
|
||||
|
||||
|
||||
Upgrading
|
||||
---------
|
||||
|
||||
@@ -3828,8 +3301,6 @@ current version of SOGo from the previous release.
|
||||
h|5.9.0
|
||||
|Run the shell script `sql-update-5.8.4_to_5.9.0.sh` (if you are using MySQL). This will change the `c_defaults` to `MEDIUMTEXT`.
|
||||
|
||||
For mac Apple's Calendar users who re-create their calendar in 5.8.2, the account must be removed and re-added (https://bugs.sogo.nu/view.php?id=5639#c16901). This can be achived in `` / `System Settings` / `Internet Accounts`.
|
||||
|
||||
h|5.8.2
|
||||
|Changes in Mac OS X Ventura 13.3. For mac Apple's Calendar users, the account must be removed and re-added (https://bugs.sogo.nu/view.php?id=5639#c16901). This can be achived in `` / `System Settings` / `Internet Accounts`.
|
||||
|
||||
|
||||
@@ -22,7 +22,7 @@ devices (iPhone, iPod touch, and iPad).
|
||||
The instructions are based on version {release_version} of SOGo.
|
||||
|
||||
The latest version of this guide is available at
|
||||
https://www.sogo.nu/support.html#/documentation.
|
||||
https://sogo.nu/downloads/documentation.html.
|
||||
|
||||
Introduction
|
||||
------------
|
||||
|
||||
@@ -22,7 +22,7 @@ Guide_ for more information on installing and configuring SOGo.
|
||||
The instructions are based on version {release_version} of SOGo.
|
||||
|
||||
The latest version of this guide is available
|
||||
at https://www.sogo.nu/support.html#/documentation.
|
||||
at https://sogo.nu/downloads/documentation.html.
|
||||
|
||||
Introduction
|
||||
------------
|
||||
|
||||
@@ -32,6 +32,7 @@
|
||||
@interface SOGo : SoApplication
|
||||
{
|
||||
SOGoCache *cache;
|
||||
NSTimer *timerCheckMemoryLimit;
|
||||
}
|
||||
|
||||
- (NSDictionary *) currentLocaleConsideringLanguages:(NSArray *)_langs;
|
||||
|
||||
+29
-30
@@ -72,6 +72,7 @@ static BOOL hasCheckedTables;
|
||||
static BOOL debugRequests;
|
||||
static BOOL useRelativeURLs;
|
||||
static BOOL trustProxyAuthentication;
|
||||
static const NSTimeInterval kMemoryCheckTimerInS = 5.0f;
|
||||
|
||||
#ifdef GNUSTEP_BASE_LIBRARY
|
||||
static BOOL debugLeaks;
|
||||
@@ -138,8 +139,9 @@ static BOOL debugLeaks;
|
||||
|
||||
/* load products */
|
||||
[[SOGoProductLoader productLoader] loadAllProducts: YES];
|
||||
if (vMemSizeLimit > 0)
|
||||
if (vMemSizeLimit > 0) {
|
||||
[self logWithFormat: @"All products loaded - current memory usage at %d MB", [[NSProcessInfo processInfo] virtualMemorySize]/1048576];
|
||||
}
|
||||
}
|
||||
|
||||
- (id) init
|
||||
@@ -152,6 +154,12 @@ static BOOL debugLeaks;
|
||||
rm = [[WEResourceManager alloc] init];
|
||||
[self setResourceManager:rm];
|
||||
[rm release];
|
||||
|
||||
timerCheckMemoryLimit = [NSTimer scheduledTimerWithTimeInterval: kMemoryCheckTimerInS
|
||||
target: self
|
||||
selector: @selector(checkIfDaemonHasToBeShutdown)
|
||||
userInfo: nil
|
||||
repeats:YES];
|
||||
}
|
||||
|
||||
return self;
|
||||
@@ -349,11 +357,8 @@ static BOOL debugLeaks;
|
||||
{
|
||||
if ([[context request] handledByDefaultHandler])
|
||||
authenticator = [SOGoWebAuthenticator sharedSOGoWebAuthenticator];
|
||||
else {
|
||||
else
|
||||
authenticator = [SOGoDAVAuthenticator sharedSOGoDAVAuthenticator];
|
||||
[authenticator setContext: context];
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return authenticator;
|
||||
@@ -366,14 +371,10 @@ static BOOL debugLeaks;
|
||||
{
|
||||
SOGoUser *user;
|
||||
id userFolder;
|
||||
NSData *decodedLogin;
|
||||
NSString *login;
|
||||
|
||||
login = [SOGoUser getDecryptedUsernameIfNeeded: _key withContext: _ctx];
|
||||
|
||||
user = [SOGoUser userWithLogin: login roles: nil];
|
||||
|
||||
user = [SOGoUser userWithLogin: _key roles: nil];
|
||||
if (user)
|
||||
userFolder = [$(@"SOGoUserFolder") objectWithName: login
|
||||
userFolder = [$(@"SOGoUserFolder") objectWithName: _key
|
||||
inContainer: self];
|
||||
else
|
||||
userFolder = nil;
|
||||
@@ -484,14 +485,24 @@ static BOOL debugLeaks;
|
||||
if (vMemSizeLimit > 0)
|
||||
{
|
||||
vmem = [[NSProcessInfo processInfo] virtualMemorySize]/1048576;
|
||||
|
||||
if (vmem > vMemSizeLimit)
|
||||
{
|
||||
[self logWithFormat:
|
||||
@"terminating app, vMem size limit (%d MB) has been reached"
|
||||
@" (currently %d MB)",
|
||||
vMemSizeLimit, vmem];
|
||||
[self terminate];
|
||||
if (![self isTerminating]) {
|
||||
if (timerCheckMemoryLimit) {
|
||||
[timerCheckMemoryLimit invalidate];
|
||||
}
|
||||
|
||||
[self logWithFormat:
|
||||
@"terminating app, vMem size limit (%d MB) has been reached"
|
||||
@" (currently %d MB)",
|
||||
vMemSizeLimit, vmem];
|
||||
[self terminate];
|
||||
} else {
|
||||
[self logWithFormat:
|
||||
@"vMem size limit (%d MB) has been reached"
|
||||
@" (currently %d MB) but process is already terminating",
|
||||
vMemSizeLimit, vmem];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -600,18 +611,6 @@ static BOOL debugLeaks;
|
||||
forKey: @"SOGo-Request-Duration"];
|
||||
}
|
||||
|
||||
if (![self isTerminating])
|
||||
{
|
||||
if (!runLoopModes)
|
||||
runLoopModes = [[NSArray alloc] initWithObjects: NSDefaultRunLoopMode, nil];
|
||||
|
||||
// TODO: a bit complicated? (-perform:afterDelay: doesn't work?)
|
||||
[[NSRunLoop currentRunLoop] performSelector:
|
||||
@selector (checkIfDaemonHasToBeShutdown)
|
||||
target: self argument: nil
|
||||
order:1 modes:runLoopModes];
|
||||
}
|
||||
|
||||
return resp;
|
||||
}
|
||||
|
||||
|
||||
@@ -1149,12 +1149,10 @@ andAttribute: (EOAttribute *)_attribute
|
||||
startDate = [theComponent startDate];
|
||||
t = [startDate timeIntervalSince1970];
|
||||
if (startDate) {
|
||||
if (t < (long long)INT_MAX && t > (long long)INT_MIN) {
|
||||
if (t < (long long)INT_MAX && t > (long long)INT_MIN)
|
||||
[quickRow setObject:[NSNumber numberWithInt: t] forKey:@"c_startdate"];
|
||||
} else {
|
||||
[self errorWithFormat:@"Unexpected value. Invalid start date for timestamp %f", t];
|
||||
else
|
||||
[quickRow setObject:[NSNumber numberWithInt: 0] forKey:@"c_startdate"];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+13
-13
@@ -159,28 +159,28 @@ static NGCardsSaxHandler *sax = nil;
|
||||
CardElement *newChild;
|
||||
|
||||
if (aChild)
|
||||
{
|
||||
childTag = [aChild tag];
|
||||
newChild = nil;
|
||||
mappedClass = [self classForTag: [childTag uppercaseString]];
|
||||
if (mappedClass)
|
||||
{
|
||||
if (![aChild isKindOfClass: mappedClass])
|
||||
{
|
||||
childTag = [aChild tag];
|
||||
newChild = nil;
|
||||
mappedClass = [self classForTag: [childTag uppercaseString]];
|
||||
if (mappedClass)
|
||||
{
|
||||
if (![aChild isKindOfClass: mappedClass])
|
||||
{
|
||||
//NSLog (@"warning: new child to entity '%@': '%@' converted to '%@'",
|
||||
// tag, childTag, NSStringFromClass(mappedClass));
|
||||
newChild = [aChild elementWithClass: mappedClass];
|
||||
}
|
||||
}
|
||||
}
|
||||
// else
|
||||
// NSLog (@"warning: no mapped class for tag '%@'",
|
||||
// childTag);
|
||||
|
||||
if (!newChild)
|
||||
newChild = aChild;
|
||||
[children addObject: newChild];
|
||||
[newChild setParent: self];
|
||||
}
|
||||
if (!newChild)
|
||||
newChild = aChild;
|
||||
[children addObject: newChild];
|
||||
[newChild setParent: self];
|
||||
}
|
||||
}
|
||||
|
||||
- (CardElement *) uniqueChildWithTag: (NSString *) aTag
|
||||
|
||||
@@ -72,14 +72,8 @@
|
||||
|
||||
- (void) setEndDate: (NSCalendarDate *) newEndDate
|
||||
{
|
||||
CardElement *c;
|
||||
|
||||
[(iCalDateTime *) [self uniqueChildWithTag: @"dtend"]
|
||||
setDateTime: newEndDate];
|
||||
|
||||
c = [self uniqueChildWithTag: @"duration"];
|
||||
if (c)
|
||||
[self removeChild: c];
|
||||
}
|
||||
|
||||
- (NSCalendarDate *) endDate
|
||||
|
||||
@@ -180,7 +180,8 @@ static inline unsigned iCalDoWForNSDoW (int dow)
|
||||
* There's no GUI to defined such conditions, so there's no
|
||||
* problem for now.
|
||||
*/
|
||||
- (NSArray *) recurrenceRangesWithinCalendarDateRange: (NGCalendarDateRange *) _r
|
||||
- (NSArray *)
|
||||
recurrenceRangesWithinCalendarDateRange: (NGCalendarDateRange *) _r
|
||||
{
|
||||
// TODO: check whether this is OK for multiday-events!
|
||||
NSMutableArray *ranges;
|
||||
@@ -239,31 +240,31 @@ static inline unsigned iCalDoWForNSDoW (int dow)
|
||||
}
|
||||
|
||||
if (byMonth && [byMonth count] > 0)
|
||||
{
|
||||
int i;
|
||||
for (i = 0; i < 12; i++)
|
||||
byMonthList[i] = [byMonth containsObject: [NSString stringWithFormat: @"%i", i + 1]];
|
||||
}
|
||||
{
|
||||
int i;
|
||||
for (i = 0; i < 12; i++)
|
||||
byMonthList[i] = [byMonth containsObject: [NSString stringWithFormat: @"%i", i + 1]];
|
||||
}
|
||||
|
||||
/* precalculate month days */
|
||||
|
||||
if (byMonthDay)
|
||||
{
|
||||
NGMonthDaySet_fillWithByMonthDay (&byPositiveMonthDaySet, &byNegativeMonthDaySet, byMonthDay);
|
||||
}
|
||||
{
|
||||
NGMonthDaySet_fillWithByMonthDay (&byPositiveMonthDaySet, &byNegativeMonthDaySet, byMonthDay);
|
||||
}
|
||||
|
||||
if (repeatCount > 0)
|
||||
{
|
||||
numberOfMonthsInRange = [eventStartDate monthsBetweenDate: rEnd] + 1;
|
||||
}
|
||||
{
|
||||
numberOfMonthsInRange = [eventStartDate monthsBetweenDate: rEnd] + 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
diff = [eventStartDate monthsBetweenDate: rStart];
|
||||
if ((diff != 0) && [rStart compare: eventStartDate] == NSOrderedAscending)
|
||||
diff = -diff;
|
||||
{
|
||||
diff = [eventStartDate monthsBetweenDate: rStart];
|
||||
if ((diff != 0) && [rStart compare: eventStartDate] == NSOrderedAscending)
|
||||
diff = -diff;
|
||||
|
||||
numberOfMonthsInRange = [rStart monthsBetweenDate: rEnd] + 1;
|
||||
}
|
||||
numberOfMonthsInRange = [rStart monthsBetweenDate: rEnd] + 1;
|
||||
}
|
||||
|
||||
ranges = [NSMutableArray arrayWithCapacity: numberOfMonthsInRange];
|
||||
|
||||
@@ -291,167 +292,172 @@ static inline unsigned iCalDoWForNSDoW (int dow)
|
||||
monthIdxInRecurrence = diff + monthIdxInRange;
|
||||
|
||||
if (monthIdxInRecurrence < 0)
|
||||
continue;
|
||||
continue;
|
||||
|
||||
/* first check whether we are in the interval */
|
||||
|
||||
if ((monthIdxInRecurrence % interval) != 0)
|
||||
continue;
|
||||
continue;
|
||||
|
||||
cursor = [referenceDate dateByAddingYears: 0 months: monthIdxInRecurrence days: 0];
|
||||
cursor = [referenceDate dateByAddingYears: 0
|
||||
months: monthIdxInRecurrence
|
||||
days: 0];
|
||||
[cursor setTimeZone: timeZone];
|
||||
numDaysInMonth = [cursor numberOfDaysInMonth];
|
||||
|
||||
/* check whether we match the BYMONTH constraint */
|
||||
|
||||
if (!byMonthList[[cursor monthOfYear] - 1])
|
||||
continue;
|
||||
continue;
|
||||
|
||||
/* check whether we match the BYMONTHDAY and BYDAY constraints */
|
||||
|
||||
didByFill = NO;
|
||||
|
||||
if (byMonthDay)
|
||||
{
|
||||
// Initialize the monthDays array with the positive days positions
|
||||
NGMonthDaySet_copyOrUnion (&monthDays, &byPositiveMonthDaySet, !didByFill);
|
||||
{
|
||||
// Initialize the monthDays array with the positive days positions
|
||||
NGMonthDaySet_copyOrUnion (&monthDays, &byPositiveMonthDaySet, !didByFill);
|
||||
|
||||
// Add to the array the days matching the negative days positions
|
||||
int i;
|
||||
for (i = 1; i <= 31; i++)
|
||||
if (byNegativeMonthDaySet[i])
|
||||
monthDays[numDaysInMonth - i + 1] = YES;
|
||||
didByFill = YES;
|
||||
}
|
||||
// Add to the array the days matching the negative days positions
|
||||
int i;
|
||||
for (i = 1; i <= 31; i++)
|
||||
if (byNegativeMonthDaySet[i])
|
||||
monthDays[numDaysInMonth - i + 1] = YES;
|
||||
didByFill = YES;
|
||||
}
|
||||
|
||||
if (byDayMask)
|
||||
{
|
||||
if (!didByFill)
|
||||
NGMonthDaySet_clear (&monthDays);
|
||||
{
|
||||
if (!didByFill)
|
||||
NGMonthDaySet_clear (&monthDays);
|
||||
|
||||
if (bySetPos)
|
||||
{
|
||||
NSUInteger monthDay;
|
||||
NSInteger currentPos;
|
||||
iCalWeekDay currentWeekDay;
|
||||
if (bySetPos)
|
||||
{
|
||||
NSUInteger monthDay;
|
||||
NSInteger currentPos;
|
||||
iCalWeekDay currentWeekDay;
|
||||
|
||||
currentWeekDay = [[cursor firstDayOfMonth] dayOfWeek];
|
||||
currentPos = 1;
|
||||
for (monthDay = 0; monthDay <= numDaysInMonth; monthDay++)
|
||||
{
|
||||
if ([byDayMask occursOnDay: currentWeekDay])
|
||||
{
|
||||
if ([bySetPos containsObject:
|
||||
[NSString stringWithFormat: @"%d", (int)currentPos]])
|
||||
monthDays[monthDay+1] = YES;
|
||||
currentPos++;
|
||||
}
|
||||
currentWeekDay = (currentWeekDay + 1) % 7;
|
||||
}
|
||||
currentWeekDay = [[cursor firstDayOfMonth] dayOfWeek];
|
||||
currentPos = 1;
|
||||
for (monthDay = 0; monthDay <= numDaysInMonth; monthDay++)
|
||||
{
|
||||
if ([byDayMask occursOnDay: currentWeekDay])
|
||||
{
|
||||
if ([bySetPos containsObject:
|
||||
[NSString stringWithFormat: @"%d", (int)currentPos]])
|
||||
monthDays[monthDay+1] = YES;
|
||||
currentPos++;
|
||||
}
|
||||
currentWeekDay = (currentWeekDay + 1) % 7;
|
||||
}
|
||||
|
||||
currentWeekDay = [[cursor lastDayOfMonth] dayOfWeek];
|
||||
currentPos = -1;
|
||||
for (monthDay = numDaysInMonth; monthDay > 0; monthDay--)
|
||||
{
|
||||
if ([byDayMask occursOnDay: currentWeekDay])
|
||||
{
|
||||
if ([bySetPos containsObject:
|
||||
[NSString stringWithFormat: @"%d", (int)currentPos]])
|
||||
monthDays[monthDay] = YES;
|
||||
currentPos--;
|
||||
}
|
||||
if (currentWeekDay > 0)
|
||||
currentWeekDay--;
|
||||
else
|
||||
currentWeekDay = 6;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
unsigned int firstDoWInMonth, currentWeekDay;
|
||||
unsigned int weekDaysCount[7], currentWeekDaysCount[7];
|
||||
int i, positiveOrder, negativeOrder;
|
||||
currentWeekDay = [[cursor lastDayOfMonth] dayOfWeek];
|
||||
currentPos = -1;
|
||||
for (monthDay = numDaysInMonth; monthDay > 0; monthDay--)
|
||||
{
|
||||
if ([byDayMask occursOnDay: currentWeekDay])
|
||||
{
|
||||
if ([bySetPos containsObject:
|
||||
[NSString stringWithFormat: @"%d", (int)currentPos]])
|
||||
monthDays[monthDay] = YES;
|
||||
currentPos--;
|
||||
}
|
||||
if (currentWeekDay > 0)
|
||||
currentWeekDay--;
|
||||
else
|
||||
currentWeekDay = 6;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
unsigned int firstDoWInMonth, currentWeekDay;
|
||||
unsigned int weekDaysCount[7], currentWeekDaysCount[7];
|
||||
int i, positiveOrder, negativeOrder;
|
||||
|
||||
firstDoWInMonth = [[cursor firstDayOfMonth] dayOfWeek];
|
||||
firstDoWInMonth = [[cursor firstDayOfMonth] dayOfWeek];
|
||||
|
||||
// Fill weekDaysCount to handle negative positions
|
||||
currentWeekDay = firstDoWInMonth;
|
||||
memset(weekDaysCount, 0, 7 * sizeof(unsigned int));
|
||||
for (i = 1; i <= numDaysInMonth; i++)
|
||||
{
|
||||
weekDaysCount[currentWeekDay]++;
|
||||
currentWeekDay = (currentWeekDay + 1) % 7;
|
||||
}
|
||||
// Fill weekDaysCount to handle negative positions
|
||||
currentWeekDay = firstDoWInMonth;
|
||||
memset(weekDaysCount, 0, 7 * sizeof(unsigned int));
|
||||
for (i = 1; i <= numDaysInMonth; i++)
|
||||
{
|
||||
weekDaysCount[currentWeekDay]++;
|
||||
currentWeekDay = (currentWeekDay + 1) % 7;
|
||||
}
|
||||
|
||||
currentWeekDay = firstDoWInMonth;
|
||||
memset(currentWeekDaysCount, 0, 7 * sizeof(unsigned int));
|
||||
for (i = 1; i <= numDaysInMonth; i++)
|
||||
{
|
||||
if (!didByFill || monthDays[i])
|
||||
{
|
||||
positiveOrder = currentWeekDaysCount[currentWeekDay] + 1;
|
||||
negativeOrder = currentWeekDaysCount[currentWeekDay] - weekDaysCount[currentWeekDay];
|
||||
monthDays[i] = (([byDayMask occursOnDay: (iCalWeekDay)currentWeekDay
|
||||
withWeekNumber: positiveOrder]) ||
|
||||
([byDayMask occursOnDay: (iCalWeekDay)currentWeekDay
|
||||
withWeekNumber: negativeOrder]));
|
||||
}
|
||||
currentWeekDaysCount[currentWeekDay]++;
|
||||
currentWeekDay = (currentWeekDay + 1) % 7;
|
||||
}
|
||||
}
|
||||
didByFill = YES;
|
||||
}
|
||||
currentWeekDay = firstDoWInMonth;
|
||||
memset(currentWeekDaysCount, 0, 7 * sizeof(unsigned int));
|
||||
for (i = 1; i <= numDaysInMonth; i++)
|
||||
{
|
||||
if (!didByFill || monthDays[i])
|
||||
{
|
||||
positiveOrder = currentWeekDaysCount[currentWeekDay] + 1;
|
||||
negativeOrder = currentWeekDaysCount[currentWeekDay] - weekDaysCount[currentWeekDay];
|
||||
monthDays[i] = (([byDayMask occursOnDay: (iCalWeekDay)currentWeekDay
|
||||
withWeekNumber: positiveOrder]) ||
|
||||
([byDayMask occursOnDay: (iCalWeekDay)currentWeekDay
|
||||
withWeekNumber: negativeOrder]));
|
||||
}
|
||||
currentWeekDaysCount[currentWeekDay]++;
|
||||
currentWeekDay = (currentWeekDay + 1) % 7;
|
||||
}
|
||||
}
|
||||
didByFill = YES;
|
||||
}
|
||||
|
||||
if (didByFill)
|
||||
{
|
||||
if (diff + monthIdxInRange == 0)
|
||||
{
|
||||
// When dealing with the month of the first occurence, remove days
|
||||
// that occur before the first occurrence.
|
||||
memset (monthDays, NO, sizeof (BOOL) * eventDayOfMonth);
|
||||
// The first occurrence must always be included.
|
||||
monthDays[eventDayOfMonth] = YES;
|
||||
}
|
||||
}
|
||||
{
|
||||
if (diff + monthIdxInRange == 0)
|
||||
{
|
||||
// When dealing with the month of the first occurence, remove days
|
||||
// that occur before the first occurrence.
|
||||
memset (monthDays, NO, sizeof (BOOL) * eventDayOfMonth);
|
||||
// The first occurrence must always be included.
|
||||
monthDays[eventDayOfMonth] = YES;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// No rules applied, take the dayOfMonth of the startDate
|
||||
NGMonthDaySet_clear (&monthDays);
|
||||
monthDays[eventDayOfMonth] = YES;
|
||||
}
|
||||
{
|
||||
// No rules applied, take the dayOfMonth of the startDate
|
||||
NGMonthDaySet_clear (&monthDays);
|
||||
monthDays[eventDayOfMonth] = YES;
|
||||
}
|
||||
|
||||
/*
|
||||
Next step is to create NSCalendarDate instances from our 'monthDays'
|
||||
set. We walk over each day of the 'monthDays' set. If its flag isn't
|
||||
set, we continue.
|
||||
If its set, we add the date to the instance.
|
||||
|
||||
The 'cursor' is the *startdate* of the event (not necessarily a
|
||||
component of the sequence!) plus the currently processed month.
|
||||
Eg:
|
||||
startdate: 2007-01-30
|
||||
cursor[1]: 2007-01-30
|
||||
cursor[2]: 2007-02-28 <== Note: we have February!
|
||||
Next step is to create NSCalendarDate instances from our 'monthDays'
|
||||
set. We walk over each day of the 'monthDays' set. If its flag isn't
|
||||
set, we continue.
|
||||
If its set, we add the date to the instance.
|
||||
|
||||
The 'cursor' is the *startdate* of the event (not necessarily a
|
||||
component of the sequence!) plus the currently processed month.
|
||||
Eg:
|
||||
startdate: 2007-01-30
|
||||
cursor[1]: 2007-01-30
|
||||
cursor[2]: 2007-02-28 <== Note: we have February!
|
||||
*/
|
||||
|
||||
for (dom = 1, doCont = YES; dom <= numDaysInMonth && doCont; dom++)
|
||||
{
|
||||
NSCalendarDate *start;
|
||||
|
||||
if (!monthDays[dom])
|
||||
continue;
|
||||
{
|
||||
NSCalendarDate *start;
|
||||
|
||||
if (!monthDays[dom])
|
||||
continue;
|
||||
|
||||
start = [cursor dateByAddingYears: 0 months: 0 days: (dom - 1)];
|
||||
doCont = [self _addInstanceWithStartDate: start
|
||||
limitDate: until
|
||||
limitRange: _r
|
||||
toArray: ranges];
|
||||
//NSLog(@"*** MONTHLY [%i/%i] adding %@%@ (count = %i)", dom, numDaysInMonth, start, (doCont?@"":@" .. NOT!"), count);
|
||||
if (repeatCount > 0)
|
||||
{
|
||||
count++;
|
||||
//NSLog(@"MONTHLY count = %i/%i", count, repeatCount);
|
||||
doCont = (count < repeatCount);
|
||||
}
|
||||
}
|
||||
start = [cursor dateByAddingYears: 0 months: 0 days: (dom - 1)];
|
||||
doCont = [self _addInstanceWithStartDate: start
|
||||
limitDate: until
|
||||
limitRange: _r
|
||||
toArray: ranges];
|
||||
//NSLog(@"*** MONTHLY [%i/%i] adding %@%@ (count = %i)", dom, numDaysInMonth, start, (doCont?@"":@" .. NOT!"), count);
|
||||
if (repeatCount > 0)
|
||||
{
|
||||
count++;
|
||||
//NSLog(@"MONTHLY count = %i/%i", count, repeatCount);
|
||||
doCont = (count < repeatCount);
|
||||
}
|
||||
}
|
||||
if (!doCont) break; /* reached some limit */
|
||||
}
|
||||
return ranges;
|
||||
|
||||
@@ -110,7 +110,7 @@ static Class yearlyCalcClass = Nil;
|
||||
|
||||
/* complex calculation convenience */
|
||||
|
||||
+ (void) _fillRanges: (NSMutableArray *) ranges
|
||||
+ (void) _fillRanges: (NSMutableArray *) ranges
|
||||
fromRules: (NSArray *) rrules
|
||||
withinRange: (NGCalendarDateRange *) limits
|
||||
startingWithDate: (NGCalendarDateRange *) first
|
||||
@@ -121,14 +121,17 @@ static Class yearlyCalcClass = Nil;
|
||||
|
||||
rules = [rrules objectEnumerator];
|
||||
while ((currentRule = [rules nextObject]))
|
||||
{
|
||||
if ([currentRule isKindOfClass: NSStringClass])
|
||||
currentRule = [iCalRecurrenceRule recurrenceRuleWithICalRepresentation: (NSString *) currentRule];
|
||||
{
|
||||
if ([currentRule isKindOfClass: NSStringClass])
|
||||
currentRule =
|
||||
[iCalRecurrenceRule
|
||||
recurrenceRuleWithICalRepresentation: (NSString *) currentRule];
|
||||
|
||||
calc = [self recurrenceCalculatorForRecurrenceRule: currentRule
|
||||
withFirstInstanceCalendarDateRange: first];
|
||||
[ranges addObjectsFromArray: [calc recurrenceRangesWithinCalendarDateRange: limits]];
|
||||
}
|
||||
calc = [self recurrenceCalculatorForRecurrenceRule: currentRule
|
||||
withFirstInstanceCalendarDateRange: first];
|
||||
[ranges addObjectsFromArray:
|
||||
[calc recurrenceRangesWithinCalendarDateRange: limits]];
|
||||
}
|
||||
}
|
||||
|
||||
+ (void) _fillRanges: (NSMutableArray *) ranges
|
||||
@@ -232,25 +235,27 @@ static Class yearlyCalcClass = Nil;
|
||||
|
||||
dates = [[self _dates: exdates withinRange: limits startingWithDate: first] objectEnumerator];
|
||||
while ((currentDate = [dates nextObject]))
|
||||
{
|
||||
maxRanges = [ranges count];
|
||||
for (count = maxRanges; count > 0; count--)
|
||||
{
|
||||
currentRange = [ranges objectAtIndex: count - 1];
|
||||
compare = [[currentRange startDate] compare: currentDate];
|
||||
if ( compare == NSOrderedSame)
|
||||
{
|
||||
[ranges removeObjectAtIndex: count - 1];
|
||||
}
|
||||
}
|
||||
}
|
||||
{
|
||||
maxRanges = [ranges count];
|
||||
for (count = maxRanges; count > 0; count--)
|
||||
{
|
||||
currentRange = [ranges objectAtIndex: count - 1];
|
||||
compare = [[currentRange startDate] compare: currentDate];
|
||||
if ((compare == NSOrderedAscending || compare == NSOrderedSame) &&
|
||||
[[currentRange endDate] compare: currentDate] == NSOrderedDescending)
|
||||
{
|
||||
[ranges removeObjectAtIndex: count - 1];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+ (NSArray *) recurrenceRangesWithinCalendarDateRange: (NGCalendarDateRange *) _r
|
||||
+ (NSArray *)
|
||||
recurrenceRangesWithinCalendarDateRange: (NGCalendarDateRange *) _r
|
||||
firstInstanceCalendarDateRange: (NGCalendarDateRange *) _fir
|
||||
recurrenceRules: (NSArray *) _rRules
|
||||
exceptionRules: (NSArray *) _exRules
|
||||
recurrenceDates: (NSArray *) _rDates
|
||||
recurrenceDates: (NSArray *) _rDates
|
||||
exceptionDates: (NSArray *) _exDates
|
||||
{
|
||||
NSMutableArray *ranges;
|
||||
@@ -259,10 +264,14 @@ static Class yearlyCalcClass = Nil;
|
||||
|
||||
if ([_rRules count] > 0 || [_rDates count] > 0)
|
||||
{
|
||||
[self _fillRanges: ranges fromRules: _rRules withinRange: _r startingWithDate: _fir];
|
||||
[self _fillRanges: ranges fromDates: _rDates withinRange: _r startingWithDate: _fir];
|
||||
[self _removeExceptionsFromRanges: ranges withRules: _exRules withinRange: _r startingWithDate: _fir];
|
||||
[self _removeExceptionDatesFromRanges: ranges withDates: _exDates withinRange: _r startingWithDate: _fir];
|
||||
[self _fillRanges: ranges fromRules: _rRules
|
||||
withinRange: _r startingWithDate: _fir];
|
||||
[self _fillRanges: ranges fromDates: _rDates
|
||||
withinRange: _r startingWithDate: _fir];
|
||||
[self _removeExceptionsFromRanges: ranges withRules: _exRules
|
||||
withinRange: _r startingWithDate: _fir];
|
||||
[self _removeExceptionDatesFromRanges: ranges withDates: _exDates
|
||||
withinRange: _r startingWithDate: _fir];
|
||||
}
|
||||
|
||||
return ranges;
|
||||
|
||||
@@ -40,15 +40,15 @@
|
||||
|
||||
@implementation iCalYearlyRecurrenceCalculator
|
||||
|
||||
- (NSArray *) recurrenceRangesWithinCalendarDateRange: (NGCalendarDateRange *) _r
|
||||
- (NSArray *)
|
||||
recurrenceRangesWithinCalendarDateRange: (NGCalendarDateRange *) _r
|
||||
{
|
||||
NSMutableArray *ranges;
|
||||
NSArray *byMonth;
|
||||
NSCalendarDate *firStart, *lastDate, *rStart, *rEnd, *until, *referenceDate, *rTemp;
|
||||
NSInteger *hoursOfOffset;
|
||||
NSCalendarDate *firStart, *lastDate, *rStart, *rEnd, *until, *referenceDate;
|
||||
iCalMonthlyRecurrenceCalculator *monthlyCalc;
|
||||
unsigned j, yearIdxInRange, numberOfYearsInRange, count, interval, monthDiff;
|
||||
int diff, repeatCount, currentMonth, origNbDaysInMonth;
|
||||
int diff, repeatCount, currentMonth;
|
||||
|
||||
firStart = [firstRange startDate];
|
||||
rStart = [_r startDate];
|
||||
@@ -185,35 +185,9 @@
|
||||
rStart = [referenceDate dateByAddingYears: 0
|
||||
months: monthDiff
|
||||
days: 0];
|
||||
|
||||
|
||||
//Due to the bug with dateByAddingYears, we may take the previous day (see line 133)
|
||||
hoursOfOffset = [rStart hourOfDay];
|
||||
origNbDaysInMonth = [rStart numberOfDaysInMonth];
|
||||
if(hoursOfOffset > 12) {
|
||||
//If rStart is 1st 22:00, we should start at the previous day 22:00
|
||||
rTemp = [NSCalendarDate dateWithYear: [rStart yearOfCommonEra]
|
||||
month: 1+([rStart monthOfYear]-2)%12
|
||||
day: 1
|
||||
hour: [rStart hourOfDay]
|
||||
minute: [rStart minuteOfHour]
|
||||
second: 0
|
||||
timeZone: [rStart timeZone]];
|
||||
rStart = [NSCalendarDate dateWithYear: [rStart yearOfCommonEra]
|
||||
month: 1+([rStart monthOfYear]-2)%12
|
||||
day: [rTemp numberOfDaysInMonth]
|
||||
hour: [rStart hourOfDay]
|
||||
minute: [rStart minuteOfHour]
|
||||
second: 0
|
||||
timeZone: [rStart timeZone]];
|
||||
}
|
||||
|
||||
|
||||
rEnd = [rStart dateByAddingYears: 0
|
||||
months: 0
|
||||
days: origNbDaysInMonth];
|
||||
|
||||
|
||||
days: [rStart numberOfDaysInMonth]];
|
||||
rangeForMonth = [NGCalendarDateRange calendarDateRangeWithStartDate: rStart
|
||||
endDate: rEnd];
|
||||
rangesInMonth = [monthlyCalc recurrenceRangesWithinCalendarDateRange: rangeForMonth];
|
||||
|
||||
@@ -33,11 +33,9 @@ import sys
|
||||
config = { "cas-addr": "127.0.0.1",
|
||||
"memcached-addrs": ["127.0.0.1:11211"] }
|
||||
|
||||
|
||||
|
||||
class CASProxyValidator:
|
||||
def run(self):
|
||||
if "GATEWAY_INTERFACE" in os.environ:
|
||||
if os.environ.has_key("GATEWAY_INTERFACE"):
|
||||
self._runAsCGI()
|
||||
else:
|
||||
self._runAsCmd()
|
||||
@@ -45,19 +43,19 @@ class CASProxyValidator:
|
||||
def _runAsCGI(self):
|
||||
if self._cgiChecks():
|
||||
form = cgi.FieldStorage()
|
||||
if form.list == []:
|
||||
message = "Empty parameters : assuming cert. validation"
|
||||
self._printCGIError(message, 200)
|
||||
return
|
||||
if form.has_key("pgtId") and form.has_key("pgtIou"):
|
||||
pgtIou = form.getfirst("pgtIou")
|
||||
pgtId = form.getfirst("pgtId")
|
||||
self._registerPGTIdAndIou(pgtIou, pgtId)
|
||||
message = "'%s' set to '%s'" \
|
||||
% ("cas-pgtiou:%s" % pgtIou, pgtId)
|
||||
self._printCGIError(message, 200)
|
||||
else:
|
||||
self._printCGIError("Missing parameter.")
|
||||
if form.list == []:
|
||||
message = "Empty parameters : assuming cert. validation"
|
||||
self._printCGIError(message, 200)
|
||||
return
|
||||
if form.has_key("pgtId") and form.has_key("pgtIou"):
|
||||
pgtIou = form.getfirst("pgtIou")
|
||||
pgtId = form.getfirst("pgtId")
|
||||
self._registerPGTIdAndIou(pgtIou, pgtId)
|
||||
message = "'%s' set to '%s'" \
|
||||
% ("cas-pgtiou:%s" % pgtIou, pgtId)
|
||||
self._printCGIError(message, 200)
|
||||
else:
|
||||
self._printCGIError("Missing parameter.")
|
||||
|
||||
def _cgiChecks(self):
|
||||
rc = False
|
||||
@@ -73,14 +71,17 @@ class CASProxyValidator:
|
||||
return rc
|
||||
|
||||
def _printCGIError(self, message, code = 403):
|
||||
print("Status: %d\nContent-Type: text/plain; charset=utf-8\n\n%s" % (code, message))
|
||||
print("Status: %d\n"
|
||||
"Content-Type: text/plain; charset=utf-8\n\n%s"
|
||||
% (code, message))
|
||||
|
||||
def _runAsCmd(self):
|
||||
if len(sys.argv) == 3:
|
||||
self._registerPGTIdAndIou(sys.argv[1], sys.argv[2])
|
||||
print("set '%s' to '%s'" % ("cas-pgtiou:%s" % sys.argv[1], sys.argv[2]))
|
||||
print "set '%s' to '%s'" \
|
||||
% ("cas-pgtiou:%s" % sys.argv[1], sys.argv[2])
|
||||
else:
|
||||
raise Exception("Missing or too many parameters.")
|
||||
raise Exception, "Missing or too many parameters."
|
||||
|
||||
def _registerPGTIdAndIou(self, pgtIou, pgtId):
|
||||
mc = memcache.Client(config["memcached-addrs"])
|
||||
|
||||
@@ -141,7 +141,7 @@ CREATE TABLE sogo_quick_contact (
|
||||
|
||||
CREATE TABLE sogo_sessions_folder (
|
||||
c_id varchar(255) NOT NULL,
|
||||
c_value varchar(4096) NOT NULL,
|
||||
c_value varchar(255) NOT NULL,
|
||||
c_creationdate int(11) NOT NULL,
|
||||
c_lastseen int(11) NOT NULL,
|
||||
PRIMARY KEY (c_id)
|
||||
|
||||
@@ -15,9 +15,9 @@ vtodo_class2 = "(Vertrauliche Aufgabe)";
|
||||
"The task \"%{Summary}\" was created" = "Die Aufgabe \"%{Summary}\" wurde angelegt";
|
||||
"The task \"%{Summary}\" was deleted" = "Die Aufgabe \"%{Summary}\" wurde gelöscht";
|
||||
"The task \"%{Summary}\" was updated" = "Die Aufgabe \"%{Summary}\" wurde geändert";
|
||||
"The following attendees(s) were notified" = "Die folgenden Teilnehmer*innen wurden benachrichtigt";
|
||||
"The following attendees(s) were added" = "Die folgenden Teilnehmer*innen wurden hinzugefügt";
|
||||
"The following attendees(s) were removed" = "Die folgenden Teilnehmer*innen wurden entfernt";
|
||||
"The following attendees(s) were notified" = "Die folgenden Teilnehmer wurden benachrichtigt";
|
||||
"The following attendees(s) were added" = "Die folgenden Teilnehmer wurden hinzugefügt";
|
||||
"The following attendees(s) were removed" = "Die folgenden Teilnehmer wurden entfernt";
|
||||
|
||||
/* IMIP messages */
|
||||
"calendar_label" = "Kalender";
|
||||
@@ -30,12 +30,12 @@ vtodo_class2 = "(Vertrauliche Aufgabe)";
|
||||
"summary_label" = "Zusammenfassung:";
|
||||
"comment_label" = "Kommentar:";
|
||||
"organizer_label" = "Organisator";
|
||||
"attendee_label" = "Teilnehmer*in";
|
||||
"attendee_label" = "Teilnehmer";
|
||||
|
||||
/* Invitation */
|
||||
"Event Invitation: \"%{Summary}\"" = "Termineinladung: \"%{Summary}\"";
|
||||
"(sent by %{SentBy}) " = "(gesendet von %{SentBy}) ";
|
||||
"%{Organizer} %{SentByText}has invited you to %{Summary}.\n\nStart: %{StartDate}\nEnd: %{EndDate}\nDescription: %{Description}" = "%{Organizer} %{SentByText} hat Sie eingeladen zu \"%{Summary}\".\n\nBeginn: %{StartDate}\nEnde: %{EndDate}\nBeschreibung: %{Description}";
|
||||
"%{Organizer} %{SentByText}has invited you to %{Summary}.\n\nStart: %{StartDate}\nEnd: %{EndDate}\nDescription: %{Description}" = "%{Organizer} %{SentByText} Hat Sie eingeladen zu \"%{Summary}\".\n\nBeginn: %{StartDate}\nEnde: %{EndDate}\nBeschreibung: %{Description}";
|
||||
"%{Organizer} %{SentByText}has invited you to %{Summary}.\n\nStart: %{StartDate} at %{StartTime}\nEnd: %{EndDate} at %{EndTime}\nDescription: %{Description}" = "%{Organizer} %{SentByText} hat Sie eingeladen zu \"%{Summary}\".\n\nBeginn: %{StartDate} um %{StartTime}\nEnde: %{EndDate} um %{EndTime}\nBeschreibung: %{Description}";
|
||||
|
||||
/* Deletion */
|
||||
@@ -47,13 +47,13 @@ vtodo_class2 = "(Vertrauliche Aufgabe)";
|
||||
|
||||
/* Update */
|
||||
"The appointment \"%{Summary}\" for the %{OldStartDate} has changed"
|
||||
= "Der Termin \"%{Summary}\" am %{OldStartDate} wurde geändert";
|
||||
= "Die Verabredung \"%{Summary}\" am %{OldStartDate} wurde geändert";
|
||||
"The appointment \"%{Summary}\" for the %{OldStartDate} at %{OldStartTime} has changed"
|
||||
= "Der Termin \"%{Summary}\" am %{OldStartDate} um %{OldStartTime} hat sich geändert";
|
||||
"The following parameters have changed in the \"%{Summary}\" meeting:"
|
||||
= "Folgendes wurde am Termin \"%{Summary}\" geändert:";
|
||||
"Please accept or decline those changes."
|
||||
= "Bitte akzeptieren Sie diese Änderungen, oder lehnen Sie diese ab.";
|
||||
= "Bitte akzeptieren Sie diese Änderung oder lehnen Sie diese ab.";
|
||||
|
||||
/* Reply */
|
||||
"Accepted invitation: \"%{Summary}\"" = "Einladung zugestimmt: \"%{Summary}\"";
|
||||
|
||||
@@ -12,9 +12,9 @@ vtodo_class2 = "(Konfidenciali užduotis)";
|
||||
"The event \"%{Summary}\" was created" = "Įvykis \"%{summary}\" buvo sukurtas";
|
||||
"The event \"%{Summary}\" was deleted" = "Įvykis \"%{Summary}\" buvo ištrintas";
|
||||
"The event \"%{Summary}\" was updated" = "Įvykis \"%{Summary}\" buvo atnaujintas";
|
||||
"The task \"%{Summary}\" was created" = "Įvykis \"%{summary}\" buvo sukurtas";
|
||||
"The task \"%{Summary}\" was deleted" = "Įvykis \"%{Summary}\" buvo ištrintas";
|
||||
"The task \"%{Summary}\" was updated" = "Įvykis \"%{Summary}\" buvo atnaujintas";
|
||||
"The task \"%{Summary}\" was created" = "The task \"%{Summary}\" was created";
|
||||
"The task \"%{Summary}\" was deleted" = "The task \"%{Summary}\" was deleted";
|
||||
"The task \"%{Summary}\" was updated" = "The task \"%{Summary}\" was updated";
|
||||
"The following attendees(s) were notified" = "Šiems dalyviui(iams) buvo pranešta";
|
||||
"The following attendees(s) were added" = "Šie dalyvis(iai) buvo pridėti";
|
||||
"The following attendees(s) were removed" = "Šie dalyvis(iai) buvo pašalinti";
|
||||
@@ -23,23 +23,23 @@ vtodo_class2 = "(Konfidenciali užduotis)";
|
||||
"calendar_label" = "Kalendorius";
|
||||
"startDate_label" = "Pradėti";
|
||||
"endDate_label" = "Pabaiga";
|
||||
"time_label" = "Laikas";
|
||||
"to_label" = "iki";
|
||||
"time_label" = "Time";
|
||||
"to_label" = "to";
|
||||
"due_label" = "Terminas";
|
||||
"location_label" = "Vietovė";
|
||||
"summary_label" = "Santrauka";
|
||||
"comment_label" = "Komentaras:";
|
||||
"organizer_label" = "Organizatorius";
|
||||
"attendee_label" = "Dalyvis";
|
||||
"organizer_label" = "Organizer";
|
||||
"attendee_label" = "Attendee";
|
||||
|
||||
/* Invitation */
|
||||
"Event Invitation: \"%{Summary}\"" = "Kvietimas į renginį: \"%{Summary}\"";
|
||||
"(sent by %{SentBy}) " = "(išsiųsta %{SentBy}) ";
|
||||
"Event Invitation: \"%{Summary}\"" = "Įvykio pakvietimas: \"%{Summary}\"";
|
||||
"(sent by %{SentBy}) " = "(išsiųsta nuo %{SentBy}) ";
|
||||
"%{Organizer} %{SentByText}has invited you to %{Summary}.\n\nStart: %{StartDate}\nEnd: %{EndDate}\nDescription: %{Description}" = "%{Organizer} %{SentByText} pakvietė Jus į %{Summary}.\nPradžia: %{StartDate}\nPabaiga: %{EndDate}\nAprašymas: %{Description}";
|
||||
"%{Organizer} %{SentByText}has invited you to %{Summary}.\n\nStart: %{StartDate} at %{StartTime}\nEnd: %{EndDate} at %{EndTime}\nDescription: %{Description}" = "%{Organizer} %{SentByText} pakvietė Jus į %{Summary}.\n\nPradžia: %{StartDate} nuo %{StartTime}\nPabaiga: %{EndDate} nuo %{EndTime}\nAprašymas: %{Description}";
|
||||
|
||||
/* Deletion */
|
||||
"Event Cancelled: \"%{Summary}\"" = "Įvykis atšauktas: \"%{Summary}\"";
|
||||
"Event Cancelled: \"%{Summary}\"" = "Įvykis vadinasi: \"%{Summary}\"";
|
||||
"%{Organizer} %{SentByText}has cancelled this event: %{Summary}.\n\nStart: %{StartDate}\nEnd: %{EndDate}\nDescription: %{Description}"
|
||||
= "%{Organizer} %{SentByText} atšaukė šį įvykį: %{Summary}.\n\nPradžia: %{StartDate}\nPabaiga: %{EndDate}\nAprašymas: %{Description}";
|
||||
"%{Organizer} %{SentByText}has cancelled this event: %{Summary}.\n\nStart: %{StartDate} at %{StartTime}\nEnd: %{EndDate} at %{EndTime}\nDescription: %{Description}"
|
||||
@@ -56,27 +56,27 @@ vtodo_class2 = "(Konfidenciali užduotis)";
|
||||
= "Prašome priimti ar atmesti pakeitimus.";
|
||||
|
||||
/* Reply */
|
||||
"Accepted invitation: \"%{Summary}\"" = "Priimti pakvietimą: \"%{Summary}\"";
|
||||
"Accepted invitation: \"%{Summary}\"" = "Priimti pakvietima: \"%{Summary}\"";
|
||||
"Declined invitation: \"%{Summary}\"" = "Atmesti pakvietimą: \"%{Summary}\"";
|
||||
"Delegated invitation: \"%{Summary}\"" = "Deleguoti pakvietimą: \"%{Summary}\"";
|
||||
"Not yet decided on invitation: \"%{Summary}\"" = "Dar neapsisprendęs dėl pakvietimo: \"%{Summary}\"";
|
||||
"%{Attendee} %{SentByText}has accepted your event invitation."
|
||||
= "%{Attendee} %{SentByText} priėmė Jūsų įvykio pakvietimą.";
|
||||
= "%{Attendee} %{SentByText} priėmė Kūsų įvykio pakvietimą.";
|
||||
"%{Attendee} %{SentByText}has declined your event invitation."
|
||||
= "%{Attendee} %{SentByText} atmetė Jūsų pakvietimą į renginį.";
|
||||
= "%{Attendee} %{SentByText} atmetė Jūsų įvykio pakvietimą.";
|
||||
"%{Attendee} %{SentByText}has delegated the invitation to %{Delegate}."
|
||||
= "%{Attendee} %{SentByText} delegavo pakvietimą į %{Delegate}.";
|
||||
"%{Attendee} %{SentByText}has not yet decided upon your event invitation."
|
||||
= "%{Attendee} %{SentByText} neapsisprendė dėl Jūsų kvietimo į renginį.";
|
||||
= "%{Attendee} %{SentByText} neapsisprendė dėl Jūsų įvykio pakvietimo.";
|
||||
|
||||
/* Reminder */
|
||||
"Reminder: \"%{Summary}\" - %{StartDate}" = "Priminimas: \"%{Summary}\" - %{StartDate}";
|
||||
"Reminder: \"%{Summary}\" - %{StartDate}" = "Reminder: \"%{Summary}\" - %{StartDate}";
|
||||
|
||||
/* Resources */
|
||||
"Cannot access resource: \"%{Cn} %{SystemEmail}\"" = "Negalime prieiti prie išteklių: \"%{Cn} %{SystemEmail}\"";
|
||||
"Maximum number of simultaneous bookings (%{NumberOfSimultaneousBookings}) reached for resource \"%{Cn} %{SystemEmail}\". The conflicting event is \"%{EventTitle}\", and starts on %{StartDate}." = "Didžiausias užsakymų vienu metu skaičius (%{NumberOfSimultaneousBookings}) pasiekė ribą \"%{Cn} %{SystemEmail}\". Įvykio konfliktas yra \"%{EventTitle}\", ir prasideda nuo %{StartDate}.";
|
||||
|
||||
/* Participation role */
|
||||
"Your participation is required to this event" = "Jūsų dalyvavimas šiame renginyje būtinas";
|
||||
"Your participation is optional to this event" = "Jūsų dalyvavimas šiame renginyje neprivalomas";
|
||||
"Your participation is not required to this event" = "Jūsų dalyvavimas šiame renginyje nebūtinas";
|
||||
"Your participation is required to this event" = "Your participation is required to this event";
|
||||
"Your participation is optional to this event" = "Your participation is optional to this event";
|
||||
"Your participation is not required to this event" = "Your participation is not required to this event";
|
||||
@@ -2551,7 +2551,7 @@ firstInstanceCalendarDateRange: (NGCalendarDateRange *) fir
|
||||
resourcetype. Anything else will prevent the iPhone from querying the
|
||||
collection. */
|
||||
request = [context request];
|
||||
if (!([request isIPhone] || [request isICal4] || [request isMacOSXCalendar]))
|
||||
if (!([request isIPhone] || [request isICal4]))
|
||||
{
|
||||
gdRT = (NSArray *) [self groupDavResourceType];
|
||||
gdVEventCol = [NSArray arrayWithObjects: [gdRT objectAtIndex: 0],
|
||||
@@ -2609,6 +2609,13 @@ firstInstanceCalendarDateRange: (NGCalendarDateRange *) fir
|
||||
- (NSException *) setDavDescription: (NSString *) description
|
||||
{
|
||||
if (description) {
|
||||
// FIXME: Improve MacOSX Ventura support
|
||||
// Check if the problem will be fixed by Apple or if this fix should be kept in the future
|
||||
// Ticket #5639
|
||||
if ([[context request] isMacOSXVenturaCalendarApp]) {
|
||||
description = [description stringByReplacingOccurrencesOfString:@"/PERSONAL" withString:@"/personal"];
|
||||
}
|
||||
|
||||
[self setFolderPropertyValue: description
|
||||
inCategory: @"DavDescription"];
|
||||
}
|
||||
@@ -3840,82 +3847,4 @@ static NSComparisonResult _comparePermissions (id perm1, id perm2, void *context
|
||||
} // if ([event hasRecurrenceRules]) ...
|
||||
}
|
||||
|
||||
- (NSArray *) _calendarProxiedUsersWithWriteAccess: (BOOL) write
|
||||
{
|
||||
NSMutableDictionary *proxiedUsers;
|
||||
NSArray *references, *elements;
|
||||
NSString *currentLogin;
|
||||
NSNumber *yesNumber;
|
||||
SOGoAppointmentFolders *parentFolder;
|
||||
SOGoUserSettings *us;
|
||||
int count, max;
|
||||
|
||||
yesNumber = [NSNumber numberWithBool: YES];
|
||||
|
||||
us = [[SOGoUser userWithLogin: owner] userSettings];
|
||||
references = [us subscribedCalendars];
|
||||
max = [references count];
|
||||
proxiedUsers = [NSMutableDictionary dictionaryWithCapacity: max];
|
||||
for (count = 0; count < max; count++)
|
||||
{
|
||||
elements = [[references objectAtIndex: count]
|
||||
componentsSeparatedByString: @":"];
|
||||
if ([elements count])
|
||||
{
|
||||
currentLogin = [elements objectAtIndex: 0];
|
||||
if (![proxiedUsers objectForKey: currentLogin])
|
||||
{
|
||||
parentFolder = [[container lookupName: currentLogin
|
||||
inContext: context
|
||||
acquire: NO]
|
||||
lookupName: @"Calendar"
|
||||
inContext: context acquire: NO];
|
||||
if ([parentFolder hasProxyCalendarsWithWriteAccess: write
|
||||
forUserWithLogin: owner])
|
||||
[proxiedUsers setObject: yesNumber forKey: currentLogin];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return [proxiedUsers allKeys];
|
||||
}
|
||||
|
||||
- (NSMutableArray *) _davCalendarProxyForWrite: (BOOL) write
|
||||
{
|
||||
NSMutableArray *proxyFor;
|
||||
NSArray *proxiedUsers, *tag;
|
||||
NSString *appName, *proxiedUser;
|
||||
int count, max;
|
||||
|
||||
appName = [[context request] applicationName];
|
||||
|
||||
proxiedUsers = [self _calendarProxiedUsersWithWriteAccess: write];
|
||||
max = [proxiedUsers count];
|
||||
proxyFor = [NSMutableArray arrayWithCapacity: max];
|
||||
if (max)
|
||||
{
|
||||
for (count = 0; count < max; count++)
|
||||
{
|
||||
proxiedUser = [proxiedUsers objectAtIndex: count];
|
||||
tag = [NSArray arrayWithObjects: @"href", XMLNS_WEBDAV, @"D",
|
||||
[NSString stringWithFormat: @"/%@/dav/%@/",
|
||||
appName, proxiedUser],
|
||||
nil];
|
||||
[proxyFor addObject: tag];
|
||||
}
|
||||
}
|
||||
|
||||
return proxyFor;
|
||||
}
|
||||
|
||||
- (NSArray *) davCalendarProxyWriteFor
|
||||
{
|
||||
return [self _davCalendarProxyForWrite: YES];
|
||||
}
|
||||
|
||||
- (NSArray *) davCalendarProxyReadFor
|
||||
{
|
||||
return [self _davCalendarProxyForWrite: NO];
|
||||
}
|
||||
|
||||
@end /* SOGoAppointmentFolder */
|
||||
@@ -105,21 +105,21 @@
|
||||
interval = [[master endDate]
|
||||
timeIntervalSinceDate: firstDate];
|
||||
if ([newOccurence isAllDay])
|
||||
{
|
||||
nbrDays = ((float) abs (interval) / 86400);
|
||||
[newOccurence setAllDayWithStartDate: date
|
||||
{
|
||||
nbrDays = ((float) abs (interval) / 86400);
|
||||
[newOccurence setAllDayWithStartDate: date
|
||||
duration: nbrDays];
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
[newOccurence setStartDate: date];
|
||||
[newOccurence setEndDate: [date addYear: 0
|
||||
month: 0
|
||||
day: 0
|
||||
hour: 0
|
||||
minute: 0
|
||||
second: interval]];
|
||||
}
|
||||
{
|
||||
[newOccurence setStartDate: date];
|
||||
[newOccurence setEndDate: [date addYear: 0
|
||||
month: 0
|
||||
day: 0
|
||||
hour: 0
|
||||
minute: 0
|
||||
second: interval]];
|
||||
}
|
||||
|
||||
return newOccurence;
|
||||
}
|
||||
@@ -2216,10 +2216,8 @@ inRecurrenceExceptionsForEvent: (iCalEvent *) theEvent
|
||||
// we receive an external invitation (IMIP/ITIP) and we accept it
|
||||
// from a CUA - it gets added to a specific CalDAV calendar using a PUT
|
||||
//
|
||||
else if ([event userIsAttendee: ownerUser]
|
||||
&& [self _shouldScheduleEvent: [event userAsAttendee: ownerUser]]
|
||||
&& iCalPersonPartStatNeedsAction != [[event userAsAttendee: ownerUser] participationStatus])
|
||||
{
|
||||
else if ([event userIsAttendee: ownerUser] && [self _shouldScheduleEvent: [event userAsAttendee: ownerUser]])
|
||||
{
|
||||
[self sendResponseToOrganizer: event
|
||||
from: ownerUser];
|
||||
}
|
||||
@@ -2311,7 +2309,7 @@ inRecurrenceExceptionsForEvent: (iCalEvent *) theEvent
|
||||
recurrenceId = [oldEvent recurrenceId];
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// We check if the PUT call is actually an PART-STATE change
|
||||
// from one of the attendees - here's the logic :
|
||||
//
|
||||
@@ -2341,24 +2339,24 @@ inRecurrenceExceptionsForEvent: (iCalEvent *) theEvent
|
||||
userIsOrganizer = [oldEvent userIsOrganizer: [context activeUser]];
|
||||
}
|
||||
|
||||
// We handle the situation where the SOGo Integrator extension isn't installed or
|
||||
// if the SENT-BY isn't set. That can happen if Bob invites Alice by creating the event
|
||||
// in Annie's calendar. Annie should be the organizer, and Bob the SENT-BY. But most
|
||||
// broken CalDAV client that aren't identity-aware will create the event in Annie's calendar
|
||||
// and set Bob as the organizer. We fix this for them. See #3368 for details.
|
||||
// We handle the situation where the SOGo Integrator extension isn't installed or
|
||||
// if the SENT-BY isn't set. That can happen if Bob invites Alice by creating the event
|
||||
// in Annie's calendar. Annie should be the organizer, and Bob the SENT-BY. But most
|
||||
// broken CalDAV client that aren't identity-aware will create the event in Annie's calendar
|
||||
// and set Bob as the organizer. We fix this for them. See #3368 for details.
|
||||
//
|
||||
// We also handle the case where Bob invites Alice and Bob has full access to Alice's calendar
|
||||
// After inviting ALice, Bob opens the event in Alice's calendar and accept/declines the event.
|
||||
//
|
||||
if (!userIsOrganizer &&
|
||||
if (!userIsOrganizer &&
|
||||
!ownerIsOrganizer &&
|
||||
[[context activeUser] hasEmail: [[newEvent organizer] rfc822Email]])
|
||||
{
|
||||
[[newEvent organizer] setCn: [ownerUser cn]];
|
||||
[[newEvent organizer] setEmail: [[ownerUser allEmails] objectAtIndex: 0]];
|
||||
[[newEvent organizer] setSentBy: [NSString stringWithFormat: @"\"MAILTO:%@\"", [[[context activeUser] allEmails] objectAtIndex: 0]]];
|
||||
ownerIsOrganizer = YES;
|
||||
}
|
||||
[[context activeUser] hasEmail: [[newEvent organizer] rfc822Email]])
|
||||
{
|
||||
[[newEvent organizer] setCn: [ownerUser cn]];
|
||||
[[newEvent organizer] setEmail: [[ownerUser allEmails] objectAtIndex: 0]];
|
||||
[[newEvent organizer] setSentBy: [NSString stringWithFormat: @"\"MAILTO:%@\"", [[[context activeUser] allEmails] objectAtIndex: 0]]];
|
||||
ownerIsOrganizer = YES;
|
||||
}
|
||||
|
||||
// With Thunderbird 10, if you create a recurring event with an exception
|
||||
// occurence, and invite someone, the PUT will have the organizer in the
|
||||
@@ -2369,25 +2367,25 @@ inRecurrenceExceptionsForEvent: (iCalEvent *) theEvent
|
||||
|
||||
if (ownerIsOrganizer)
|
||||
{
|
||||
// We check ACLs of the 'organizer' - in case someone forges the SENT-BY
|
||||
NSString *uid;
|
||||
// We check ACLs of the 'organizer' - in case someone forges the SENT-BY
|
||||
NSString *uid;
|
||||
|
||||
uid = [[oldEvent organizer] uidInContext: context];
|
||||
uid = [[oldEvent organizer] uidInContext: context];
|
||||
|
||||
if (uid && [[[context activeUser] login] caseInsensitiveCompare: uid] != NSOrderedSame)
|
||||
{
|
||||
SOGoAppointmentObject *organizerObject;
|
||||
if (uid && [[[context activeUser] login] caseInsensitiveCompare: uid] != NSOrderedSame)
|
||||
{
|
||||
SOGoAppointmentObject *organizerObject;
|
||||
|
||||
organizerObject = [self _lookupEvent: [oldEvent uid] forUID: uid];
|
||||
roles = [[context activeUser] rolesForObject: organizerObject
|
||||
inContext: context];
|
||||
organizerObject = [self _lookupEvent: [oldEvent uid] forUID: uid];
|
||||
roles = [[context activeUser] rolesForObject: organizerObject
|
||||
inContext: context];
|
||||
|
||||
if (![roles containsObject: @"ComponentModifier"] && ![[context activeUser] isSuperUser])
|
||||
{
|
||||
return [self exceptionWithHTTPStatus: 409
|
||||
if (![roles containsObject: @"ComponentModifier"] && ![[context activeUser] isSuperUser])
|
||||
{
|
||||
return [self exceptionWithHTTPStatus: 409
|
||||
reason: @"Not allowed to perform this action. Wrong SENT-BY being used regarding access rights on organizer's calendar."];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// A RECCURENCE-ID was removed
|
||||
if (!newEvent && oldEvent)
|
||||
@@ -2438,10 +2436,9 @@ inRecurrenceExceptionsForEvent: (iCalEvent *) theEvent
|
||||
|
||||
// We first check of the sequences are alright. We don't accept attendees
|
||||
// accepting "old" invitations. If that's the case, we return a 409
|
||||
if ([[newEvent sequence] intValue] < [[oldEvent sequence] intValue]) {
|
||||
return [self exceptionWithHTTPStatus: 403
|
||||
if ([[newEvent sequence] intValue] < [[oldEvent sequence] intValue])
|
||||
return [self exceptionWithHTTPStatus: 409
|
||||
reason: @"sequences don't match"];
|
||||
}
|
||||
|
||||
// Remove the RSVP attribute, as an action from the attendee
|
||||
// was actually performed, and this confuses iCal (bug #1850)
|
||||
@@ -2465,14 +2462,14 @@ inRecurrenceExceptionsForEvent: (iCalEvent *) theEvent
|
||||
if ([[changes updatedProperties] containsObject: @"exdate"])
|
||||
{
|
||||
[self changeParticipationStatus: @"DECLINED"
|
||||
withDelegate: nil // FIXME (specify delegate?)
|
||||
withDelegate: nil // FIXME (specify delegate?)
|
||||
alarm: nil
|
||||
forRecurrenceId: [self _addedExDate: oldEvent newEvent: newEvent]];
|
||||
}
|
||||
else if (attendee)
|
||||
{
|
||||
[self changeParticipationStatus: [attendee partStat]
|
||||
withDelegate: delegate
|
||||
withDelegate: delegate
|
||||
alarm: nil
|
||||
forRecurrenceId: recurrenceId];
|
||||
}
|
||||
@@ -2484,7 +2481,7 @@ inRecurrenceExceptionsForEvent: (iCalEvent *) theEvent
|
||||
else
|
||||
{
|
||||
[self _handleRemovedUsers: [changes deletedAttendees]
|
||||
withRecurrenceId: recurrenceId];
|
||||
withRecurrenceId: recurrenceId];
|
||||
}
|
||||
}
|
||||
} // if ([[newEvent attendees] count] || [[oldEvent attendees] count])
|
||||
@@ -2493,9 +2490,9 @@ inRecurrenceExceptionsForEvent: (iCalEvent *) theEvent
|
||||
changes = [iCalEventChanges changesFromEvent: oldEvent toEvent: newEvent];
|
||||
if ([changes hasMajorChanges])
|
||||
[self sendReceiptEmailForObject: newEvent
|
||||
addedAttendees: nil
|
||||
deletedAttendees: nil
|
||||
updatedAttendees: nil
|
||||
addedAttendees: nil
|
||||
deletedAttendees: nil
|
||||
updatedAttendees: nil
|
||||
operation: EventUpdated];
|
||||
}
|
||||
} // else of if (isNew) ...
|
||||
@@ -2504,11 +2501,9 @@ inRecurrenceExceptionsForEvent: (iCalEvent *) theEvent
|
||||
// We must NOT invoke [super PUTAction:] here as it'll resave
|
||||
// the content string and we could have etag mismatches.
|
||||
baseVersion = (isNew ? 0 : version);
|
||||
|
||||
ex = [self saveComponent: calendar
|
||||
baseVersion: baseVersion];
|
||||
|
||||
|
||||
ex = [self saveComponent: calendar
|
||||
baseVersion: baseVersion];
|
||||
|
||||
return ex;
|
||||
}
|
||||
@@ -2614,8 +2609,6 @@ inRecurrenceExceptionsForEvent: (iCalEvent *) theEvent
|
||||
ex = [self matchesRequestConditionInContext: context];
|
||||
if (ex)
|
||||
return ex;
|
||||
|
||||
|
||||
}
|
||||
|
||||
if (mustUpdate)
|
||||
|
||||
@@ -391,28 +391,28 @@
|
||||
inContext: localContext
|
||||
acquire: acquire];
|
||||
if (!obj)
|
||||
{
|
||||
if ([lookupName isEqualToString: @"master"])
|
||||
obj = [self occurence: [self component: NO secure: NO]];
|
||||
else if ([lookupName hasPrefix: @"occurence"])
|
||||
{
|
||||
recID = [lookupName substringFromIndex: 9];
|
||||
occurence = [self lookupOccurrence: recID];
|
||||
if (occurence)
|
||||
isNewOccurence = NO;
|
||||
else
|
||||
{
|
||||
if ([lookupName isEqualToString: @"master"])
|
||||
obj = [self occurence: [self component: NO secure: NO]];
|
||||
else if ([lookupName hasPrefix: @"occurence"])
|
||||
{
|
||||
recID = [lookupName substringFromIndex: 9];
|
||||
occurence = [self lookupOccurrence: recID];
|
||||
if (occurence)
|
||||
isNewOccurence = NO;
|
||||
else
|
||||
{
|
||||
occurence = [self newOccurenceWithID: recID];
|
||||
isNewOccurence = YES;
|
||||
}
|
||||
if (occurence)
|
||||
if (occurence)
|
||||
{
|
||||
obj = [self occurence: occurence];
|
||||
if (isNewOccurence)
|
||||
[obj setIsNew: isNewOccurence];
|
||||
[obj setIsNew: isNewOccurence];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return obj;
|
||||
}
|
||||
@@ -470,33 +470,36 @@
|
||||
calendar = &fullCalendar;
|
||||
|
||||
if (!*calendar)
|
||||
{
|
||||
if (secure)
|
||||
iCalString = [self secureContentAsString];
|
||||
else
|
||||
iCalString = content;
|
||||
{
|
||||
if (secure)
|
||||
iCalString = [self secureContentAsString];
|
||||
else
|
||||
iCalString = content;
|
||||
|
||||
if ([iCalString length] > 0)
|
||||
{
|
||||
ASSIGN (*calendar, [iCalCalendar parseSingleFromSource: iCalString]);
|
||||
if (!secure)
|
||||
originalCalendar = [*calendar copy];
|
||||
if ([iCalString length] > 0)
|
||||
{
|
||||
ASSIGN (*calendar, [iCalCalendar parseSingleFromSource: iCalString]);
|
||||
if (!secure)
|
||||
originalCalendar = [*calendar copy];
|
||||
}
|
||||
else
|
||||
{
|
||||
if (create)
|
||||
{
|
||||
ASSIGN (*calendar, [iCalCalendar groupWithTag: @"vcalendar"]);
|
||||
[*calendar setVersion: @"2.0"];
|
||||
prodID = [NSString stringWithFormat:
|
||||
@"-//Inverse inc./SOGo %@//EN",
|
||||
SOGoVersion];
|
||||
[*calendar setProdID: prodID];
|
||||
tag = [[self componentTag] uppercaseString];
|
||||
newComponent = [[*calendar classForTag: tag]
|
||||
groupWithTag: tag];
|
||||
[newComponent setUid: [self globallyUniqueObjectId]];
|
||||
[*calendar addChild: newComponent];
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (create)
|
||||
{
|
||||
ASSIGN (*calendar, [iCalCalendar groupWithTag: @"vcalendar"]);
|
||||
[*calendar setVersion: @"2.0"];
|
||||
prodID = [NSString stringWithFormat:@"-//Inverse inc./SOGo %@//EN", SOGoVersion];
|
||||
[*calendar setProdID: prodID];
|
||||
tag = [[self componentTag] uppercaseString];
|
||||
newComponent = [[*calendar classForTag: tag] groupWithTag: tag];
|
||||
[newComponent setUid: [self globallyUniqueObjectId]];
|
||||
[*calendar addChild: newComponent];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
returnedCopy = [*calendar mutableCopy];
|
||||
[returnedCopy autorelease];
|
||||
|
||||
@@ -88,7 +88,7 @@
|
||||
}
|
||||
|
||||
tag = [NSArray arrayWithObjects: @"href", XMLNS_WEBDAV, @"D",
|
||||
[NSString stringWithFormat: @"/SOGo/dav/%@/", [self nameInContainer]],
|
||||
[NSString stringWithFormat: @"/SOGo/dav/%@/", nameInContainer],
|
||||
nil];
|
||||
[addresses addObjectUniquely: tag];
|
||||
|
||||
@@ -128,7 +128,7 @@
|
||||
}
|
||||
|
||||
tag = [NSArray arrayWithObjects: @"href", XMLNS_WEBDAV, @"D",
|
||||
[NSString stringWithFormat: @"/SOGo/dav/%@/", [self nameInContainer]],
|
||||
[NSString stringWithFormat: @"/SOGo/dav/%@/", nameInContainer],
|
||||
nil];
|
||||
[addresses addObjectUniquely: tag];
|
||||
|
||||
@@ -190,7 +190,7 @@
|
||||
|
||||
- (NSArray *) davCalendarScheduleOutboxURL
|
||||
{
|
||||
return [self _davSpecialCalendarURLWithName: @"personal"];
|
||||
return [[context request] isMacOSXVenturaCalendarApp] ? [self _davSpecialCalendarURLWithName: @"PERSONAL"] : [self _davSpecialCalendarURLWithName: @"personal"];
|
||||
}
|
||||
|
||||
- (NSArray *) _calendarProxiedUsersWithWriteAccess: (BOOL) write
|
||||
|
||||
@@ -39,7 +39,6 @@
|
||||
id occurrence;
|
||||
iCalRepeatableEntityObject *component;
|
||||
NSUInteger count, max, seconds, recSeconds;
|
||||
NSCalendarDate* recId;
|
||||
|
||||
occurrence = nil;
|
||||
|
||||
@@ -51,26 +50,26 @@
|
||||
component = [components objectAtIndex: 0];
|
||||
|
||||
if ([component isRecurrent] || [component recurrenceId])
|
||||
{
|
||||
// Skip the master event if required
|
||||
count = ([component recurrenceId] ? 0 : 1);
|
||||
while (!occurrence && count < max)
|
||||
{
|
||||
component = [components objectAtIndex: count];
|
||||
recSeconds = [[component recurrenceId] timeIntervalSince1970];
|
||||
if (recSeconds == seconds)
|
||||
occurrence = component;
|
||||
else
|
||||
count++;
|
||||
}
|
||||
}
|
||||
{
|
||||
// Skip the master event if required
|
||||
count = ([component recurrenceId] ? 0 : 1);
|
||||
while (!occurrence && count < max)
|
||||
{
|
||||
component = [components objectAtIndex: count];
|
||||
recSeconds = [[component recurrenceId] timeIntervalSince1970];
|
||||
if (recSeconds == seconds)
|
||||
occurrence = component;
|
||||
else
|
||||
count++;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
/* The "master" event could be that occurrence. */
|
||||
recSeconds = [[component recurrenceId] timeIntervalSince1970];
|
||||
if (recSeconds == seconds)
|
||||
occurrence = component;
|
||||
}
|
||||
{
|
||||
/* The "master" event could be that occurrence. */
|
||||
recSeconds = [[component recurrenceId] timeIntervalSince1970];
|
||||
if (recSeconds == seconds)
|
||||
occurrence = component;
|
||||
}
|
||||
|
||||
return occurrence;
|
||||
}
|
||||
|
||||
@@ -408,23 +408,24 @@
|
||||
|
||||
doesOccur = [self isRecurrent];
|
||||
if (doesOccur)
|
||||
{
|
||||
// Retrieve the range of the first event
|
||||
firstRange = [self firstOccurenceRange]; // returns GMT dates
|
||||
{
|
||||
// Retrieve the range of the first event
|
||||
firstRange = [self firstOccurenceRange]; // returns GMT dates
|
||||
|
||||
// Set the range to check with respect to the event timezone (extracted from the start date)
|
||||
firstStartDate = (iCalDateTime *)[self uniqueChildWithTag: @"dtstart"];
|
||||
timeZone = [(iCalDateTime *)firstStartDate timeZone];
|
||||
if (timeZone)
|
||||
startDate = [(iCalTimeZone *)timeZone computedDateForDate: theOccurenceDate];
|
||||
else
|
||||
startDate = theOccurenceDate;
|
||||
endDate = [startDate addTimeInterval: [self occurenceInterval]];
|
||||
checkRange = [NGCalendarDateRange calendarDateRangeWithStartDate: startDate
|
||||
// Set the range to check with respect to the event timezone (extracted from the start date)
|
||||
firstStartDate = (iCalDateTime *)[self uniqueChildWithTag: @"dtstart"];
|
||||
timeZone = [(iCalDateTime *)firstStartDate timeZone];
|
||||
if (timeZone)
|
||||
startDate = [(iCalTimeZone *)timeZone computedDateForDate: theOccurenceDate];
|
||||
else
|
||||
startDate = theOccurenceDate;
|
||||
endDate = [startDate addTimeInterval: [self occurenceInterval]];
|
||||
checkRange = [NGCalendarDateRange calendarDateRangeWithStartDate: startDate
|
||||
endDate: endDate];
|
||||
|
||||
// Calculate the occurrences for the given date
|
||||
ranges = [NSMutableArray arrayWithArray:
|
||||
// Calculate the occurrences for the given date
|
||||
ranges =
|
||||
[NSMutableArray arrayWithArray:
|
||||
[iCalRecurrenceCalculator recurrenceRangesWithinCalendarDateRange: checkRange
|
||||
firstInstanceCalendarDateRange: firstRange
|
||||
recurrenceRules: [self recurrenceRulesWithTimeZone: timeZone]
|
||||
@@ -432,17 +433,18 @@
|
||||
recurrenceDates: [self recurrenceDatesWithTimeZone: timeZone]
|
||||
exceptionDates: [self exceptionDatesWithTimeZone: timeZone]]];
|
||||
|
||||
// Add the master occurrence when dealing with RDATES.
|
||||
// However, the master event must not be flagged with X-MOZ-FAKED-MASTER.
|
||||
if ([self hasRecurrenceDates] &&
|
||||
![[[self uniqueChildWithTag: @"x-moz-faked-master"] flattenedValuesForKey: @""] isEqualToString: @"1"] &&
|
||||
[checkRange doesIntersectWithDateRange: firstRange])
|
||||
{
|
||||
[ranges insertObject: firstRange atIndex: 0];
|
||||
}
|
||||
// Add the master occurrence when dealing with RDATES.
|
||||
// However, the master event must not be flagged with X-MOZ-FAKED-MASTER.
|
||||
if ([self hasRecurrenceDates] &&
|
||||
![[[self uniqueChildWithTag: @"x-moz-faked-master"]
|
||||
flattenedValuesForKey: @""] isEqualToString: @"1"] &&
|
||||
[checkRange doesIntersectWithDateRange: firstRange])
|
||||
{
|
||||
[ranges insertObject: firstRange atIndex: 0];
|
||||
}
|
||||
|
||||
doesOccur = [ranges dateRangeArrayContainsDate: startDate];
|
||||
}
|
||||
doesOccur = [ranges dateRangeArrayContainsDate: startDate];
|
||||
}
|
||||
|
||||
return doesOccur;
|
||||
}
|
||||
|
||||
@@ -1,2 +1,2 @@
|
||||
"Personal Address Book" = "Asmeninė adresų knyga";
|
||||
"Collected Address Book" = "Rinktinė adresų knygą";
|
||||
"Collected Address Book" = "Rinktinė adresų knyga";
|
||||
@@ -418,7 +418,7 @@ convention:
|
||||
[self setCategories: [o componentsSeparatedByString: @","]];
|
||||
|
||||
// Photo
|
||||
if ([ldifRecord objectForKey: @"photo"] && [ldifRecord objectForKey: @"photo"] != [NSNull null])
|
||||
if ([ldifRecord objectForKey: @"photo"])
|
||||
[self setPhoto: [[ldifRecord objectForKey: @"photo"] stringByEncodingBase64]];
|
||||
|
||||
[self cleanupEmptyChildren];
|
||||
|
||||
@@ -271,9 +271,6 @@ static NSArray *folderListingFields = nil;
|
||||
if ([data length])
|
||||
[contactRecord setObject: data forKey: @"id"];
|
||||
|
||||
// Container name
|
||||
[contactRecord setObject: [self displayName] forKey: @"containerName"];
|
||||
|
||||
// c_cn
|
||||
data = [contactRecord objectForKey: @"c_cn"];
|
||||
if (![data length])
|
||||
|
||||
@@ -239,13 +239,6 @@
|
||||
[newRecord setObject: [oldRecord objectForKey: @"c_uid"]
|
||||
forKey: @"c_uid"];
|
||||
|
||||
//source id
|
||||
[newRecord setObject: [source sourceID] forKey: @"sourceID"];
|
||||
|
||||
//source display name
|
||||
[newRecord setObject: [self displayName]
|
||||
forKey: @"containerName"];
|
||||
|
||||
// c_name => id
|
||||
[newRecord setObject: [oldRecord objectForKey: @"c_name"]
|
||||
forKey: @"c_name"];
|
||||
@@ -434,43 +427,26 @@
|
||||
{
|
||||
NSArray *records, *result;
|
||||
EOSortOrdering *ordering;
|
||||
BOOL processed;
|
||||
|
||||
result = nil;
|
||||
processed = NO;
|
||||
|
||||
[source setListRequiresDot: NO];
|
||||
if ( ![source listRequiresDot])
|
||||
if ([filter length] > 0 || ![source listRequiresDot])
|
||||
{
|
||||
if ([filter length] > 0) {
|
||||
records = [source fetchContactsMatching: filter
|
||||
records = [source fetchContactsMatching: filter
|
||||
withCriteria: criteria
|
||||
inDomain: domain];
|
||||
processed = YES;
|
||||
} else if ([[SOGoSystemDefaults sharedSystemDefaults] globalAddressBookFirstEntriesEnabled]) {
|
||||
records = [source fetchContactsMatching: filter
|
||||
withCriteria: criteria
|
||||
inDomain: domain
|
||||
limit: [[SOGoSystemDefaults sharedSystemDefaults] globalAddressBookFirstEntriesCount]];
|
||||
processed = YES;
|
||||
}
|
||||
|
||||
if (processed) {
|
||||
[childRecords setObjects: records
|
||||
forKeys: [records objectsForKey: @"c_name"
|
||||
notFoundMarker: nil]];
|
||||
records = [self _flattenedRecords: records];
|
||||
ordering
|
||||
= [EOSortOrdering sortOrderingWithKey: sortKey
|
||||
selector: ((sortOrdering == NSOrderedDescending)
|
||||
? EOCompareCaseInsensitiveDescending
|
||||
: EOCompareCaseInsensitiveAscending)];
|
||||
result
|
||||
= [records sortedArrayUsingKeyOrderArray:
|
||||
[NSArray arrayWithObject: ordering]];
|
||||
}
|
||||
|
||||
|
||||
[childRecords setObjects: records
|
||||
forKeys: [records objectsForKey: @"c_name"
|
||||
notFoundMarker: nil]];
|
||||
records = [self _flattenedRecords: records];
|
||||
ordering
|
||||
= [EOSortOrdering sortOrderingWithKey: sortKey
|
||||
selector: ((sortOrdering == NSOrderedDescending)
|
||||
? EOCompareCaseInsensitiveDescending
|
||||
: EOCompareCaseInsensitiveAscending)];
|
||||
result
|
||||
= [records sortedArrayUsingKeyOrderArray:
|
||||
[NSArray arrayWithObject: ordering]];
|
||||
}
|
||||
|
||||
return result;
|
||||
|
||||
@@ -56,7 +56,6 @@
|
||||
int sourceIMAP4ID;
|
||||
NSMutableDictionary *headers;
|
||||
NSString *inReplyTo;
|
||||
NSString *references;
|
||||
NSString *text;
|
||||
NSString *sourceURL;
|
||||
NSString *sourceFlag;
|
||||
@@ -64,7 +63,6 @@
|
||||
BOOL isHTML;
|
||||
BOOL sign;
|
||||
BOOL encrypt;
|
||||
NSMutableArray *tmpFiles;
|
||||
|
||||
// Used during S/MIME encryption
|
||||
NSMutableDictionary *certificates;
|
||||
@@ -97,9 +95,6 @@
|
||||
- (NSString *) inReplyTo;
|
||||
- (void) setInReplyTo: (NSString *) newInReplyTo;
|
||||
|
||||
- (NSString *) references;
|
||||
- (void) setReferences: (NSString *) newReferences;
|
||||
|
||||
- (void) setSourceURL: (NSString *) newSurceURL;
|
||||
- (void) setSourceFlag: (NSString *) newSourceFlag;
|
||||
- (void) setSourceFolder: (NSString *) newSourceFolder;
|
||||
@@ -122,7 +117,7 @@
|
||||
|
||||
/* NGMime representations */
|
||||
|
||||
- (NSData *)mimeMessageForRecipient:(NSString *)theRecipient extractingImages:(BOOL)extractImage;
|
||||
- (NSData *) mimeMessageForRecipient: (NSString *) theRecipient;
|
||||
|
||||
/* operations */
|
||||
- (NSArray *) allRecipients;
|
||||
|
||||
@@ -67,7 +67,6 @@
|
||||
#import <SOGo/SOGoUserFolder.h>
|
||||
#import <SOGo/SOGoUserDefaults.h>
|
||||
#import <SOGo/SOGoSystemDefaults.h>
|
||||
#import <SOGo/NGMimeFileData+SOGo.h>
|
||||
|
||||
#import <NGCards/NGVCard.h>
|
||||
|
||||
@@ -139,11 +138,9 @@ static NSString *userAgent = nil;
|
||||
sourceURL = nil;
|
||||
sourceFlag = nil;
|
||||
inReplyTo = nil;
|
||||
references = nil;
|
||||
isHTML = NO;
|
||||
sign = NO;
|
||||
encrypt = NO;
|
||||
tmpFiles = [[NSMutableArray alloc] init];
|
||||
}
|
||||
|
||||
return self;
|
||||
@@ -158,8 +155,6 @@ static NSString *userAgent = nil;
|
||||
[sourceURL release];
|
||||
[sourceFlag release];
|
||||
[inReplyTo release];
|
||||
[references release];
|
||||
[tmpFiles release];
|
||||
[super dealloc];
|
||||
}
|
||||
|
||||
@@ -366,16 +361,6 @@ static NSString *userAgent = nil;
|
||||
ASSIGN (inReplyTo, newInReplyTo);
|
||||
}
|
||||
|
||||
- (NSString *) references
|
||||
{
|
||||
return references;
|
||||
}
|
||||
|
||||
- (void) setReferences: (NSString *) newReferences
|
||||
{
|
||||
ASSIGN (references, newReferences);
|
||||
}
|
||||
|
||||
- (void) setSourceURL: (NSString *) newSourceURL
|
||||
{
|
||||
ASSIGN (sourceURL, newSourceURL);
|
||||
@@ -436,8 +421,6 @@ static NSString *userAgent = nil;
|
||||
forKey: @"isHTML"];
|
||||
if (inReplyTo)
|
||||
[infos setObject: inReplyTo forKey: @"inReplyTo"];
|
||||
if (references)
|
||||
[infos setObject: references forKey: @"references"];
|
||||
if (sourceIMAP4ID > -1)
|
||||
[infos setObject: [NSString stringWithFormat: @"%i", sourceIMAP4ID]
|
||||
forKey: @"sourceIMAP4ID"];
|
||||
@@ -509,10 +492,6 @@ static NSString *userAgent = nil;
|
||||
value = [infoDict objectForKey: @"inReplyTo"];
|
||||
if (value)
|
||||
[self setInReplyTo: value];
|
||||
|
||||
value = [infoDict objectForKey: @"references"];
|
||||
if (value)
|
||||
[self setReferences: value];
|
||||
}
|
||||
|
||||
//
|
||||
@@ -591,7 +570,7 @@ static NSString *userAgent = nil;
|
||||
id result;
|
||||
|
||||
error = nil;
|
||||
message = [self mimeMessageForRecipient: nil extractingImages: NO];
|
||||
message = [self mimeMessageForRecipient: nil];
|
||||
|
||||
if (!message)
|
||||
{
|
||||
@@ -935,6 +914,7 @@ static NSString *userAgent = nil;
|
||||
if (filename)
|
||||
{
|
||||
NSMutableDictionary *currentInfo;
|
||||
|
||||
currentInfo = [NSMutableDictionary dictionaryWithObjectsAndKeys:
|
||||
filename, @"filename",
|
||||
mimeType, @"mimetype",
|
||||
@@ -1061,7 +1041,6 @@ static NSString *userAgent = nil;
|
||||
{
|
||||
BOOL fromSentMailbox;
|
||||
NSString *msgID;
|
||||
NSString *oldReferences;
|
||||
NSMutableDictionary *info;
|
||||
NGImap4Envelope *sourceEnvelope;
|
||||
SOGoUserDefaults *ud;
|
||||
@@ -1080,13 +1059,6 @@ static NSString *userAgent = nil;
|
||||
msgID = [sourceEnvelope messageID];
|
||||
if ([msgID length] > 0)
|
||||
[self setInReplyTo: msgID];
|
||||
oldReferences = [sourceMail references];
|
||||
if ([oldReferences length] > 0)
|
||||
[self setReferences: [oldReferences stringByAppendingFormat: @" %@", msgID]];
|
||||
else
|
||||
[self setReferences: msgID];
|
||||
|
||||
|
||||
|
||||
ud = [[context activeUser] userDefaults];
|
||||
|
||||
@@ -1860,9 +1832,6 @@ static NSString *userAgent = nil;
|
||||
if (inReplyTo)
|
||||
[map setObject: inReplyTo forKey: @"in-reply-to"];
|
||||
|
||||
if (references)
|
||||
[map setObject: references forKey: @"references"];
|
||||
|
||||
/* add subject */
|
||||
if ([(s = [headers objectForKey: @"subject"]) length] > 0)
|
||||
[map setObject: [s asQPSubjectString: @"utf-8"]
|
||||
@@ -1924,10 +1893,8 @@ static NSString *userAgent = nil;
|
||||
if (_extractImages)
|
||||
{
|
||||
newText = [text htmlByExtractingImages: extractedBodyParts];
|
||||
if ([extractedBodyParts count]) {
|
||||
if ([extractedBodyParts count])
|
||||
[self setText: newText];
|
||||
[self addTmpFiles: extractedBodyParts];
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1939,11 +1906,8 @@ static NSString *userAgent = nil;
|
||||
//[self debugWithFormat: @"MIME Envelope: %@", map];
|
||||
allBodyParts = [self bodyPartsForAllAttachments];
|
||||
|
||||
if (!allBodyParts) {
|
||||
return nil;
|
||||
} else {
|
||||
[self addTmpFiles: allBodyParts];
|
||||
}
|
||||
if (!allBodyParts)
|
||||
return nil;
|
||||
|
||||
//[self debugWithFormat: @"attachments: %@", bodyParts];
|
||||
|
||||
@@ -1962,13 +1926,58 @@ static NSString *userAgent = nil;
|
||||
}
|
||||
}
|
||||
|
||||
if (_extractImages)
|
||||
{
|
||||
int i;
|
||||
for (i = 0 ; i < [extractedBodyParts count] ; i++) {
|
||||
NSMutableDictionary *currentInfo;
|
||||
NSString *filename, *mimeType, *bodyId, *encoding;
|
||||
NSData *body;
|
||||
NGMimeBodyPart *extractedBodyPart;
|
||||
NGMimeContentDispositionHeaderField *contentDisposition;
|
||||
|
||||
extractedBodyPart = [extractedBodyParts objectAtIndex:i];
|
||||
if (extractedBodyPart && [extractedBodyPart headerForKey: @"content-disposition"]) {
|
||||
encoding = [extractedBodyPart encoding];
|
||||
contentDisposition = [[NGMimeContentDispositionHeaderField alloc] initWithString: [extractedBodyPart headerForKey: @"content-disposition"]];
|
||||
|
||||
if (encoding
|
||||
&& [extractedBodyPart contentType]
|
||||
&& [extractedBodyPart contentId]
|
||||
&& [contentDisposition filename]) {
|
||||
|
||||
mimeType = [[extractedBodyPart contentType] stringValue];
|
||||
bodyId = [[extractedBodyPart contentId] stringValue];
|
||||
filename = [contentDisposition filename];
|
||||
currentInfo = [NSMutableDictionary dictionaryWithObjectsAndKeys:
|
||||
filename, @"filename",
|
||||
mimeType, @"mimetype",
|
||||
bodyId, @"bodyId",
|
||||
nil];
|
||||
|
||||
if ([[extractedBodyParts objectAtIndex:i] body]) {
|
||||
if (encoding && [encoding rangeOfString:@"base64"].location != NSNotFound)
|
||||
body = [[[extractedBodyParts objectAtIndex:i] body] dataByDecodingBase64];
|
||||
else
|
||||
body = [[extractedBodyParts objectAtIndex:i] body];
|
||||
|
||||
[self saveAttachment: body
|
||||
withMetadata: currentInfo];
|
||||
}
|
||||
}
|
||||
[contentDisposition release];
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
return message;
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// Return a NGMimeMessage object with inline HTML images (<img src=data>) extracted as attachments (<img src=cid>).
|
||||
//
|
||||
- (NSData *) mimeMessageForRecipient: (NSString *) theRecipient extractingImages: (BOOL)extractImage
|
||||
- (NSData *) mimeMessageForRecipient: (NSString *) theRecipient
|
||||
{
|
||||
NGMimeMessageGenerator *generator, *partGenerator;
|
||||
NGMimeMessage *mimeMessage;
|
||||
@@ -1981,7 +1990,7 @@ static NSString *userAgent = nil;
|
||||
// Nothing to sign or encrypt, let's generate the message and return immediately
|
||||
if (![self sign] && ![self encrypt])
|
||||
{
|
||||
mimeMessage = [self mimeMessageWithHeaders: nil excluding: nil extractingImages: extractImage bodyOnly: NO];
|
||||
mimeMessage = [self mimeMessageWithHeaders: nil excluding: nil extractingImages: YES bodyOnly: NO];
|
||||
if (mimeMessage)
|
||||
{
|
||||
generator = [[[NGMimeMessageGenerator alloc] init] autorelease];
|
||||
@@ -1995,7 +2004,7 @@ static NSString *userAgent = nil;
|
||||
partGenerator = [[[NGMimePartGenerator alloc] init] autorelease];
|
||||
content = [partGenerator generateMimeFromPart: [self mimeMessageWithHeaders: nil
|
||||
excluding: nil
|
||||
extractingImages: extractImage
|
||||
extractingImages: YES
|
||||
bodyOnly: YES]];
|
||||
|
||||
if ([self sign])
|
||||
@@ -2233,41 +2242,6 @@ static NSString *userAgent = nil;
|
||||
return [self sendMailAndCopyToSent: YES];
|
||||
}
|
||||
|
||||
// Extract tmp files in NGMimeBuildMimeTempDirectory from NSArray and store locally
|
||||
// The tmp files will be deleted once the message sent
|
||||
- (void) addTmpFiles:(NSArray *) parts {
|
||||
NGMimeBodyPart *part;
|
||||
|
||||
for (part in parts) {
|
||||
NGMimeFileData *body;
|
||||
NSString *path;
|
||||
|
||||
body = [part body];
|
||||
if (body && [body isKindOfClass: [NGMimeFileData class]]) {
|
||||
path = [body path];
|
||||
if (path) {
|
||||
[tmpFiles addObject: path];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Clean temporary files
|
||||
- (void) cleanTmpFiles {
|
||||
NSString *path;
|
||||
NSFileManager *fm;
|
||||
|
||||
fm = [NSFileManager defaultManager];
|
||||
|
||||
for (path in tmpFiles) {
|
||||
if ([fm fileExistsAtPath: path])
|
||||
[fm removeFileAtPath: path handler: nil];
|
||||
}
|
||||
|
||||
// Clean tmp files
|
||||
[tmpFiles removeAllObjects];
|
||||
}
|
||||
|
||||
//
|
||||
//
|
||||
//
|
||||
@@ -2297,9 +2271,9 @@ static NSString *userAgent = nil;
|
||||
recipient = [recipients objectAtIndex: i];
|
||||
|
||||
if ([[context activeUser] hasEmail: recipient])
|
||||
message = messageForSent = [self mimeMessageForRecipient: nil extractingImages: YES];
|
||||
message = messageForSent = [self mimeMessageForRecipient: nil];
|
||||
else
|
||||
message = [self mimeMessageForRecipient: recipient extractingImages: YES];
|
||||
message = [self mimeMessageForRecipient: recipient];
|
||||
|
||||
if (!message)
|
||||
return [NSException exceptionWithHTTPStatus: 500
|
||||
@@ -2313,21 +2287,19 @@ static NSString *userAgent = nil;
|
||||
inContext: context
|
||||
systemMessage: NO];
|
||||
|
||||
if (error) {
|
||||
[self cleanTmpFiles];
|
||||
if (error)
|
||||
return error;
|
||||
}
|
||||
}
|
||||
|
||||
// If the current user isn't part of the recipient list for encrypted emails
|
||||
// let's generate a crypted email for its sent folder.
|
||||
if (!messageForSent)
|
||||
messageForSent = [self mimeMessageForRecipient: nil extractingImages: YES];
|
||||
messageForSent = [self mimeMessageForRecipient: nil];
|
||||
}
|
||||
else
|
||||
{
|
||||
// Encryption is done or not, if we didn't have to.
|
||||
message = messageForSent = [self mimeMessageForRecipient: nil extractingImages: YES];
|
||||
message = messageForSent = [self mimeMessageForRecipient: nil];
|
||||
|
||||
if (!message)
|
||||
return [NSException exceptionWithHTTPStatus: 500
|
||||
@@ -2375,8 +2347,6 @@ static NSString *userAgent = nil;
|
||||
[imap4 doesMailboxExistAtURL: [container imap4URL]])
|
||||
[(SOGoDraftsFolder *) container expunge];
|
||||
|
||||
[self cleanTmpFiles];
|
||||
|
||||
return error;
|
||||
}
|
||||
|
||||
@@ -2402,7 +2372,7 @@ static NSString *userAgent = nil;
|
||||
NSString *str;
|
||||
NSData *message;
|
||||
|
||||
message = [self mimeMessageForRecipient: nil extractingImages: NO];
|
||||
message = [self mimeMessageForRecipient: nil];
|
||||
if (message)
|
||||
{
|
||||
str = [[NSString alloc] initWithData: message
|
||||
|
||||
@@ -136,6 +136,12 @@ static NSString *inboxFolderName = @"INBOX";
|
||||
namespaceDict = [client namespace];
|
||||
|
||||
namespace = [namespaceDict objectForKey: @"personal"];
|
||||
// FIXME: Improve MacOSX Ventura support
|
||||
// Check if the problem will be fixed by Apple or if this fix should be kept in the future
|
||||
// Ticket #5639
|
||||
if ([[context request] isMacOSXVenturaCalendarApp]) {
|
||||
namespace = [namespaceDict objectForKey: @"PERSONAL"];
|
||||
}
|
||||
if (namespace)
|
||||
[self _appendNamespace: namespace toFolders: folders];
|
||||
|
||||
@@ -1005,27 +1011,33 @@ static NSString *inboxFolderName = @"INBOX";
|
||||
[[[self imap4Connection] client] namespace];
|
||||
|
||||
if ([_key hasPrefix: @"folder"])
|
||||
{
|
||||
folderName = [[_key substringFromIndex: 6] fromCSSIdentifier];
|
||||
{
|
||||
folderName = [[_key substringFromIndex: 6] fromCSSIdentifier];
|
||||
|
||||
namespaces = [NSMutableArray array];
|
||||
[self _appendNamespaces: namespaces];
|
||||
if ([namespaces containsObject: folderName])
|
||||
klazz = [SOGoMailNamespace class];
|
||||
else if ([folderName isEqualToString: [self draftsFolderNameInContext: _ctx]])
|
||||
klazz = [SOGoDraftsFolder class];
|
||||
else if ([folderName isEqualToString: [self sentFolderNameInContext: _ctx]])
|
||||
klazz = [SOGoSentFolder class];
|
||||
else if ([folderName isEqualToString: [self trashFolderNameInContext: _ctx]])
|
||||
klazz = [SOGoTrashFolder class];
|
||||
else if ([folderName isEqualToString: [self junkFolderNameInContext: _ctx]])
|
||||
klazz = [SOGoJunkFolder class];
|
||||
else if ([folderName isEqualToString: [self templatesFolderNameInContext: _ctx]])
|
||||
klazz = [SOGoTemplatesFolder class];
|
||||
else
|
||||
klazz = [SOGoMailFolder class];
|
||||
obj = [klazz objectWithName: _key inContainer: self];
|
||||
}
|
||||
namespaces = [NSMutableArray array];
|
||||
[self _appendNamespaces: namespaces];
|
||||
if ([namespaces containsObject: folderName])
|
||||
klazz = [SOGoMailNamespace class];
|
||||
else if ([folderName
|
||||
isEqualToString: [self draftsFolderNameInContext: _ctx]])
|
||||
klazz = [SOGoDraftsFolder class];
|
||||
else if ([folderName
|
||||
isEqualToString: [self sentFolderNameInContext: _ctx]])
|
||||
klazz = [SOGoSentFolder class];
|
||||
else if ([folderName
|
||||
isEqualToString: [self trashFolderNameInContext: _ctx]])
|
||||
klazz = [SOGoTrashFolder class];
|
||||
else if ([folderName
|
||||
isEqualToString: [self junkFolderNameInContext: _ctx]])
|
||||
klazz = [SOGoJunkFolder class];
|
||||
else if ([folderName
|
||||
isEqualToString: [self templatesFolderNameInContext: _ctx]])
|
||||
klazz = [SOGoTemplatesFolder class];
|
||||
else
|
||||
klazz = [SOGoMailFolder class];
|
||||
|
||||
obj = [klazz objectWithName: _key inContainer: self];
|
||||
}
|
||||
else
|
||||
obj = [super lookupName: _key inContext: _ctx acquire: NO];
|
||||
|
||||
@@ -1210,8 +1222,9 @@ static NSString *inboxFolderName = @"INBOX";
|
||||
{
|
||||
if (!templatesFolder)
|
||||
{
|
||||
templatesFolder = [self folderWithTraversal: [self templatesFolderNameInContext: _ctx]
|
||||
andClassName: @"SOGoTemplatesFolder"];
|
||||
templatesFolder
|
||||
= [self folderWithTraversal: [self templatesFolderNameInContext: _ctx]
|
||||
andClassName: @"SOGoTemplatesFolder"];
|
||||
[templatesFolder retain];
|
||||
}
|
||||
|
||||
|
||||
@@ -1149,40 +1149,51 @@ static NSInteger _compareFetchResultsByUID (id entry1, id entry2, NSDictionary *
|
||||
|
||||
obj = [super lookupName: _key inContext: _ctx acquire: NO];
|
||||
if (!obj)
|
||||
{
|
||||
if ([_key hasPrefix: @"folder"])
|
||||
{
|
||||
mailAccount = [self mailAccountFolder];
|
||||
folderName = [[_key substringFromIndex: 6] fromCSSIdentifier];
|
||||
fullFolderName = [NSString stringWithFormat: @"%@/%@",
|
||||
[self traversalFromMailAccount], folderName];
|
||||
if ([fullFolderName isEqualToString: [mailAccount draftsFolderNameInContext: _ctx]])
|
||||
className = @"SOGoDraftsFolder";
|
||||
else if ([fullFolderName isEqualToString: [mailAccount sentFolderNameInContext: _ctx]])
|
||||
className = @"SOGoSentFolder";
|
||||
else if ([fullFolderName isEqualToString: [mailAccount trashFolderNameInContext: _ctx]])
|
||||
className = @"SOGoTrashFolder";
|
||||
else if ([fullFolderName isEqualToString: [mailAccount junkFolderNameInContext: _ctx]])
|
||||
className = @"SOGoJunkFolder";
|
||||
else if ([fullFolderName isEqualToString: [mailAccount templatesFolderNameInContext: _ctx]])
|
||||
className = @"SOGoTemplatesFolder";
|
||||
/* else if ([folderName isEqualToString:
|
||||
[mailAccount sieveFolderNameInContext: _ctx]])
|
||||
obj = [self lookupFiltersFolder: _key inContext: _ctx]; */
|
||||
else
|
||||
className = @"SOGoMailFolder";
|
||||
if ([_key hasPrefix: @"folder"])
|
||||
{
|
||||
mailAccount = [self mailAccountFolder];
|
||||
folderName = [[_key substringFromIndex: 6] fromCSSIdentifier];
|
||||
fullFolderName = [NSString stringWithFormat: @"%@/%@",
|
||||
[self traversalFromMailAccount], folderName];
|
||||
if ([fullFolderName
|
||||
isEqualToString:
|
||||
[mailAccount draftsFolderNameInContext: _ctx]])
|
||||
className = @"SOGoDraftsFolder";
|
||||
else if ([fullFolderName
|
||||
isEqualToString:
|
||||
[mailAccount sentFolderNameInContext: _ctx]])
|
||||
className = @"SOGoSentFolder";
|
||||
else if ([fullFolderName
|
||||
isEqualToString:
|
||||
[mailAccount trashFolderNameInContext: _ctx]])
|
||||
className = @"SOGoTrashFolder";
|
||||
else if ([fullFolderName
|
||||
isEqualToString:
|
||||
[mailAccount junkFolderNameInContext: _ctx]])
|
||||
className = @"SOGoJunkFolder";
|
||||
else if ([fullFolderName
|
||||
isEqualToString:
|
||||
[mailAccount templatesFolderNameInContext: _ctx]])
|
||||
className = @"SOGoTemplatesFolder";
|
||||
/* else if ([folderName isEqualToString:
|
||||
[mailAccount sieveFolderNameInContext: _ctx]])
|
||||
obj = [self lookupFiltersFolder: _key inContext: _ctx]; */
|
||||
else
|
||||
className = @"SOGoMailFolder";
|
||||
|
||||
obj = [NSClassFromString (className) objectWithName: _key
|
||||
inContainer: self];
|
||||
obj = [NSClassFromString (className) objectWithName: _key
|
||||
inContainer: self];
|
||||
}
|
||||
else if (isdigit ([_key characterAtIndex: 0])
|
||||
&& [self exists])
|
||||
{
|
||||
obj = [SOGoMailObject objectWithName: _key inContainer: self];
|
||||
if ([_key hasSuffix: @".eml"])
|
||||
_key = [_key substringToIndex: [_key length] - 4];
|
||||
[obj setCoreInfos: [prefetchedInfos objectForKey: _key]];
|
||||
}
|
||||
}
|
||||
else if (isdigit ([_key characterAtIndex: 0]) && [self exists])
|
||||
{
|
||||
obj = [SOGoMailObject objectWithName: _key inContainer: self];
|
||||
if ([_key hasSuffix: @".eml"])
|
||||
_key = [_key substringToIndex: [_key length] - 4];
|
||||
[obj setCoreInfos: [prefetchedInfos objectForKey: _key]];
|
||||
}
|
||||
}
|
||||
|
||||
if (!obj && _acquire)
|
||||
obj = [NSException exceptionWithHTTPStatus: 404 /* Not Found */];
|
||||
|
||||
@@ -40,16 +40,18 @@
|
||||
folderName = [[_key substringFromIndex: 6] fromCSSIdentifier];
|
||||
fullFolderName = [NSString stringWithFormat: @"%@/%@",
|
||||
[self traversalFromMailAccount], folderName];
|
||||
if ([fullFolderName isEqualToString: [mailAccount sentFolderNameInContext: _ctx]])
|
||||
if ([fullFolderName
|
||||
isEqualToString: [mailAccount sentFolderNameInContext: _ctx]])
|
||||
className = @"SOGoSentFolder";
|
||||
else if ([fullFolderName isEqualToString: [mailAccount draftsFolderNameInContext: _ctx]])
|
||||
else if ([fullFolderName isEqualToString:
|
||||
[mailAccount draftsFolderNameInContext: _ctx]])
|
||||
className = @"SOGoDraftsFolder";
|
||||
else if ([fullFolderName isEqualToString: [mailAccount trashFolderNameInContext: _ctx]])
|
||||
else if ([fullFolderName isEqualToString:
|
||||
[mailAccount trashFolderNameInContext: _ctx]])
|
||||
className = @"SOGoTrashFolder";
|
||||
else if ([fullFolderName isEqualToString: [mailAccount junkFolderNameInContext: _ctx]])
|
||||
else if ([fullFolderName isEqualToString:
|
||||
[mailAccount junkFolderNameInContext: _ctx]])
|
||||
className = @"SOGoJunkFolder";
|
||||
else if ([fullFolderName isEqualToString: [mailAccount templatesFolderNameInContext: _ctx]])
|
||||
className = @"SOGoTemplatesFolder";
|
||||
/* else if ([folderName isEqualToString:
|
||||
[mailAccount sieveFolderNameInContext: _ctx]])
|
||||
obj = [self lookupFiltersFolder: _key inContext: _ctx]; */
|
||||
|
||||
@@ -97,7 +97,6 @@ extern NSArray *SOGoMailCoreInfoKeys;
|
||||
- (NSString *) cc;
|
||||
- (NSString *) from;
|
||||
- (NSString *) inReplyTo;
|
||||
- (NSString *) references;
|
||||
- (NSString *) messageId;
|
||||
- (NSString *) received;
|
||||
|
||||
|
||||
@@ -93,10 +93,7 @@ SOGo_HEADER_FILES = \
|
||||
\
|
||||
JWT.h \
|
||||
\
|
||||
NGMimeBodyPart+SOGo.h \
|
||||
NGMimeFileData+SOGo.h \
|
||||
\
|
||||
SOGoMobileProvision.h
|
||||
NGMimeBodyPart+SOGo.h
|
||||
|
||||
all::
|
||||
@touch SOGoBuild.m
|
||||
@@ -183,10 +180,7 @@ SOGo_OBJC_FILES = \
|
||||
\
|
||||
JWT.m \
|
||||
\
|
||||
NGMimeBodyPart+SOGo.m \
|
||||
NGMimeFileData+SOGo.m \
|
||||
\
|
||||
SOGoMobileProvision.m
|
||||
NGMimeBodyPart+SOGo.m
|
||||
|
||||
SOGo_C_FILES += lmhash.c aes.c crypt_blowfish.c pkcs5_pbkdf2.c
|
||||
|
||||
|
||||
+12
-59
@@ -1384,21 +1384,6 @@ groupObjectClasses: (NSArray *) newGroupObjectClasses
|
||||
- (NSArray *) fetchContactsMatching: (NSString *) match
|
||||
withCriteria: (NSArray *) criteria
|
||||
inDomain: (NSString *) theDomain
|
||||
{
|
||||
if ([match length] > 0) {
|
||||
return [self fetchContactsMatching: (NSString *) match
|
||||
withCriteria: (NSArray *) criteria
|
||||
inDomain: (NSString *) theDomain
|
||||
limit: -1];
|
||||
} else {
|
||||
return [NSMutableArray array];
|
||||
}
|
||||
}
|
||||
|
||||
- (NSArray *) fetchContactsMatching: (NSString *) match
|
||||
withCriteria: (NSArray *) criteria
|
||||
inDomain: (NSString *) theDomain
|
||||
limit: (int) limit
|
||||
{
|
||||
NSAutoreleasePool *pool;
|
||||
NGLdapConnection *ldapConnection;
|
||||
@@ -1407,8 +1392,6 @@ groupObjectClasses: (NSArray *) newGroupObjectClasses
|
||||
NSMutableArray *contacts;
|
||||
EOQualifier *qualifier;
|
||||
unsigned int i;
|
||||
NSString *sortAttribute;
|
||||
BOOL sortReverse;
|
||||
|
||||
contacts = [NSMutableArray array];
|
||||
|
||||
@@ -1417,48 +1400,18 @@ groupObjectClasses: (NSArray *) newGroupObjectClasses
|
||||
ldapConnection = [self _ldapConnection];
|
||||
qualifier = [self _qualifierForFilter: match onCriteria: criteria];
|
||||
|
||||
if (limit > 0) {
|
||||
[ldapConnection setQuerySizeLimit: limit];
|
||||
}
|
||||
|
||||
if (limit > 0) {
|
||||
// Sort results
|
||||
sortAttribute = @"cn";
|
||||
sortReverse = NO;
|
||||
if ([_scope caseInsensitiveCompare: @"BASE"] == NSOrderedSame)
|
||||
entries = [ldapConnection baseSearchAtBaseDN: _baseDN
|
||||
qualifier: qualifier
|
||||
attributes: _lookupFields
|
||||
sortAttribute: sortAttribute
|
||||
sortReverse: sortReverse];
|
||||
else if ([_scope caseInsensitiveCompare: @"ONE"] == NSOrderedSame)
|
||||
entries = [ldapConnection flatSearchAtBaseDN: _baseDN
|
||||
qualifier: qualifier
|
||||
attributes: _lookupFields
|
||||
sortAttribute: sortAttribute
|
||||
sortReverse: sortReverse];
|
||||
else /* we do it like before */
|
||||
entries = [ldapConnection deepSearchAtBaseDN: _baseDN
|
||||
qualifier: qualifier
|
||||
attributes: _lookupFields
|
||||
sortAttribute: sortAttribute
|
||||
sortReverse: sortReverse];
|
||||
} else {
|
||||
// No sort results
|
||||
if ([_scope caseInsensitiveCompare: @"BASE"] == NSOrderedSame)
|
||||
entries = [ldapConnection baseSearchAtBaseDN: _baseDN
|
||||
qualifier: qualifier
|
||||
attributes: _lookupFields];
|
||||
else if ([_scope caseInsensitiveCompare: @"ONE"] == NSOrderedSame)
|
||||
entries = [ldapConnection flatSearchAtBaseDN: _baseDN
|
||||
qualifier: qualifier
|
||||
attributes: _lookupFields];
|
||||
else /* we do it like before */
|
||||
entries = [ldapConnection deepSearchAtBaseDN: _baseDN
|
||||
qualifier: qualifier
|
||||
attributes: _lookupFields];
|
||||
}
|
||||
|
||||
if ([_scope caseInsensitiveCompare: @"BASE"] == NSOrderedSame)
|
||||
entries = [ldapConnection baseSearchAtBaseDN: _baseDN
|
||||
qualifier: qualifier
|
||||
attributes: _lookupFields];
|
||||
else if ([_scope caseInsensitiveCompare: @"ONE"] == NSOrderedSame)
|
||||
entries = [ldapConnection flatSearchAtBaseDN: _baseDN
|
||||
qualifier: qualifier
|
||||
attributes: _lookupFields];
|
||||
else /* we do it like before */
|
||||
entries = [ldapConnection deepSearchAtBaseDN: _baseDN
|
||||
qualifier: qualifier
|
||||
attributes: _lookupFields];
|
||||
|
||||
i = 0;
|
||||
pool = [NSAutoreleasePool new];
|
||||
|
||||
@@ -1,34 +0,0 @@
|
||||
/* NGMimeFileData+SOGo.h - this file is part of SOGo
|
||||
*
|
||||
* Copyright (C) 2023 Alinto
|
||||
*
|
||||
* Author: Sébastien Mizrahi <smizrahi@alinto.eu>
|
||||
*
|
||||
* This file is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2, or (at your option)
|
||||
* any later version.
|
||||
*
|
||||
* This file is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; see the file COPYING. If not, write to
|
||||
* the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
|
||||
* Boston, MA 02111-1307, USA.
|
||||
*/
|
||||
|
||||
#ifndef NGMIMEFILEDATA_SOGO_H
|
||||
#define NGMIMEFILEDATA_SOGO_H
|
||||
|
||||
#import <NGMime/NGMimeFileData.h>
|
||||
|
||||
@interface NGMimeFileData (SOGoExtensions)
|
||||
|
||||
- (NSString *) path;
|
||||
|
||||
@end
|
||||
|
||||
#endif /* NGMIMEFILEDATA_SOGO_H */
|
||||
@@ -1,32 +0,0 @@
|
||||
/* NGMimeFileData+SOGo.m - this file is part of SOGo
|
||||
*
|
||||
* Copyright (C) 2023 Alinto
|
||||
*
|
||||
* Author: Sébastien Mizrahi <smizrahi@alinto.eu>
|
||||
*
|
||||
* This file is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2, or (at your option)
|
||||
* any later version.
|
||||
*
|
||||
* This file is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; see the file COPYING. If not, write to
|
||||
* the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
|
||||
* Boston, MA 02111-1307, USA.
|
||||
*/
|
||||
|
||||
#import "NGMimeFileData+SOGo.h"
|
||||
|
||||
|
||||
@implementation NGMimeFileData (SOGoExtensions)
|
||||
|
||||
- (NSString *) path {
|
||||
return path;
|
||||
}
|
||||
|
||||
@end
|
||||
@@ -26,7 +26,6 @@
|
||||
#define NSSTRING_CRYPTO_H
|
||||
|
||||
#import <Foundation/NSString.h>
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
typedef enum {
|
||||
encDefault, //!< default encoding, let the algorithm decide
|
||||
@@ -36,10 +35,8 @@ typedef enum {
|
||||
} keyEncoding;
|
||||
|
||||
@class NSObject;
|
||||
@class NSScanner;
|
||||
|
||||
@interface
|
||||
NSString(SOGoCryptoExtension)
|
||||
@interface NSString (SOGoCryptoExtension)
|
||||
|
||||
- (BOOL) isEqualToCrypted: (NSString *) cryptedPassword
|
||||
withDefaultScheme: (NSString *) theScheme
|
||||
@@ -65,9 +62,6 @@ typedef enum {
|
||||
|
||||
+ (NSArray *) getDefaultEncodingForScheme: (NSString *) passwordScheme;
|
||||
|
||||
- (NSString *) encodeAES128ECBBase64:(NSString *)passwordScheme encodedURL:(BOOL)encodedURL exception:(NSException **)ex;
|
||||
- (NSString *) decodeAES128ECBBase64:(NSString *)passwordScheme encodedURL:(BOOL)encodedURL exception:(NSException **)ex;
|
||||
|
||||
@end
|
||||
|
||||
#endif /* NSSTRING_CRYPTO_H */
|
||||
@@ -29,11 +29,6 @@
|
||||
#import "NSData+Crypto.h"
|
||||
#import <NGExtensions/NGBase64Coding.h>
|
||||
|
||||
#import "aes.h"
|
||||
#define AES_SIZE 8096
|
||||
#define AES_KEY_SIZE 16
|
||||
|
||||
static const NSString *kAES128ECError = @"kAES128ECError";
|
||||
|
||||
@implementation NSString (SOGoCryptoExtension)
|
||||
|
||||
@@ -354,135 +349,4 @@ static const NSString *kAES128ECError = @"kAES128ECError";
|
||||
return [[NSData encodeDataAsHexString: [d asLM]] uppercaseString];
|
||||
}
|
||||
|
||||
/**
|
||||
* Encrypts the data using AES 128 ECB mechanism
|
||||
*
|
||||
* @param passwordScheme The 128 bits password key
|
||||
* @param encodedURL YES if the special base64 characters shall be escaped for URL
|
||||
* @param ex Exception pointer
|
||||
* @return If successful, encrypted string in base64
|
||||
*/
|
||||
- (NSString *) encodeAES128ECBBase64:(NSString *)passwordScheme encodedURL:(BOOL)encodedURL exception:(NSException **)ex
|
||||
{
|
||||
NSData *inputData, *keyData, *outputData;
|
||||
|
||||
if (AES_KEY_SIZE != [passwordScheme length]) {
|
||||
*ex = [NSException exceptionWithName:kAES128ECError reason: [NSString stringWithFormat:@"Key must be %d bits", (AES_KEY_SIZE * 8)] userInfo: nil];
|
||||
return nil;
|
||||
}
|
||||
|
||||
NSString *value;
|
||||
int size, i;
|
||||
uint8_t output[AES_SIZE], input[AES_SIZE];
|
||||
|
||||
inputData = [self dataUsingEncoding: NSUnicodeStringEncoding];
|
||||
keyData = [passwordScheme dataUsingEncoding: NSUnicodeStringEncoding];
|
||||
size = [inputData length] + 16; // Add one unicode char size 16 bits (NUL)
|
||||
|
||||
if (inputData == nil) {
|
||||
*ex = [NSException exceptionWithName:kAES128ECError reason: @"Invalid input data (encrypt)" userInfo: nil];
|
||||
return nil;
|
||||
}
|
||||
|
||||
if (((AES_SIZE / 2) - 16) < [self length]) {
|
||||
*ex = [NSException exceptionWithName:kAES128ECError reason: [NSString stringWithFormat:@"Invalid size (encrypt). max size is %d. Current size : %d", ((AES_SIZE / 2) - 16), [self length]] userInfo: nil];
|
||||
return nil;
|
||||
}
|
||||
|
||||
memset(output, 0x00, AES_SIZE);
|
||||
memset(input, 0x00, AES_SIZE);
|
||||
|
||||
[inputData getBytes: input length: [inputData length]];
|
||||
input[[inputData length]] = '\0'; // Add NUL
|
||||
|
||||
|
||||
for(i = 0 ; i < (AES_SIZE / 16) ; ++i)
|
||||
{
|
||||
AES128_ECB_encrypt(input + (i*16), [keyData bytes], output+(i*16));
|
||||
}
|
||||
|
||||
outputData = [NSData dataWithBytes: (char *)output length: size];
|
||||
|
||||
if (outputData) {
|
||||
value = [outputData base64EncodedStringWithOptions: 0];
|
||||
if (encodedURL) {
|
||||
value = [value stringByReplacingOccurrencesOfString: @"+" withString: @"."];
|
||||
value = [value stringByReplacingOccurrencesOfString: @"/" withString: @"_"];
|
||||
value = [value stringByReplacingOccurrencesOfString: @"=" withString: @"-"];
|
||||
}
|
||||
|
||||
return value;
|
||||
} else {
|
||||
*ex = [NSException exceptionWithName:kAES128ECError reason:@"Empty data" userInfo: nil];
|
||||
}
|
||||
|
||||
return nil;
|
||||
}
|
||||
|
||||
/**
|
||||
* Decrypts the base64 data using AES 128 ECB mechanism
|
||||
*
|
||||
* @param passwordScheme The 128 bits password key
|
||||
* @param encodedURL YES if the special base64 characters has been escaped for URL
|
||||
* @param ex Exception pointer
|
||||
* @return If successful, decrypted string
|
||||
*/
|
||||
- (NSString *) decodeAES128ECBBase64:(NSString *)passwordScheme encodedURL:(BOOL)encodedURL exception:(NSException **)ex
|
||||
{
|
||||
NSData *inputData, *keyData, *outputData;
|
||||
NSString *value, *inputString, *tmpStr;
|
||||
int i;
|
||||
uint8_t output[AES_SIZE];
|
||||
|
||||
if (AES_KEY_SIZE != [passwordScheme length]) {
|
||||
*ex = [NSException exceptionWithName:kAES128ECError reason: [NSString stringWithFormat:@"Key must be %d bits", (AES_KEY_SIZE * 8)] userInfo: nil];
|
||||
return nil;
|
||||
}
|
||||
|
||||
|
||||
value = nil;
|
||||
keyData = [passwordScheme dataUsingEncoding: NSUnicodeStringEncoding];
|
||||
memset(output, 0x00, AES_SIZE);
|
||||
|
||||
inputString = [NSString stringWithString: self];
|
||||
if (encodedURL) {
|
||||
inputString = [inputString stringByReplacingOccurrencesOfString: @"." withString: @"+"];
|
||||
inputString = [inputString stringByReplacingOccurrencesOfString: @"_" withString: @"/"];
|
||||
inputString = [inputString stringByReplacingOccurrencesOfString: @"-" withString: @"="];
|
||||
}
|
||||
|
||||
inputData = [[NSData alloc] initWithBase64EncodedString:inputString options:0];
|
||||
|
||||
if (inputData == nil) {
|
||||
*ex = [NSException exceptionWithName:kAES128ECError reason: @"Invalid input data (decrypt)" userInfo: nil];
|
||||
return nil;
|
||||
}
|
||||
|
||||
if ((AES_SIZE) < [inputData length]) {
|
||||
*ex = [NSException exceptionWithName:kAES128ECError reason: [NSString stringWithFormat:@"Invalid size (decrypt). max size is %d. Current size : %d", AES_SIZE, [inputData length]] userInfo: nil];
|
||||
return nil;
|
||||
}
|
||||
|
||||
for(i = 0; i < (AES_SIZE / 16); ++i)
|
||||
{
|
||||
AES128_ECB_decrypt([inputData bytes] + (i*16), [keyData bytes], output + (i*16));
|
||||
}
|
||||
|
||||
outputData = [NSData dataWithBytes: output length:([inputData length] - 16)];
|
||||
if (outputData) {
|
||||
tmpStr = [[NSString alloc] initWithData: outputData encoding: NSUnicodeStringEncoding];
|
||||
if (tmpStr) {
|
||||
value = [NSString stringWithUTF8String: [tmpStr UTF8String]];
|
||||
} else {
|
||||
*ex = [NSException exceptionWithName:kAES128ECError reason:@"Empty converted decrypted data" userInfo: nil];
|
||||
value = nil;
|
||||
}
|
||||
[tmpStr release];
|
||||
} else {
|
||||
*ex = [NSException exceptionWithName:kAES128ECError reason:@"Empty data" userInfo: nil];
|
||||
}
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
@end
|
||||
@@ -65,7 +65,6 @@
|
||||
- (id) objectFromJSONString;
|
||||
|
||||
/* bare email addresses */
|
||||
- (NSString *) mailDomain;
|
||||
- (NSString *) pureEMailAddress;
|
||||
|
||||
- (NSString *) asQPSubjectString: (NSString *) encoding;
|
||||
|
||||
@@ -609,18 +609,6 @@ static int cssEscapingCount;
|
||||
return newString;
|
||||
}
|
||||
|
||||
|
||||
- (NSString *) mailDomain
|
||||
{
|
||||
NSArray *mailSeparated;
|
||||
|
||||
mailSeparated = [self componentsSeparatedByString: @"@"];
|
||||
if([mailSeparated count] == 2)
|
||||
return [mailSeparated objectAtIndex: 1];
|
||||
[self logWithFormat: @"Error while extracting domain from : %@", self];
|
||||
return nil;
|
||||
}
|
||||
|
||||
- (NSString *) pureEMailAddress
|
||||
{
|
||||
NSString *pureAddress;
|
||||
@@ -991,18 +979,6 @@ static int cssEscapingCount;
|
||||
newResult = [regex stringByReplacingMatchesInString:result options:0 range:NSMakeRange(0, [result length]) withTemplate:@"<ifr***"];
|
||||
result = [NSString stringWithString: newResult];
|
||||
|
||||
// Remove <form
|
||||
regex = [NSRegularExpression regularExpressionWithPattern:@"<[\\s\\u200B	

\\\\0]*f[\\s\\u200B	

\\\\0]*o[\\s\\u200B	

\\\\0]*r[\\s\\u200B	

\\\\0]*m"
|
||||
options: NSRegularExpressionCaseInsensitive error:&error];
|
||||
newResult = [regex stringByReplacingMatchesInString:result options:0 range:NSMakeRange(0, [result length]) withTemplate:@"<for*"];
|
||||
result = [NSString stringWithString: newResult];
|
||||
|
||||
// Remove </form
|
||||
regex = [NSRegularExpression regularExpressionWithPattern:@"<[\\s\\u200B	

\\\\0]*/[\\s\\u200B	

\\\\0]*f[\\s\\u200B	

\\\\0]*o[\\s\\u200B	

\\\\0]*r[\\s\\u200B	

\\\\0]*m"
|
||||
options: NSRegularExpressionCaseInsensitive error:&error];
|
||||
newResult = [regex stringByReplacingMatchesInString:result options:0 range:NSMakeRange(0, [result length]) withTemplate:@"</for*"];
|
||||
result = [NSString stringWithString: newResult];
|
||||
|
||||
// Remove onload
|
||||
regex = [NSRegularExpression regularExpressionWithPattern:@"onload="
|
||||
options: NSRegularExpressionCaseInsensitive error:&error];
|
||||
|
||||
@@ -400,7 +400,8 @@
|
||||
|
||||
application = [WOApplication application];
|
||||
request = [[application context] request];
|
||||
pgtURL = [NSString stringWithFormat: @"%@casProxy", [soURL absoluteString]];
|
||||
pgtURL = [NSString stringWithFormat: @"https://%@/%@/casProxy",
|
||||
[soURL host], [request applicationName]];
|
||||
|
||||
return pgtURL;
|
||||
}
|
||||
|
||||
@@ -106,7 +106,6 @@
|
||||
//
|
||||
// CAS support
|
||||
//
|
||||
- (NSString *) sha512HashTicket: (NSString* ) ticket;
|
||||
- (NSString *) CASTicketFromIdentifier: (NSString *) identifier;
|
||||
- (NSString *) CASSessionWithTicket: (NSString *) ticket;
|
||||
- (void) setCASSession: (NSString *) casSession
|
||||
|
||||
@@ -54,10 +54,6 @@
|
||||
|
||||
#import "SOGoCache.h"
|
||||
|
||||
#ifdef HAVE_OPENSSL
|
||||
#include <openssl/sha.h>
|
||||
#endif
|
||||
|
||||
/* Those used to be ivars but were converted to static globals to work around
|
||||
a bug in GCC that prevent SOGo from using libmemcached > 0.37:
|
||||
|
||||
@@ -603,32 +599,9 @@ static memcached_st *handle = NULL;
|
||||
forKey: theLogin];
|
||||
}
|
||||
|
||||
|
||||
//
|
||||
// CAS session support
|
||||
//
|
||||
|
||||
- (NSString *) sha512HashTicket: (NSString* ) ticket
|
||||
{
|
||||
int i;
|
||||
NSMutableString *hashedTicket;
|
||||
|
||||
#if defined(HAVE_OPENSSL)
|
||||
unsigned char hash[SHA512_DIGEST_LENGTH] = {};
|
||||
const unsigned char *data = (const unsigned char *) [ticket UTF8String];
|
||||
SHA512(data, strlen((char *)data), hash);
|
||||
|
||||
hashedTicket = [NSMutableString stringWithCapacity:SHA512_DIGEST_LENGTH * 2];
|
||||
for( i = 0; i < SHA512_DIGEST_LENGTH; i++) {
|
||||
[hashedTicket appendFormat:@"%02x", hash[i]];
|
||||
}
|
||||
#endif
|
||||
if(!hashedTicket)
|
||||
return ticket;
|
||||
return [NSString stringWithString:hashedTicket];
|
||||
}
|
||||
|
||||
|
||||
- (NSString *) CASTicketFromIdentifier: (NSString *) identifier
|
||||
{
|
||||
return [self valueForKey: [NSString stringWithFormat: @"cas-id:%@",
|
||||
@@ -637,9 +610,8 @@ static memcached_st *handle = NULL;
|
||||
|
||||
- (NSString *) CASSessionWithTicket: (NSString *) ticket
|
||||
{
|
||||
//Ticket can be more than 255 char so we hashed it to avoid memcached key limit
|
||||
return [self valueForKey: [NSString stringWithFormat: @"cas-ticket:%@",
|
||||
[self sha512HashTicket: ticket]]];
|
||||
ticket]];
|
||||
}
|
||||
|
||||
- (void) setCASSession: (NSString *) casSession
|
||||
@@ -649,14 +621,14 @@ static memcached_st *handle = NULL;
|
||||
[self setValue: ticket
|
||||
forKey: [NSString stringWithFormat: @"cas-id:%@", identifier]];
|
||||
[self setValue: casSession
|
||||
forKey: [NSString stringWithFormat: @"cas-ticket:%@", [self sha512HashTicket: ticket]]];
|
||||
forKey: [NSString stringWithFormat: @"cas-ticket:%@", ticket]];
|
||||
}
|
||||
|
||||
- (NSString *) CASPGTIdFromPGTIOU: (NSString *) pgtIou
|
||||
{
|
||||
NSString *casPgtId, *key;
|
||||
|
||||
key = [NSString stringWithFormat: @"cas-pgtiou:%@", [self sha512HashTicket: pgtIou]];
|
||||
key = [NSString stringWithFormat: @"cas-pgtiou:%@", pgtIou];
|
||||
casPgtId = [self valueForKey: key];
|
||||
/* we directly remove the value as it can only be used once anyway */
|
||||
if (casPgtId)
|
||||
@@ -669,7 +641,7 @@ static memcached_st *handle = NULL;
|
||||
forPGTIOU: (NSString *) pgtIou
|
||||
{
|
||||
[self setValue: pgtId
|
||||
forKey: [NSString stringWithFormat: @"cas-pgtiou:%@", [self sha512HashTicket: pgtIou]]];
|
||||
forKey: [NSString stringWithFormat: @"cas-pgtiou:%@", pgtIou]];
|
||||
}
|
||||
|
||||
- (void) removeCASSessionWithTicket: (NSString *) ticket
|
||||
@@ -677,7 +649,7 @@ static memcached_st *handle = NULL;
|
||||
NSString *key, *session;
|
||||
if ((session = [self CASSessionWithTicket: ticket]))
|
||||
{
|
||||
key = [NSString stringWithFormat: @"cas-ticket:%@", [self sha512HashTicket: ticket]];
|
||||
key = [NSString stringWithFormat: @"cas-ticket:%@", ticket];
|
||||
[self removeValueForKey: key];
|
||||
[self debugWithFormat: @"Removed CAS session: %@", session];
|
||||
}
|
||||
|
||||
@@ -100,19 +100,8 @@
|
||||
if (data) {
|
||||
if ([data isKindOfClass: [NSString class]])
|
||||
ASSIGN (content, data);
|
||||
else {
|
||||
char lastByte;
|
||||
[data getBytes:&lastByte range:NSMakeRange([data length]-1, 1)];
|
||||
if (lastByte == 0x0) {
|
||||
// string is null terminated
|
||||
ASSIGN (content, [NSString stringWithUTF8String: [data bytes]]);
|
||||
} else {
|
||||
// string is not null terminated
|
||||
NSString* myString = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
|
||||
ASSIGN (content, myString);
|
||||
[myString release];
|
||||
}
|
||||
}
|
||||
else
|
||||
ASSIGN (content, [NSString stringWithUTF8String:[data bytes]]);
|
||||
}
|
||||
data = [objectRecord objectForKey: @"c_version"];
|
||||
if (data)
|
||||
|
||||
@@ -29,8 +29,6 @@
|
||||
|
||||
@interface SOGoDAVAuthenticator : SoHTTPAuthenticator <SOGoAuthenticator>
|
||||
|
||||
WOContext *context;
|
||||
|
||||
+ (id) sharedSOGoDAVAuthenticator;
|
||||
|
||||
@end
|
||||
|
||||
@@ -41,19 +41,12 @@
|
||||
{
|
||||
static SOGoDAVAuthenticator *auth = nil;
|
||||
|
||||
if (!auth) {
|
||||
if (!auth)
|
||||
auth = [self new];
|
||||
context = nil;
|
||||
}
|
||||
|
||||
return auth;
|
||||
}
|
||||
|
||||
- (void) setContext: (WOContext *) _context
|
||||
{
|
||||
context = _context;
|
||||
}
|
||||
|
||||
- (BOOL) checkLogin: (NSString *) _login
|
||||
password: (NSString *) _pwd
|
||||
{
|
||||
@@ -75,10 +68,6 @@
|
||||
expire: &expire
|
||||
grace: &grace]
|
||||
&& perr == PolicyNoError);
|
||||
if (context) {
|
||||
[SOGoUser getEncryptedUsernameIfNeeded: [_login stringByReplacingString: @"%40"
|
||||
withString: @"@"] withContext: context]; // Create cache entry
|
||||
}
|
||||
if (!rc)
|
||||
{
|
||||
sd = [SOGoSystemDefaults sharedSystemDefaults];
|
||||
|
||||
@@ -123,8 +123,6 @@
|
||||
SOGoTemplatesFolderName = "Templates";
|
||||
SOGoMailComposeMessageType = "html";
|
||||
SOGoMailComposeFontSize = "0";
|
||||
SOGoMailHideInlineAttachments = NO;
|
||||
SOGoMailDisplayFullEmail = NO;
|
||||
SOGoMailDisplayRemoteInlineImages = "never";
|
||||
SOGoMailCertificateEnabled = YES;
|
||||
|
||||
|
||||
@@ -53,7 +53,6 @@
|
||||
- (BOOL) forwardEnabled;
|
||||
- (int) forwardConstraints;
|
||||
- (NSArray *) forwardConstraintsDomains;
|
||||
- (BOOL) notificationEnabled;
|
||||
- (BOOL) vacationEnabled;
|
||||
- (BOOL) vacationPeriodEnabled;
|
||||
- (NSString *) vacationDefaultSubject;
|
||||
|
||||
@@ -217,7 +217,7 @@
|
||||
|
||||
v = [self integerForKey: @"SOGoForwardConstraints"];
|
||||
|
||||
return (v > 3 ? 0 : v);
|
||||
return (v > 2 ? 0 : v);
|
||||
}
|
||||
|
||||
- (NSArray *) forwardConstraintsDomains
|
||||
@@ -225,11 +225,6 @@
|
||||
return [self stringArrayForKey: @"SOGoForwardConstraintsDomains"];
|
||||
}
|
||||
|
||||
- (BOOL) notificationEnabled
|
||||
{
|
||||
return [self boolForKey: @"SOGoNotificationEnabled"];
|
||||
}
|
||||
|
||||
- (BOOL) vacationEnabled
|
||||
{
|
||||
return [self boolForKey: @"SOGoVacationEnabled"];
|
||||
|
||||
@@ -150,7 +150,7 @@ static NSArray *childRecordFields = nil;
|
||||
if ([pathElements count] > 1)
|
||||
ocsName = [pathElements objectAtIndex: 1];
|
||||
else
|
||||
ocsName = @"personal";
|
||||
ocsName = [[context request] isMacOSXVenturaCalendarApp] ? @"Personal" : @"personal";
|
||||
|
||||
path = [NSString stringWithFormat: @"/Users/%@/%@/%@",
|
||||
login, [pathElements objectAtIndex: 0], ocsName];
|
||||
@@ -400,7 +400,14 @@ static NSArray *childRecordFields = nil;
|
||||
}
|
||||
|
||||
- (void) setOCSPath: (NSString *) _path
|
||||
{
|
||||
{
|
||||
// FIXME: Improve MacOSX Ventura support
|
||||
// Check if the problem will be fixed by Apple or if this fix should be kept in the future
|
||||
// Ticket #5639
|
||||
if ([[context request] isMacOSXVenturaCalendarApp]) {
|
||||
_path = [_path stringByReplacingOccurrencesOfString:@"/PERSONAL" withString:@"/personal"];
|
||||
}
|
||||
|
||||
if (![ocsPath isEqualToString:_path])
|
||||
{
|
||||
if (ocsPath)
|
||||
@@ -431,6 +438,12 @@ static NSArray *childRecordFields = nil;
|
||||
|
||||
cache = [SOGoCache sharedCache];
|
||||
record = [[cache valueForKey: _path] objectFromJSONString];
|
||||
// FIXME: Improve MacOSX Ventura support
|
||||
// Check if the problem will be fixed by Apple or if this fix should be kept in the future
|
||||
// Ticket #5639
|
||||
if ([[context request] isMacOSXVenturaCalendarApp]) {
|
||||
_path = [_path stringByReplacingOccurrencesOfString:@"PERSONAL" withString:@"personal"];
|
||||
}
|
||||
|
||||
// We check if we got a cache miss or a potentially bogus
|
||||
// entry from the cache
|
||||
@@ -458,6 +471,13 @@ static NSArray *childRecordFields = nil;
|
||||
|
||||
realNameInContainer = [self realNameInContainer];
|
||||
|
||||
// FIXME: Improve MacOSX Ventura support
|
||||
// Check if the problem will be fixed by Apple or if this fix should be kept in the future
|
||||
// Ticket #5639
|
||||
if ([[context request] isMacOSXVenturaCalendarApp]) {
|
||||
realNameInContainer = [realNameInContainer stringByReplacingOccurrencesOfString:@"PERSONAL" withString:@"personal"];
|
||||
}
|
||||
|
||||
return [NSString stringWithFormat: @"%@:%@/%@",
|
||||
owner,
|
||||
[container nameInContainer],
|
||||
@@ -533,7 +553,7 @@ static NSArray *childRecordFields = nil;
|
||||
publicParticle = @"";
|
||||
path = [NSString stringWithFormat: @"/%@/dav%@/%@/%@/%@/",
|
||||
appName, publicParticle,
|
||||
[[SOGoUser getEncryptedUsernameIfNeeded:[self ownerInContext: nil] withContext: context] stringByEscapingURL],
|
||||
[[self ownerInContext: nil] stringByEscapingURL],
|
||||
[[container nameInContainer] stringByEscapingURL],
|
||||
[[self realNameInContainer] stringByEscapingURL]];
|
||||
currentDavURL = [self davURL];
|
||||
@@ -709,7 +729,7 @@ static NSArray *childRecordFields = nil;
|
||||
{
|
||||
NSArray *records, *names;
|
||||
NSString *sqlFilter, *compFilter;
|
||||
EOQualifier *aclQualifier, *componentQualifier, *qualifier, *vlistExclusionQualifier;
|
||||
EOQualifier *aclQualifier, *componentQualifier, *qualifier;
|
||||
|
||||
sqlFilter = [self aclSQLListingFilter];
|
||||
if (sqlFilter)
|
||||
@@ -738,19 +758,8 @@ static NSArray *childRecordFields = nil;
|
||||
else
|
||||
qualifier = aclQualifier;
|
||||
|
||||
// For Thunderbird, disable contact list
|
||||
// This will be removed when VCARD will be implemented
|
||||
if ([[context request] isThunderbird] && [self isKindOfClass: NSClassFromString(@"SOGoContactGCSFolder")]) {
|
||||
vlistExclusionQualifier = [EOQualifier qualifierWithQualifierFormat: @"c_component != 'vlist'"];
|
||||
qualifier = [[[EOAndQualifier alloc] initWithQualifiers:
|
||||
vlistExclusionQualifier,
|
||||
qualifier,
|
||||
nil] autorelease];
|
||||
}
|
||||
|
||||
records = [[self ocsFolder] fetchFields: childRecordFields
|
||||
matchingQualifier: qualifier];
|
||||
|
||||
if (![records isNotNull])
|
||||
{
|
||||
[self errorWithFormat: @"(%s): fetch failed!", __PRETTY_FUNCTION__];
|
||||
@@ -1187,20 +1196,9 @@ static NSArray *childRecordFields = nil;
|
||||
{
|
||||
GCSFolder *folder;
|
||||
EOFetchSpecification *fetchSpec;
|
||||
EOQualifier *vlistExclusionQualifier;
|
||||
|
||||
folder = [self ocsFolder];
|
||||
|
||||
// For Thunderbird, disable contact list
|
||||
// This will be removed when VCARD will be implemented
|
||||
if ([[context request] isThunderbird] && [self isKindOfClass: NSClassFromString(@"SOGoContactGCSFolder")]) {
|
||||
vlistExclusionQualifier = [EOQualifier qualifierWithQualifierFormat: @"c_component != 'vlist'"];
|
||||
qualifier = [[[EOAndQualifier alloc] initWithQualifiers:
|
||||
vlistExclusionQualifier,
|
||||
qualifier,
|
||||
nil] autorelease];
|
||||
}
|
||||
|
||||
if (qualifier)
|
||||
fetchSpec = [EOFetchSpecification
|
||||
fetchSpecificationWithEntityName: [folder folderName]
|
||||
|
||||
@@ -1,45 +0,0 @@
|
||||
/* SOGoMobileProvision.h - this file is part of SOGo
|
||||
*
|
||||
* Copyright (C) 2023 Alinto
|
||||
*
|
||||
* This file is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2, or (at your option)
|
||||
* any later version.
|
||||
*
|
||||
* This file is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; see the file COPYING. If not, write to
|
||||
* the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
|
||||
* Boston, MA 02111-1307, USA.
|
||||
*/
|
||||
|
||||
#ifndef SOGOMOBILEPROVISION_H
|
||||
#define SOGOMOBILEPROVISION_H
|
||||
|
||||
#include <SOGo/SOGoObject.h>
|
||||
|
||||
@class SOGoMobileProvision;
|
||||
@class NSString;
|
||||
|
||||
typedef enum
|
||||
{
|
||||
ProvisioningTypeCalendar = 0,
|
||||
ProvisioningTypeContact = 1
|
||||
} ProvisioningType;
|
||||
|
||||
@interface SOGoMobileProvision : SOGoObject
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
+ (NSString *)plistForCalendarsWithContext:(WOContext *)context andPath:(NSString *)path andName:(NSString *)name;
|
||||
+ (NSString *)plistForContactsWithContext:(WOContext *)context andPath:(NSString *)path andName:(NSString *)name;
|
||||
|
||||
@end
|
||||
|
||||
#endif /* SOGOMOBILEPROVISION_H */
|
||||
@@ -1,131 +0,0 @@
|
||||
|
||||
/* SOGoMobileProvision.m - this file is part of SOGo
|
||||
*
|
||||
* Copyright (C) 2023 Alinto
|
||||
*
|
||||
* This file is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2, or (at your option)
|
||||
* any later version.
|
||||
*
|
||||
* This file is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; see the file COPYING. If not, write to
|
||||
* the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
|
||||
* Boston, MA 02111-1307, USA.
|
||||
*/
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
#import "SOGoMobileProvision.h"
|
||||
#import "SOGoUser.h"
|
||||
#import "NSString+Crypto.h"
|
||||
|
||||
@implementation SOGoMobileProvision
|
||||
|
||||
+ (NSString *)_plistWithContext:(WOContext *)context andPath:(NSString *)path andType:(ProvisioningType) provisioningType andName:(NSString *)name
|
||||
{
|
||||
NSData *plistData;
|
||||
SOGoUser *activeUser;
|
||||
NSURL *serverURL;
|
||||
NSDictionary *provisioning;
|
||||
NSError *error;
|
||||
NSString *payloadType, *prefix, *type;
|
||||
NSNumber *port;
|
||||
|
||||
activeUser = [context activeUser];
|
||||
serverURL = [context serverURL];
|
||||
|
||||
if (!context || !path) {
|
||||
[self errorWithFormat: @"Invalid provisioning profile - no parameters"];
|
||||
return nil;
|
||||
}
|
||||
|
||||
switch(provisioningType) {
|
||||
case ProvisioningTypeCalendar:
|
||||
payloadType = @"com.apple.caldav.account";
|
||||
prefix = @"CalDAV";
|
||||
type = @"Calendar";
|
||||
break;
|
||||
case ProvisioningTypeContact:
|
||||
payloadType = @"com.apple.carddav.account";
|
||||
prefix = @"CardDAV";
|
||||
type = @"Contact";
|
||||
break;
|
||||
}
|
||||
|
||||
port = [serverURL port];
|
||||
if (!port) {
|
||||
if ([[serverURL scheme] isEqualToString:@"https"]) {
|
||||
port = [NSNumber numberWithInt: 443];
|
||||
} else {
|
||||
port = [NSNumber numberWithInt: 80];
|
||||
}
|
||||
}
|
||||
|
||||
provisioning = [NSDictionary dictionaryWithObjectsAndKeys:
|
||||
[NSArray arrayWithObject: [NSDictionary dictionaryWithObjectsAndKeys:
|
||||
[NSString stringWithFormat:@"%@ %@", type, name], [NSString stringWithFormat:@"%@%@", prefix, @"AccountDescription"],
|
||||
[serverURL host], [NSString stringWithFormat:@"%@%@", prefix, @"HostName"],
|
||||
port, [NSString stringWithFormat:@"%@%@", prefix, @"Port"],
|
||||
path, [NSString stringWithFormat:@"%@%@", prefix, @"PrincipalURL"],
|
||||
[NSNumber numberWithBool:[[serverURL scheme] isEqualToString:@"https"]], [NSString stringWithFormat:@"%@%@", prefix, @"UseSSL"],
|
||||
[activeUser login], [NSString stringWithFormat:@"%@%@", prefix, @"Username"],
|
||||
[NSString stringWithFormat: @"SOGo %@ provisioning", prefix], @"PayloadDescription",
|
||||
[NSString stringWithFormat:@"%@ %@", type, name], @"PayloadDisplayName",
|
||||
[NSString stringWithFormat:@"%@.%@.apple.%@", [serverURL host], [name asMD5String], [prefix lowercaseString]], @"PayloadIdentifier",
|
||||
[serverURL host], @"PayloadOrganization",
|
||||
payloadType, @"PayloadType",
|
||||
[SOGoObject globallyUniqueObjectId], @"PayloadUUID",
|
||||
[NSNumber numberWithInt: 1], @"PayloadVersion",
|
||||
nil]], @"PayloadContent",
|
||||
[NSString stringWithFormat:@"SOGo %@ provisioning", prefix], @"PayloadDescription",
|
||||
[NSString stringWithFormat: @"%@ (%@)", name, type], @"PayloadDisplayName",
|
||||
[NSString stringWithFormat:@"%@.%@.%@.apple", [prefix lowercaseString], [serverURL host], [name asMD5String]], @"PayloadIdentifier",
|
||||
@"SOGo", @"PayloadOrganization",
|
||||
[NSNumber numberWithBool: NO], @"PayloadRemovalDisallowed",
|
||||
@"Configuration", @"PayloadType",
|
||||
[SOGoObject globallyUniqueObjectId], @"PayloadUUID",
|
||||
[NSNumber numberWithInt: 1], @"PayloadVersion",
|
||||
nil];
|
||||
|
||||
return [NSPropertyListSerialization dataWithPropertyList: provisioning
|
||||
format: NSPropertyListXMLFormat_v1_0
|
||||
options: 0
|
||||
error: &error];
|
||||
}
|
||||
|
||||
+ (NSString *)plistForCalendarsWithContext:(WOContext *)context andPath:(NSString *)path andName:(NSString *)name
|
||||
{
|
||||
NSData *plistData;
|
||||
SOGoUser *activeUser;
|
||||
|
||||
plistData = [self _plistWithContext: context andPath: path andType: ProvisioningTypeCalendar andName: name];
|
||||
if (nil != plistData) {
|
||||
return [[[NSString alloc] initWithData:plistData encoding:NSUTF8StringEncoding] autorelease];
|
||||
} else {
|
||||
[self errorWithFormat: @"Invalid provisioning profile plist for account : %@", [activeUser login]];
|
||||
return nil;
|
||||
}
|
||||
}
|
||||
|
||||
+ (NSString *)plistForContactsWithContext:(WOContext *)context andPath:(NSString *)path andName:(NSString *)name
|
||||
{
|
||||
NSData *plistData;
|
||||
SOGoUser *activeUser;
|
||||
|
||||
plistData = [self _plistWithContext: context andPath: path andType: ProvisioningTypeContact andName: name];
|
||||
if (nil != plistData) {
|
||||
return [[[NSString alloc] initWithData:plistData encoding:NSUTF8StringEncoding] autorelease];
|
||||
} else {
|
||||
[self errorWithFormat: @"Invalid provisioning profile plist for account : %@", [activeUser login]];
|
||||
return nil;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@end
|
||||
@@ -103,8 +103,7 @@
|
||||
|
||||
/* ownership */
|
||||
|
||||
- (NSString *)owner;
|
||||
- (void)setOwner:(NSString *)newOwner;
|
||||
- (void) setOwner: (NSString *) newOwner;
|
||||
- (NSString *) ownerInContext: (id) _ctx;
|
||||
|
||||
/* a helper that determines whether access rights can be ignored on
|
||||
|
||||
@@ -211,11 +211,6 @@
|
||||
|
||||
/* ownership */
|
||||
|
||||
- (NSString *) owner
|
||||
{
|
||||
return owner;
|
||||
}
|
||||
|
||||
- (void) setOwner: (NSString *) newOwner
|
||||
{
|
||||
NSString *uid;
|
||||
@@ -365,7 +360,7 @@
|
||||
NSString *usersUrl;
|
||||
|
||||
usersUrl = [NSString stringWithFormat: @"%@%@/",
|
||||
[[WOApplication application] davURLAsString], [SOGoUser getEncryptedUsernameIfNeeded: owner withContext: context]];
|
||||
[[WOApplication application] davURLAsString], owner];
|
||||
ownerHREF = davElementWithContent (@"href", XMLNS_WEBDAV, usersUrl);
|
||||
|
||||
return [davElementWithContent (@"owner", XMLNS_WEBDAV, ownerHREF)
|
||||
@@ -1227,7 +1222,7 @@
|
||||
davCurrentUserPrincipal = nil;
|
||||
else
|
||||
{
|
||||
s = [NSString stringWithFormat: @"/SOGo/dav/%@", [SOGoUser getEncryptedUsernameIfNeeded:login withContext:[self context]]];
|
||||
s = [NSString stringWithFormat: @"/SOGo/dav/%@", login];
|
||||
userHREF = davElementWithContent (@"href", XMLNS_WEBDAV, s);
|
||||
davCurrentUserPrincipal
|
||||
= [davElementWithContent (@"current-user-principal",
|
||||
|
||||
@@ -144,7 +144,7 @@ static SoSecurityManager *sm = nil;
|
||||
|
||||
- (NSString *) defaultFolderName
|
||||
{
|
||||
return @"Personal";
|
||||
return [[context request] isMacOSXVenturaCalendarApp] ? @"PERSONAL" : @"Personal";
|
||||
}
|
||||
|
||||
- (NSString *) collectedFolderName
|
||||
@@ -173,7 +173,7 @@ static SoSecurityManager *sm = nil;
|
||||
{
|
||||
if (folderType == SOGoPersonalFolder)
|
||||
{
|
||||
folderName = @"personal";
|
||||
folderName = [[context request] isMacOSXVenturaCalendarApp] ? @"PERSONAL" : @"personal";
|
||||
folder = [subFolderClass objectWithName: folderName inContainer: self];
|
||||
[folder setDisplayName: [self defaultFolderName]];
|
||||
[folder setOCSPath: [NSString stringWithFormat: @"%@/%@", OCSPath, folderName]];
|
||||
@@ -221,7 +221,15 @@ static SoSecurityManager *sm = nil;
|
||||
while ((row = [fc fetchAttributes: attrs withZone: NULL]))
|
||||
{
|
||||
key = [row objectForKey: @"c_path4"];
|
||||
|
||||
// FIXME: Improve MacOSX Ventura support
|
||||
// Check if the problem will be fixed by Apple or if this fix should be kept in the future
|
||||
// Ticket #5639
|
||||
if ([[context request] isMacOSXVenturaCalendarApp]) {
|
||||
if ([key isEqualToString:@"personal"]) {
|
||||
key = @"PERSONAL";
|
||||
}
|
||||
}
|
||||
|
||||
if ([key isKindOfClass: [NSString class]])
|
||||
{
|
||||
folder = [subFolderClass objectWithName: key inContainer: self];
|
||||
@@ -233,7 +241,7 @@ static SoSecurityManager *sm = nil;
|
||||
}
|
||||
if (folderType == SOGoPersonalFolder)
|
||||
{
|
||||
if (![subFolders objectForKey: @"personal"])
|
||||
if (![subFolders objectForKey: @"personal"] || ![subFolders objectForKey: @"Personal"] || ![subFolders objectForKey: @"PERSONAL"])
|
||||
[self createSpecialFolder: SOGoPersonalFolder];
|
||||
}
|
||||
else if (folderType == SOGoCollectedFolder)
|
||||
|
||||
@@ -33,7 +33,7 @@ static const NSString *POLICY_MIN_DIGIT = @"POLICY_MIN_DIGIT";
|
||||
static const NSString *POLICY_MIN_SPECIAL_SYMBOLS = @"POLICY_MIN_SPECIAL_SYMBOLS";
|
||||
static const NSString *POLICY_MIN_LENGTH = @"POLICY_MIN_LENGTH";
|
||||
|
||||
static const NSString *SPECIAL_SYMBOL_ALLOWED = @"%$&*(){}!?\\@#.,:;+=\\[\\]\\|<>\\/-_";
|
||||
static const NSString *SPECIAL_SYMBOL_ALLOWED = @"%$&*(){}!?\\@#.,:;+=";
|
||||
|
||||
@implementation SOGoPasswordPolicy
|
||||
|
||||
|
||||
@@ -32,11 +32,9 @@
|
||||
acquire: (BOOL) acquire
|
||||
{
|
||||
id userFolder;
|
||||
NSString *login;
|
||||
|
||||
login = [SOGoUser getDecryptedUsernameIfNeeded: key withContext: localContext];
|
||||
if ([key length] > 0 && [SOGoUser userWithLogin: login roles: nil])
|
||||
userFolder = [SOGoUserFolder objectWithName: login inContainer: self];
|
||||
if ([key length] > 0 && [SOGoUser userWithLogin: key roles: nil])
|
||||
userFolder = [SOGoUserFolder objectWithName: key inContainer: self];
|
||||
else
|
||||
userFolder = nil;
|
||||
|
||||
|
||||
@@ -27,7 +27,6 @@
|
||||
#import <GDLAccess/EOAdaptorChannel.h>
|
||||
#import <GDLAccess/EOAdaptorContext.h>
|
||||
#import <GDLAccess/EOAttribute.h>
|
||||
#import <SOGo/SOGoCache.h>
|
||||
|
||||
#import "SOGoSystemDefaults.h"
|
||||
|
||||
@@ -36,7 +35,6 @@
|
||||
static NSURL *tableURL = nil;
|
||||
static NSString *uidColumnName = @"c_uid";
|
||||
static EOAttribute *textColumn = nil;
|
||||
static const NSString *kCDefaultsLenKey = @"kCDefaultsLenKey";
|
||||
|
||||
@implementation SOGoSQLUserProfile
|
||||
|
||||
@@ -102,12 +100,10 @@ static const NSString *kCDefaultsLenKey = @"kCDefaultsLenKey";
|
||||
EOAdaptorChannel *channel;
|
||||
NSDictionary *row;
|
||||
NSException *ex;
|
||||
NSString *sql, *result;
|
||||
id value;
|
||||
NSString *sql, *value;
|
||||
NSArray *attrs;
|
||||
|
||||
value = nil;
|
||||
result = nil;
|
||||
|
||||
cm = [GCSChannelManager defaultChannelManager];
|
||||
channel = [cm acquireOpenChannelForURL: tableURL];
|
||||
@@ -138,12 +134,6 @@ static const NSString *kCDefaultsLenKey = @"kCDefaultsLenKey";
|
||||
value = [row objectForKey: fieldName];
|
||||
if (![value isNotNull])
|
||||
value = nil; /* we discard any NSNull instance */
|
||||
|
||||
if (value && [value isKindOfClass: [NSData class]]) {
|
||||
result = [NSString stringWithUTF8String: [value bytes]];
|
||||
} else if (value && [value isKindOfClass: [NSString class]]) {
|
||||
result = value;
|
||||
}
|
||||
}
|
||||
|
||||
[cm releaseChannel: channel];
|
||||
@@ -155,7 +145,7 @@ static const NSString *kCDefaultsLenKey = @"kCDefaultsLenKey";
|
||||
tableURL];
|
||||
}
|
||||
|
||||
return result;
|
||||
return value;
|
||||
}
|
||||
|
||||
- (NSString *) generateSQLForInsert: (NSString *) jsonRepresentation
|
||||
@@ -258,37 +248,22 @@ static const NSString *kCDefaultsLenKey = @"kCDefaultsLenKey";
|
||||
EOAdaptorContext *context;
|
||||
NSArray *attrs;
|
||||
NSDictionary *infos;
|
||||
SOGoCache *cache;
|
||||
NSNumberFormatter *formatter;
|
||||
|
||||
r = 65535;
|
||||
|
||||
cache = [SOGoCache sharedCache];
|
||||
if ([cache valueForKey:kCDefaultsLenKey]) {
|
||||
formatter = [[NSNumberFormatter alloc] init];
|
||||
formatter.numberStyle = NSNumberFormatterDecimalStyle;
|
||||
r = [[formatter numberFromString: [cache valueForKey:kCDefaultsLenKey]] unsignedLongLongValue];
|
||||
[formatter release];
|
||||
} else {
|
||||
cm = [GCSChannelManager defaultChannelManager];
|
||||
channel = [cm acquireOpenChannelForURL: tableURL];
|
||||
sql = [NSString stringWithFormat: @"select character_octet_length as CHARACTER_MAXIMUM_LENGTH from information_schema.columns where table_name = '%@' AND column_name = 'c_defaults'", [tableURL gcsTableName]];
|
||||
ex = [channel evaluateExpressionX: sql];
|
||||
|
||||
if (!ex) {
|
||||
attrs = [channel describeResults: NO];
|
||||
infos = [channel fetchAttributes: attrs withZone: NULL];
|
||||
[cm releaseChannel: channel immediately: YES];
|
||||
if (infos && [infos objectForKey:@"CHARACTER_MAXIMUM_LENGTH"]) {
|
||||
r = [[infos objectForKey:@"CHARACTER_MAXIMUM_LENGTH"] longLongValue];
|
||||
} else if (infos && [infos objectForKey:@"character_maximum_length"]) { // PGSQL case
|
||||
r = [[infos objectForKey:@"character_maximum_length"] longLongValue];
|
||||
}
|
||||
}
|
||||
|
||||
[cache setValue: [[NSNumber numberWithUnsignedLongLong: r] stringValue] forKey: kCDefaultsLenKey];
|
||||
cm = [GCSChannelManager defaultChannelManager];
|
||||
channel = [cm acquireOpenChannelForURL: tableURL];
|
||||
sql = [NSString stringWithFormat: @"select character_octet_length as CHARACTER_MAXIMUM_LENGTH from information_schema.columns where table_name = '%@' AND column_name = 'c_defaults'", [tableURL gcsTableName]];
|
||||
ex = [channel evaluateExpressionX: sql];
|
||||
if (!ex) {
|
||||
attrs = [channel describeResults: NO];
|
||||
infos = [channel fetchAttributes: attrs withZone: NULL];
|
||||
[cm releaseChannel: channel immediately: YES];
|
||||
if (infos && [infos objectForKey:@"CHARACTER_MAXIMUM_LENGTH"]) {
|
||||
r = [[infos objectForKey:@"CHARACTER_MAXIMUM_LENGTH"] longLongValue];
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
@@ -206,7 +206,7 @@ static NSString *sieveScriptName = @"sogo";
|
||||
@"imapflags", @"removeflag",
|
||||
@"imapflags", @"flag",
|
||||
@"vacation", @"vacation",
|
||||
@"notify", @"enotify",
|
||||
@"notify", @"notify",
|
||||
@"fileinto", @"fileinto",
|
||||
@"reject", @"reject",
|
||||
@"regex", @"regex",
|
||||
@@ -543,12 +543,6 @@ static NSString *sieveScriptName = @"sogo";
|
||||
else if ([method isEqualToString: @"redirect"])
|
||||
sieveAction = [NSString stringWithFormat: @"%@ %@",
|
||||
method, [argument asSieveQuotedString]];
|
||||
else if ([method isEqualToString: @"notify"])
|
||||
{
|
||||
argument = [NSString stringWithFormat: @"mailto:%@", argument];
|
||||
sieveAction = [NSString stringWithFormat: @"%@ %@",
|
||||
method, [argument asSieveQuotedString]];
|
||||
}
|
||||
else if ([method isEqualToString: @"reject"])
|
||||
sieveAction = [NSString stringWithFormat: @"%@ %@",
|
||||
method, [argument asSieveQuotedString]];
|
||||
@@ -869,7 +863,7 @@ static NSString *sieveScriptName = @"sogo";
|
||||
|
||||
error = nil;
|
||||
dd = [user domainDefaults];
|
||||
if (!([dd sieveScriptsEnabled] || [dd vacationEnabled] || [dd forwardEnabled] || [dd notificationEnabled]))
|
||||
if (!([dd sieveScriptsEnabled] || [dd vacationEnabled] || [dd forwardEnabled]))
|
||||
return error;
|
||||
|
||||
req = [NSMutableArray arrayWithCapacity: 15];
|
||||
@@ -1213,47 +1207,6 @@ static NSString *sieveScriptName = @"sogo";
|
||||
}
|
||||
}
|
||||
|
||||
// We handle mail notification
|
||||
values = [ud notificationOptions];
|
||||
|
||||
if (values && [[values objectForKey: @"enabled"] boolValue])
|
||||
{
|
||||
// BOOL alwaysSend;
|
||||
NSString *notify;
|
||||
NSString *message, *notificationTranslated;
|
||||
id addresses;
|
||||
int i;
|
||||
|
||||
// alwaysSend = [[values objectForKey: @"alwaysSend"] boolValue];
|
||||
b = YES;
|
||||
|
||||
[req addObjectUniquely: @"enotify"];
|
||||
[req addObjectUniquely: @"variables"];
|
||||
|
||||
addresses = [values objectForKey: @"notificationAddress"];
|
||||
if ([addresses isKindOfClass: [NSString class]])
|
||||
addresses = [addresses componentsSeparatedByString: @","];
|
||||
|
||||
message = [values objectForKey: @"notificationMessage"];
|
||||
notificationTranslated = [values objectForKey: @"notificationTranslated"];
|
||||
|
||||
for (i = 0; i < [addresses count]; i++)
|
||||
{
|
||||
v = [addresses objectAtIndex: i];
|
||||
if (v && [v length] > 0)
|
||||
{
|
||||
notify = @"if header :matches \"subject\" \"*\" {\r\n set \"subject\" \"${1}\";\r\n}\r\n";
|
||||
notify = [notify stringByAppendingFormat: @"set :encodeurl \"body_param\" \"%@\";\r\n", message];
|
||||
notify = [notify stringByAppendingFormat: @"notify :message \"%@: ${subject}\" \"mailto:%@?body=${body_param}\";\r\n", notificationTranslated, v];
|
||||
|
||||
// if (alwaysSend)
|
||||
// [script insertString: notify atIndex: 0];
|
||||
// else
|
||||
[script appendString: notify];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// We handle header/footer Sieve scripts
|
||||
if ((v = [dd sieveScriptHeaderTemplateFile]))
|
||||
{
|
||||
|
||||
@@ -75,11 +75,6 @@
|
||||
- (NSArray *) fetchContactsMatching: (NSString *) filter
|
||||
withCriteria: (NSArray *) criteria
|
||||
inDomain: (NSString *) domain;
|
||||
- (NSArray *)fetchContactsMatching:(NSString *)filter
|
||||
withCriteria:(NSArray *)criteria
|
||||
inDomain:(NSString *)domain
|
||||
limit:(int)limit;
|
||||
|
||||
- (NSArray *) lookupContactsWithQualifier: (EOQualifier *) qualifier
|
||||
andSortOrdering: (EOSortOrdering *) ordering
|
||||
inDomain: (NSString *) domain;
|
||||
|
||||
@@ -78,8 +78,6 @@ static const NSString *kDisableSharingCalendar = @"Calendar";
|
||||
- (NSString *) pageTitle;
|
||||
- (NSString *) helpURL;
|
||||
|
||||
NSComparisonResult languageSort(id el1, id el2, void *context);
|
||||
|
||||
- (NSArray *) supportedLanguages;
|
||||
- (NSString *) loginSuffix;
|
||||
|
||||
@@ -130,14 +128,6 @@ NSComparisonResult languageSort(id el1, id el2, void *context);
|
||||
- (NSString *) JWTSecret;
|
||||
|
||||
- (NSArray *) disableSharing;
|
||||
- (NSArray *) disableSharingAnyAuthUser;
|
||||
- (NSArray *) disableExport;
|
||||
|
||||
- (BOOL) enableGlobalAddressBookFirstEntries;
|
||||
- (int) globalAddressBookFirstEntriesCount;
|
||||
|
||||
- (BOOL)isURLEncryptionEnabled;
|
||||
- (NSString *)urlEncryptionPassphrase;
|
||||
|
||||
@end
|
||||
|
||||
|
||||
@@ -467,16 +467,6 @@ _injectConfigurationFromFile (NSMutableDictionary *defaultsDict,
|
||||
return [self stringForKey: @"SOGoHelpURL"];
|
||||
}
|
||||
|
||||
NSComparisonResult languageSort(id el1, id el2, void *context)
|
||||
{
|
||||
NSString *t1, *t2;
|
||||
|
||||
t1 = [context labelForKey: el1];
|
||||
t2 = [context labelForKey: el2];
|
||||
|
||||
return [t1 compare: t2 options: NSCaseInsensitiveSearch];
|
||||
}
|
||||
|
||||
- (NSArray *) supportedLanguages
|
||||
{
|
||||
static NSArray *supportedLanguages = nil;
|
||||
@@ -819,63 +809,5 @@ NSComparisonResult languageSort(id el1, id el2, void *context)
|
||||
return disableSharing;
|
||||
}
|
||||
|
||||
- (BOOL)isURLEncryptionEnabled {
|
||||
return [self boolForKey: @"SOGoURLEncryptionEnabled"];
|
||||
}
|
||||
|
||||
- (NSString *) urlEncryptionPassphrase
|
||||
{
|
||||
NSString *passphrase;
|
||||
|
||||
passphrase = [self stringForKey: @"SOGoURLEncryptionPassphrase"];
|
||||
|
||||
if (!passphrase)
|
||||
passphrase = @"SOGoSuperSecret0"; // Default passphrase
|
||||
|
||||
return passphrase;
|
||||
}
|
||||
|
||||
- (NSArray *) disableSharingAnyAuthUser
|
||||
{
|
||||
static NSArray *disableSharingAnyAuthUser = nil;
|
||||
|
||||
if (!disableSharingAnyAuthUser)
|
||||
{
|
||||
disableSharingAnyAuthUser = [self stringArrayForKey: @"SOGoDisableSharingAnyAuthUser"];
|
||||
[disableSharingAnyAuthUser retain];
|
||||
}
|
||||
|
||||
return disableSharingAnyAuthUser;
|
||||
}
|
||||
|
||||
- (NSArray *) disableExport
|
||||
{
|
||||
static NSArray *disableExport = nil;
|
||||
|
||||
if (!disableExport)
|
||||
{
|
||||
disableExport = [self stringArrayForKey: @"SOGoDisableExport"];
|
||||
[disableExport retain];
|
||||
}
|
||||
|
||||
return disableExport;
|
||||
}
|
||||
|
||||
- (BOOL) globalAddressBookFirstEntriesEnabled
|
||||
{
|
||||
return [self boolForKey: @"SOGoGlobalAddressBookFirstEntries"];
|
||||
}
|
||||
|
||||
- (int) globalAddressBookFirstEntriesCount
|
||||
{
|
||||
int v;
|
||||
|
||||
v = [self integerForKey: @"SOGoGlobalAddressBookFirstEntriesCount"];
|
||||
|
||||
if (!v)
|
||||
v = 100;
|
||||
|
||||
return v;
|
||||
}
|
||||
|
||||
@end
|
||||
@@ -135,10 +135,6 @@
|
||||
- (SOGoAppointmentFolder *) personalCalendarFolderInContext: (WOContext *) context;
|
||||
- (SOGoContactFolder *) personalContactsFolderInContext: (WOContext *) context;
|
||||
|
||||
/* Encryption */
|
||||
+ (NSString *)getEncryptedUsernameIfNeeded:(NSString *)username withContext:(WOContext *)context;
|
||||
+ (NSString *)getDecryptedUsernameIfNeeded:(NSString *)username withContext:(WOContext *)context;
|
||||
|
||||
@end
|
||||
|
||||
#endif /* __SOGoUser_H__ */
|
||||
@@ -39,7 +39,6 @@
|
||||
#import "SOGoUserManager.h"
|
||||
#import "SOGoUserSettings.h"
|
||||
#import "WOResourceManager+SOGo.h"
|
||||
#import "NSString+Crypto.h"
|
||||
|
||||
#if defined(MFA_CONFIG)
|
||||
#include <liboath/oath.h>
|
||||
@@ -47,8 +46,6 @@
|
||||
|
||||
#import "SOGoUser.h"
|
||||
|
||||
static const NSString *kEncryptedUserNamePrefix = @"uenc";
|
||||
|
||||
@implementation SoUser (SOGoExtension)
|
||||
|
||||
- (SOGoDomainDefaults *) userDefaults
|
||||
@@ -1063,7 +1060,7 @@ static const NSString *kEncryptedUserNamePrefix = @"uenc";
|
||||
|
||||
- (SOGoAppointmentFolder *) personalCalendarFolderInContext: (WOContext *) context
|
||||
{
|
||||
return [[self calendarsFolderInContext: context] lookupPersonalFolder: @"personal"
|
||||
return [[self calendarsFolderInContext: context] lookupPersonalFolder: [[context request] isMacOSXVenturaCalendarApp] ? @"PERSONAL" : @"personal"
|
||||
ignoringRights: YES];
|
||||
}
|
||||
|
||||
@@ -1200,84 +1197,4 @@ static const NSString *kEncryptedUserNamePrefix = @"uenc";
|
||||
return [accessValue boolValue];
|
||||
}
|
||||
|
||||
/* Encryption */
|
||||
+ (NSString *) getEncryptedUsernameIfNeeded:(NSString *)username withContext:(WOContext *)context
|
||||
{
|
||||
NSException *exception;
|
||||
NSString *tmp, *cacheKey;
|
||||
SOGoCache *cache;
|
||||
WORequest *request;
|
||||
|
||||
if (![[SOGoSystemDefaults sharedSystemDefaults] isURLEncryptionEnabled])
|
||||
return username;
|
||||
|
||||
request = [context request];
|
||||
cache = [SOGoCache sharedCache];
|
||||
cacheKey = [NSString stringWithFormat: @"%@_%@_%@", kEncryptedUserNamePrefix, username, [request requestHandlerKey]];
|
||||
|
||||
exception = nil;
|
||||
tmp = nil;
|
||||
|
||||
tmp = [cache valueForKey: cacheKey];
|
||||
if (tmp) {
|
||||
return tmp;
|
||||
} else {
|
||||
if (username && [username length] > 0) {
|
||||
tmp = [username encodeAES128ECBBase64: [[SOGoSystemDefaults sharedSystemDefaults] urlEncryptionPassphrase] encodedURL:YES exception: &exception];
|
||||
if (!exception) {
|
||||
[cache setValue:tmp forKey:cacheKey];
|
||||
return tmp;
|
||||
} else {
|
||||
[self errorWithFormat: @"URL Encryption error : %@", [exception reason]];
|
||||
return username;
|
||||
}
|
||||
} else {
|
||||
[self logWithFormat: @"Empty username for encryption"];
|
||||
return username;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+ (NSString *) getDecryptedUsernameIfNeeded:(NSString *)username withContext:(WOContext *)context
|
||||
{
|
||||
NSException *exception;
|
||||
NSString *tmp, *cacheKey;
|
||||
SOGoCache *cache;
|
||||
WORequest *request;
|
||||
|
||||
if (![[SOGoSystemDefaults sharedSystemDefaults] isURLEncryptionEnabled])
|
||||
return username;
|
||||
|
||||
request = [context request];
|
||||
cache = [SOGoCache sharedCache];
|
||||
cacheKey = [NSString stringWithFormat: @"%@_%@_%@", kEncryptedUserNamePrefix, username, [request requestHandlerKey]];
|
||||
exception = nil;
|
||||
tmp = nil;
|
||||
|
||||
tmp = [cache valueForKey: cacheKey];
|
||||
if (tmp) {
|
||||
return tmp;
|
||||
} else {
|
||||
if (username && [username length] > 0) {
|
||||
tmp = [username decodeAES128ECBBase64: [[SOGoSystemDefaults sharedSystemDefaults] urlEncryptionPassphrase] encodedURL:YES exception: &exception];
|
||||
if (tmp) {
|
||||
[cache setValue:tmp forKey:cacheKey];
|
||||
} else {
|
||||
[cache setValue:username forKey:cacheKey];
|
||||
}
|
||||
if (!exception) {
|
||||
return tmp;
|
||||
} else {
|
||||
[self errorWithFormat: @"URL Decryption error : %@", [exception reason]];
|
||||
return username;
|
||||
}
|
||||
} else {
|
||||
[self errorWithFormat: @"Empty username for decryption"];
|
||||
return username;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@end /* SOGoUser */
|
||||
@@ -156,9 +156,6 @@ extern NSString *SOGoPasswordRecoverySecondaryEmail;
|
||||
- (void) setMailComposeFontSize: (int) newValue;
|
||||
- (int) mailComposeFontSize;
|
||||
|
||||
- (void) setMailDisplayFullEmail: (BOOL *) newValue;
|
||||
- (BOOL *) mailDisplayFullEmail;
|
||||
|
||||
- (void) setMailDisplayRemoteInlineImages: (NSString *) newValue;
|
||||
- (NSString *) mailDisplayRemoteInlineImages;
|
||||
|
||||
@@ -225,17 +222,11 @@ extern NSString *SOGoPasswordRecoverySecondaryEmail;
|
||||
- (void) setForwardOptions: (NSDictionary *) newValue;
|
||||
- (NSDictionary *) forwardOptions;
|
||||
|
||||
- (void) setNotificationOptions: (NSDictionary *) newValue;
|
||||
- (NSDictionary *) notificationOptions;
|
||||
|
||||
- (void) setMailLabelsColors: (NSDictionary *) newValues;
|
||||
- (NSDictionary *) mailLabelsColors;
|
||||
|
||||
- (void)setHideInlineAttachments:(BOOL)newValue;
|
||||
- (BOOL)hideInlineAttachments;
|
||||
|
||||
/* calendar */
|
||||
- (void)setCalendarCategories : (NSArray *)newValues;
|
||||
- (void) setCalendarCategories: (NSArray *) newValues;
|
||||
- (NSArray *) calendarCategories;
|
||||
|
||||
- (void) setCalendarCategoriesColors: (NSDictionary *) newValues;
|
||||
|
||||
@@ -631,16 +631,6 @@ NSString *SOGoPasswordRecoverySecondaryEmail = @"SecondaryEmail";
|
||||
return [self integerForKey: @"SOGoMailComposeFontSize"];
|
||||
}
|
||||
|
||||
- (void) setMailDisplayFullEmail: (BOOL *) newValue
|
||||
{
|
||||
[self setBool: newValue forKey: @"SOGoMailDisplayFullEmail"];
|
||||
}
|
||||
|
||||
- (BOOL *) mailDisplayFullEmail;
|
||||
{
|
||||
return [self boolForKey: @"SOGoMailDisplayFullEmail"];
|
||||
}
|
||||
|
||||
- (void) setMailDisplayRemoteInlineImages: (NSString *) newValue
|
||||
{
|
||||
[self setObject: newValue forKey: @"SOGoMailDisplayRemoteInlineImages"];
|
||||
@@ -969,16 +959,6 @@ NSString *SOGoPasswordRecoverySecondaryEmail = @"SecondaryEmail";
|
||||
return [self objectForKey: @"SOGoMailLabelsColors"];
|
||||
}
|
||||
|
||||
- (void) setHideInlineAttachments: (BOOL) newValue
|
||||
{
|
||||
[self setBool: newValue forKey: @"SOGoMailHideInlineAttachments"];
|
||||
}
|
||||
|
||||
- (BOOL) hideInlineAttachments
|
||||
{
|
||||
return [self boolForKey: @"SOGoMailHideInlineAttachments"];
|
||||
}
|
||||
|
||||
- (void) setSieveFilters: (NSArray *) newValue
|
||||
{
|
||||
[self setObject: newValue forKey: @"SOGoSieveFilters"];
|
||||
@@ -1009,16 +989,6 @@ NSString *SOGoPasswordRecoverySecondaryEmail = @"SecondaryEmail";
|
||||
return [self dictionaryForKey: @"Forward"];
|
||||
}
|
||||
|
||||
- (void) setNotificationOptions: (NSDictionary *) newValue
|
||||
{
|
||||
[self setObject: newValue forKey: @"Notification"];
|
||||
}
|
||||
|
||||
- (NSDictionary *) notificationOptions
|
||||
{
|
||||
return [self dictionaryForKey: @"Notification"];
|
||||
}
|
||||
|
||||
- (void) setContactsCategories: (NSArray *) newValues
|
||||
{
|
||||
[self setObject: newValues forKey: @"SOGoContactsCategories"];
|
||||
|
||||
@@ -24,8 +24,6 @@
|
||||
#import <NGObjWeb/WOContext+SoObjects.h>
|
||||
#import <NGObjWeb/WORequest+So.h>
|
||||
|
||||
#import <SaxObjC/XMLNamespaces.h>
|
||||
|
||||
#import <NGExtensions/NSObject+Logs.h>
|
||||
#import <NGExtensions/NSString+misc.h>
|
||||
#import <DOM/DOMNode.h>
|
||||
@@ -38,7 +36,6 @@
|
||||
#import "NSArray+Utilities.h"
|
||||
#import "NSDictionary+Utilities.h"
|
||||
#import "NSString+Utilities.h"
|
||||
#import "NSString+Crypto.h"
|
||||
#import "SOGoUserManager.h"
|
||||
#import "SOGoPermissions.h"
|
||||
#import "SOGoSystemDefaults.h"
|
||||
@@ -86,16 +83,6 @@
|
||||
return children;
|
||||
}
|
||||
|
||||
- (NSString *) nameInContainer {
|
||||
return [SOGoUser getEncryptedUsernameIfNeeded: [super nameInContainer] withContext: context];
|
||||
}
|
||||
|
||||
- (NSString *) davURLAsString
|
||||
{
|
||||
return [[container davURLAsString]
|
||||
stringByAppendingFormat: @"%@/", [self nameInContainer]];
|
||||
}
|
||||
|
||||
/* ownership */
|
||||
|
||||
- (NSString *) ownerInContext: (WOContext *) _ctx
|
||||
|
||||
@@ -924,22 +924,6 @@
|
||||
- (NSArray *) fetchContactsMatching: (NSString *) filter
|
||||
withCriteria: (NSArray *) criteria
|
||||
inDomain: (NSString *) domain
|
||||
|
||||
{
|
||||
if ([filter length] > 0) {
|
||||
return [self fetchContactsMatching: (NSString *) filter
|
||||
withCriteria: (NSArray *) criteria
|
||||
inDomain: (NSString *) domain
|
||||
limit: -1];
|
||||
} else {
|
||||
return [NSMutableArray array];
|
||||
}
|
||||
}
|
||||
|
||||
- (NSArray *)fetchContactsMatching: (NSString *)filter
|
||||
withCriteria: (NSArray *)criteria
|
||||
inDomain: (NSString *)domain
|
||||
limit: (int)limit
|
||||
{
|
||||
EOAdaptorChannel *channel;
|
||||
NSEnumerator *criteriaList;
|
||||
@@ -1010,8 +994,6 @@
|
||||
[sql appendFormat: @" AND %@ IS NULL", _domainField];
|
||||
}
|
||||
|
||||
if (limit > 0)
|
||||
[sql appendFormat: @" ORDER BY c_cn ASC LIMIT %i", limit];
|
||||
ex = [channel evaluateExpressionX: sql];
|
||||
if (!ex)
|
||||
{
|
||||
|
||||
@@ -33,10 +33,9 @@
|
||||
- (BOOL) isICal;
|
||||
- (BOOL) isICal4;
|
||||
- (BOOL) isMacOSXAddressBookApp;
|
||||
- (BOOL) isMacOSXCalendarApp;
|
||||
- (BOOL) isIPhoneAddressBookApp;
|
||||
- (BOOL)isMacOSXCalendarApp;
|
||||
- (BOOL)isIPhoneAddressBookApp;
|
||||
- (BOOL) isAndroid;
|
||||
- (BOOL) isMacOSXCalendar;
|
||||
|
||||
@end
|
||||
|
||||
|
||||
@@ -242,7 +242,7 @@
|
||||
return ([[cc userAgent] rangeOfString: @"Android"].location != NSNotFound);
|
||||
}
|
||||
|
||||
- (BOOL) isMacOSXCalendar
|
||||
- (BOOL) isMacOSXVenturaCalendarApp
|
||||
{
|
||||
WEClientCapabilities *cc;
|
||||
BOOL b;
|
||||
@@ -251,8 +251,7 @@
|
||||
|
||||
b = (
|
||||
(
|
||||
nil != [cc userAgent]
|
||||
&& [[cc userAgent] rangeOfString: @"macOS"].location != NSNotFound
|
||||
nil != [cc userAgent] && [[cc userAgent] rangeOfString: @"macOS/13"].location != NSNotFound
|
||||
&& [[cc userAgent] rangeOfString: @"dataaccessd"].location != NSNotFound
|
||||
)
|
||||
);
|
||||
@@ -260,11 +259,4 @@
|
||||
return b;
|
||||
}
|
||||
|
||||
- (BOOL) isThunderbird
|
||||
{
|
||||
BOOL ret;
|
||||
ret = ([[self headerForKey:@"user-agent"] rangeOfString: @"Thunderbird"].location != NSNotFound);
|
||||
return ret;
|
||||
}
|
||||
|
||||
@end
|
||||
@@ -105,7 +105,6 @@
|
||||
testEquals([[NSString stringWithString:@"<img vbscript:test"] stringWithoutHTMLInjection: NO], @"<img test");
|
||||
testEquals([[NSString stringWithString:@"<img javascript:test"] stringWithoutHTMLInjection: NO], @"<img test");
|
||||
testEquals([[NSString stringWithString:@"<img livescript:test"] stringWithoutHTMLInjection: NO], @"<img test");
|
||||
testEquals([[NSString stringWithString:@"foobar <form action=\"\">bar</form>"] stringWithoutHTMLInjection: NO], @"foobar <for* action=\"\">bar</for*>");
|
||||
testEquals([[NSString stringWithString:@"foobar <iframe src=\"\">bar</iframe>"] stringWithoutHTMLInjection: NO], @"foobar <ifr*** src=\"\">bar</iframe>");
|
||||
testEquals([[NSString stringWithString:@"foobar <img onload=foo bar"] stringWithoutHTMLInjection: NO], @"foobar <img onl***=foo bar");
|
||||
testEquals([[NSString stringWithString:@"foobar <img onmouseover=foo bar"] stringWithoutHTMLInjection: NO], @"foobar <img onmouseo***=foo bar");
|
||||
|
||||
@@ -45,7 +45,6 @@ class ManageSieve {
|
||||
base64 = buff.toString('base64')
|
||||
response = await this.connection.send(`AUTHENTICATE "PLAIN" {${base64.length}+}\r\n${base64}`, { waitfor: /\b(OK|NO)/ })
|
||||
// console.debug(`ManageSieve.authenticate => ${response}`)
|
||||
// console.log(`AUTHENTICATE "PLAIN" {${base64.length}+}\r\n${base64}`)
|
||||
parsedResponse = this.parseResponse(response)
|
||||
if (!Object.keys(parsedResponse).includes('OK')) {
|
||||
throw new Error(`Authentication failed: ${parsedResponse['NO']}`)
|
||||
|
||||
@@ -933,5 +933,5 @@ describe('CalDAV Scheduling', function() {
|
||||
|
||||
vcalendarInvitationAttendee = await _getEvent(webdavAttendee1, attendee1Calendar, icsName, 404)
|
||||
vcalendarInvitationDelegate = await _getEvent(webdavAttendee1Delegate, attendee1DelegateCalendar, icsName, 404)
|
||||
}, 10000) // increase timeout for this long test
|
||||
})
|
||||
})
|
||||
@@ -232,7 +232,7 @@ describe('CardDAV extensions', function() {
|
||||
expect(emails)
|
||||
.withContext(`Returned vCard has email of ${config.attendee1_username} (${config.attendee1})`)
|
||||
.toContain(config.attendee1)
|
||||
}, 10000) // increase timeout for this long test
|
||||
})
|
||||
|
||||
// CARDDAV:addressbook-multiget Report
|
||||
// https://datatracker.ietf.org/doc/html/rfc6352#section-8.7
|
||||
|
||||
@@ -229,5 +229,5 @@ describe('Apple iCal', function() {
|
||||
.toBe(0)
|
||||
|
||||
await webdav_su.deleteObject(otherResource)
|
||||
}, 10000) // increase timeout for this long test
|
||||
})
|
||||
})
|
||||
@@ -9,7 +9,6 @@ let prefs, webdav, utility, manageSieve, user
|
||||
describe('Sieve', function() {
|
||||
|
||||
async function _getSogoSieveScript() {
|
||||
await manageSieve.authenticate(true)
|
||||
const scripts = await manageSieve.listScripts()
|
||||
|
||||
expect(Object.keys(scripts))
|
||||
@@ -60,6 +59,7 @@ describe('Sieve', function() {
|
||||
|
||||
beforeEach(async function() {
|
||||
await _killFilters()
|
||||
await manageSieve.authenticate(true)
|
||||
})
|
||||
|
||||
afterAll(async function() {
|
||||
|
||||
+70
-70
@@ -221,95 +221,95 @@
|
||||
count = [objects count];
|
||||
|
||||
for (i = 0; i < count; i++)
|
||||
{
|
||||
content = [[[objects objectAtIndex: i] objectForKey: @"c_content"] stringByTrimmingSpaces];
|
||||
c_name = [[objects objectAtIndex: i] objectForKey: @"c_name"];
|
||||
if (is_calendar)
|
||||
{
|
||||
// We check for
|
||||
// BEGIN:VCALENDAR
|
||||
// ..
|
||||
// END:VCALENDAR
|
||||
iCalCalendar *calendar;
|
||||
{
|
||||
content = [[[objects objectAtIndex: i] objectForKey: @"c_content"] stringByTrimmingSpaces];
|
||||
c_name = [[objects objectAtIndex: i] objectForKey: @"c_name"];
|
||||
if (is_calendar)
|
||||
{
|
||||
// We check for
|
||||
// BEGIN:VCALENDAR
|
||||
// ..
|
||||
// END:VCALENDAR
|
||||
iCalCalendar *calendar;
|
||||
|
||||
if ([content length] < 30 ||
|
||||
if ([content length] < 30 ||
|
||||
[[content substringToIndex: 15] caseInsensitiveCompare: @"BEGIN:VCALENDAR"] != NSOrderedSame ||
|
||||
[[content substringFromIndex: [content length]-13] caseInsensitiveCompare: @"END:VCALENDAR"] != NSOrderedSame)
|
||||
{
|
||||
NSLog(@"Corrupted calendar item (missing tags) in path %@ with c_name = %@", folder, c_name);
|
||||
if (delete)
|
||||
[gcsFolder deleteContentWithName: c_name];
|
||||
[gcsFolder deleteContentWithName: c_name];
|
||||
rc = NO;
|
||||
}
|
||||
else
|
||||
else
|
||||
{
|
||||
calendar = [iCalCalendar parseSingleFromSource: content];
|
||||
if (!calendar)
|
||||
{
|
||||
NSLog(@"Corrupted calendar item (unparsable) in path %@ with c_name = %@", folder, c_name);
|
||||
if (delete)
|
||||
[gcsFolder deleteContentWithName: c_name];
|
||||
rc = NO;
|
||||
}
|
||||
else
|
||||
{
|
||||
iCalEvent *event;
|
||||
{
|
||||
NSLog(@"Corrupted calendar item (unparsable) in path %@ with c_name = %@", folder, c_name);
|
||||
if (delete)
|
||||
[gcsFolder deleteContentWithName: c_name];
|
||||
rc = NO;
|
||||
}
|
||||
else
|
||||
{
|
||||
iCalEvent *event;
|
||||
|
||||
event = (iCalEvent *) [calendar firstChildWithTag: @"vevent"];
|
||||
if (event)
|
||||
{
|
||||
iCalDateTime *startDate, *endDate;
|
||||
event = (iCalEvent *) [calendar firstChildWithTag: @"vevent"];
|
||||
if (event)
|
||||
{
|
||||
iCalDateTime *startDate, *endDate;
|
||||
|
||||
startDate = (iCalDateTime *) [event uniqueChildWithTag: @"dtstart"];
|
||||
if (![startDate dateTime])
|
||||
{
|
||||
NSLog(@"Missing start date of event in path %@ with c_name = %@ (%@)", folder, c_name, [event summary]);
|
||||
if (delete)
|
||||
[gcsFolder deleteContentWithName: c_name];
|
||||
rc = NO;
|
||||
}
|
||||
endDate = (iCalDateTime *) [event uniqueChildWithTag: @"dtend"];
|
||||
if (![endDate dateTime] && ![event hasDuration])
|
||||
{
|
||||
NSLog(@"Missing end date of event in path %@ with c_name = %@ (%@)", folder, c_name, [event summary]);
|
||||
if (delete)
|
||||
[gcsFolder deleteContentWithName: c_name];
|
||||
rc = NO;
|
||||
}
|
||||
if ([startDate dateTime] && [endDate dateTime])
|
||||
{
|
||||
NSComparisonResult comparison;
|
||||
startDate = (iCalDateTime *) [event uniqueChildWithTag: @"dtstart"];
|
||||
if (![startDate dateTime])
|
||||
{
|
||||
NSLog(@"Missing start date of event in path %@ with c_name = %@ (%@)", folder, c_name, [event summary]);
|
||||
if (delete)
|
||||
[gcsFolder deleteContentWithName: c_name];
|
||||
rc = NO;
|
||||
}
|
||||
endDate = (iCalDateTime *) [event uniqueChildWithTag: @"dtend"];
|
||||
if (![endDate dateTime] && ![event hasDuration])
|
||||
{
|
||||
NSLog(@"Missing end date of event in path %@ with c_name = %@ (%@)", folder, c_name, [event summary]);
|
||||
if (delete)
|
||||
[gcsFolder deleteContentWithName: c_name];
|
||||
rc = NO;
|
||||
}
|
||||
if ([startDate dateTime] && [endDate dateTime])
|
||||
{
|
||||
NSComparisonResult comparison;
|
||||
|
||||
comparison = [[startDate dateTime] compare: [endDate dateTime]];
|
||||
if (([event isAllDay] && comparison == NSOrderedDescending) ||
|
||||
(![event isAllDay] && comparison != NSOrderedAscending))
|
||||
{
|
||||
NSLog(@"Start date (%@) is not before end date (%@) for event in path %@ with c_name = %@ (%@)",
|
||||
[startDate dateTime], [endDate dateTime], folder, c_name, [event summary]);
|
||||
if (delete)
|
||||
[gcsFolder deleteContentWithName: c_name];
|
||||
rc = NO;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
comparison = [[startDate dateTime] compare: [endDate dateTime]];
|
||||
if (([event isAllDay] && comparison == NSOrderedDescending) ||
|
||||
(![event isAllDay] && comparison != NSOrderedAscending))
|
||||
{
|
||||
NSLog(@"Start date (%@) is not before end date (%@) for event in path %@ with c_name = %@ (%@)",
|
||||
[startDate dateTime], [endDate dateTime], folder, c_name, [event summary]);
|
||||
if (delete)
|
||||
[gcsFolder deleteContentWithName: c_name];
|
||||
rc = NO;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
NGVCard *card;
|
||||
}
|
||||
else
|
||||
{
|
||||
NGVCard *card;
|
||||
|
||||
card = [NGVCard parseSingleFromSource: content];
|
||||
card = [NGVCard parseSingleFromSource: content];
|
||||
|
||||
if (!card)
|
||||
if (!card)
|
||||
{
|
||||
NSLog(@"Corrupted card item (unparsable) in path %@ with c_name = %@", folder, c_name);
|
||||
if (delete)
|
||||
[gcsFolder deleteContentWithName: c_name];
|
||||
rc = NO;
|
||||
NSLog(@"Corrupted card item (unparsable) in path %@ with c_name = %@", folder, c_name);
|
||||
if (delete)
|
||||
[gcsFolder deleteContentWithName: c_name];
|
||||
rc = NO;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
@@ -138,14 +138,7 @@
|
||||
sessionExpireMinutes = [[arguments objectAtIndex: 0] intValue];
|
||||
}
|
||||
|
||||
NSLog(@"Remove all sessions older than %d min", sessionExpireMinutes);
|
||||
|
||||
if (sessionExpireMinutes == 0 && ![[arguments objectAtIndex: 0] isEqualToString:@"0"])
|
||||
{
|
||||
//If the input is not a number intValue return 0 so we check that's really the case
|
||||
[self usage];
|
||||
}
|
||||
else if (sessionExpireMinutes >= 0)
|
||||
if (sessionExpireMinutes >= 0)
|
||||
{
|
||||
rc = [self expireUserSessionOlderThan: sessionExpireMinutes];
|
||||
}
|
||||
|
||||
@@ -113,13 +113,13 @@ typedef enum
|
||||
" remove remove all ACL information of folder for user\n"
|
||||
" subscribe subscribe user to owner's folder\n"
|
||||
" unsubscribe unsubscribe user to owner's folder\n"
|
||||
" owner the user owning the folder\n"
|
||||
" owner the user owning the folder\n"
|
||||
" folder the folder - Calendar/<ID> or Contacts/<ID>\n"
|
||||
" user the user (or group without the @ prefix) to get/set rights for - 'ALL', '<default>', 'anonymous' are supported\n"
|
||||
" rights rights to add\n\n"
|
||||
"Example: sogo-tool manage-acl get jdoe Calendar/personal ALL\n\n"
|
||||
"Note: You can add only one access right at the time. To set them all at once,\n"
|
||||
" invoke 'remove' first to remove them all.\n\n");
|
||||
" rights rights to add\n\n"
|
||||
"Example: sogo-tool manage-acl get jdoe Calendar/personal ALL\n\n"
|
||||
"Note: You can add only one access right at the time. To set them all at once,\n"
|
||||
" invoke 'remove' first to remove them all.\n\n");
|
||||
}
|
||||
|
||||
- (BOOL) parseArguments
|
||||
|
||||
@@ -86,15 +86,15 @@
|
||||
/* we want to match the field value case-insensitively */
|
||||
recordEmail = [[record objectForKey: field] uppercaseString];
|
||||
if ([recordEmail length])
|
||||
{
|
||||
recordList = [doubleEmails objectForKey: recordEmail];
|
||||
if (!recordList)
|
||||
{
|
||||
recordList = [NSMutableArray arrayWithCapacity: 5];
|
||||
[doubleEmails setObject: recordList forKey: recordEmail];
|
||||
recordList = [doubleEmails objectForKey: recordEmail];
|
||||
if (!recordList)
|
||||
{
|
||||
recordList = [NSMutableArray arrayWithCapacity: 5];
|
||||
[doubleEmails setObject: recordList forKey: recordEmail];
|
||||
}
|
||||
[recordList addObject: record];
|
||||
}
|
||||
[recordList addObject: record];
|
||||
}
|
||||
}
|
||||
|
||||
- (void) cleanupSingleRecords: (NSMutableDictionary *) doubleEmails
|
||||
@@ -220,20 +220,21 @@
|
||||
|
||||
recordsToRemove = [NSMutableArray arrayWithCapacity: (max - 1)];
|
||||
for (count = 0; count < max; count++)
|
||||
{
|
||||
if (count != keptRecord)
|
||||
{
|
||||
currentRecord = [records objectAtIndex: count];
|
||||
[recordsToRemove addObject: [currentRecord objectForKey: @"c_name"]];
|
||||
if (count != keptRecord)
|
||||
{
|
||||
currentRecord = [records objectAtIndex: count];
|
||||
[recordsToRemove
|
||||
addObject: [currentRecord objectForKey: @"c_name"]];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return recordsToRemove;
|
||||
}
|
||||
|
||||
- (NSArray *) records: (NSArray *) records
|
||||
withLowestScores: (unsigned int *) scores
|
||||
count: (unsigned int) max
|
||||
count: (unsigned int) max
|
||||
{
|
||||
unsigned int count, highestScore;
|
||||
int highestScoreRecord;
|
||||
@@ -241,13 +242,13 @@
|
||||
highestScore = 0;
|
||||
highestScoreRecord = -1;
|
||||
for (count = 0; count < max; count++)
|
||||
{
|
||||
if (scores[count] > highestScore)
|
||||
{
|
||||
highestScore = scores[count];
|
||||
highestScoreRecord = count;
|
||||
if (scores[count] > highestScore)
|
||||
{
|
||||
highestScore = scores[count];
|
||||
highestScoreRecord = count;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (highestScoreRecord == -1)
|
||||
highestScoreRecord = 0;
|
||||
@@ -267,15 +268,16 @@
|
||||
|
||||
highestVersion = 0;
|
||||
for (count = 0; count < max; count++)
|
||||
{
|
||||
currentVersion = [[records objectAtIndex: count] objectForKey: @"c_version"];
|
||||
version = [currentVersion intValue];
|
||||
if (version > highestVersion)
|
||||
{
|
||||
mostModified = count;
|
||||
highestVersion = version;
|
||||
currentVersion
|
||||
= [[records objectAtIndex: count] objectForKey: @"c_version"];
|
||||
version = [currentVersion intValue];
|
||||
if (version > highestVersion)
|
||||
{
|
||||
mostModified = count;
|
||||
highestVersion = version;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return mostModified;
|
||||
}
|
||||
@@ -289,25 +291,25 @@
|
||||
amount = 0;
|
||||
|
||||
if (!quickFields)
|
||||
{
|
||||
quickFields = [NSArray arrayWithObjects: @"c_givenname", @"c_cn",
|
||||
@"c_sn", @"c_screenname", @"c_l", @"c_mail",
|
||||
@"c_o", @"c_ou", @"c_telephonenumber", nil];
|
||||
[quickFields retain];
|
||||
}
|
||||
{
|
||||
quickFields = [NSArray arrayWithObjects: @"c_givenname", @"c_cn",
|
||||
@"c_sn", @"c_screenname", @"c_l", @"c_mail",
|
||||
@"c_o", @"c_ou", @"c_telephonenumber", nil];
|
||||
[quickFields retain];
|
||||
}
|
||||
|
||||
max = [quickFields count];
|
||||
for (count = 0; count < max; count++)
|
||||
{
|
||||
value = [record objectForKey: [quickFields objectAtIndex: count]];
|
||||
if ([value isKindOfClass: [NSString class]])
|
||||
{
|
||||
if ([value length])
|
||||
amount++;
|
||||
value = [record objectForKey: [quickFields objectAtIndex: count]];
|
||||
if ([value isKindOfClass: [NSString class]])
|
||||
{
|
||||
if ([value length])
|
||||
amount++;
|
||||
}
|
||||
else if ([value isKindOfClass: [NSNumber class]])
|
||||
amount++;
|
||||
}
|
||||
else if ([value isKindOfClass: [NSNumber class]])
|
||||
amount++;
|
||||
}
|
||||
|
||||
return amount;
|
||||
}
|
||||
@@ -321,14 +323,15 @@
|
||||
|
||||
highestQFields = 0;
|
||||
for (count = 0; count < max; count++)
|
||||
{
|
||||
currentQFields = [self amountOfFilledQuickFields: [records objectAtIndex: count]];
|
||||
if (currentQFields > highestQFields)
|
||||
{
|
||||
mostQuickFields = count;
|
||||
highestQFields = currentQFields;
|
||||
currentQFields
|
||||
= [self amountOfFilledQuickFields: [records objectAtIndex: count]];
|
||||
if (currentQFields > highestQFields)
|
||||
{
|
||||
mostQuickFields = count;
|
||||
highestQFields = currentQFields;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return mostQuickFields;
|
||||
}
|
||||
@@ -396,28 +399,10 @@
|
||||
}
|
||||
|
||||
- (void) assignScores: (unsigned int *) scores
|
||||
toRecords: (NSArray *) records
|
||||
count: (unsigned int) max
|
||||
toRecords: (NSArray *) records
|
||||
count: (unsigned int) max
|
||||
withCardsInLists: (NSArray *) cardsInLists
|
||||
{
|
||||
/*
|
||||
Records is an Array of record which are duplicates of each other.
|
||||
The goal here is to know which one to keep and whoch ones to discard.
|
||||
We will assign a score to each record, the one with the best scores is kept
|
||||
Record which has been the last modified: +1
|
||||
Record has the most content: +2
|
||||
Record has the most quick field set: +3
|
||||
Record is in a list: +6
|
||||
|
||||
If two record have the same, for exemple, content. It's the first one on the list
|
||||
that will get the points.
|
||||
If two recors have the same score. t's the first one on the list
|
||||
that will get the points.
|
||||
|
||||
quick fiels are =("c_givenname": Firstname, @"c_cn": Display,
|
||||
@"c_sn": LastName, @"c_screenname": Screen Name @"c_l": City, @"c_mail": mails,
|
||||
@"c_o": organisation, @"c_ou": organisation unit, @"c_telephonenumber": telephone)
|
||||
*/
|
||||
int recordIndex;
|
||||
|
||||
recordIndex = [self mostModifiedRecord: records count: max];
|
||||
|
||||
@@ -634,7 +634,7 @@
|
||||
|
||||
if (authname == nil || authpwd == nil)
|
||||
{
|
||||
NSLog(@"To update Sieve scripts, you must provide the \"-c credentialFile\" parameter");
|
||||
NSLog(@"To update Sieve scripts, you must provide the \"-p credentialFile\" parameter");
|
||||
return NO;
|
||||
}
|
||||
|
||||
|
||||
@@ -210,7 +210,8 @@
|
||||
GCSFolder *folder;
|
||||
BOOL rc;
|
||||
|
||||
folderPath = [NSString stringWithFormat: @"/Users/%@/Calendar/%@", username, folderId];
|
||||
folderPath = [NSString stringWithFormat: @"/Users/%@/Calendar/%@",
|
||||
username, folderId];
|
||||
folder = [fom folderAtPath: folderPath];
|
||||
if (folder)
|
||||
rc = [self truncateEntriesFromFolder: folder usingDate: date];
|
||||
@@ -237,13 +238,12 @@
|
||||
// in the default timezone.
|
||||
s = [NSString stringWithFormat: @"%@ GMT", date];
|
||||
d = [NSCalendarDate dateWithString: s calendarFormat: @"%Y-%m-%dT%H:%M:%S %Z"];
|
||||
NSLog(@"Date is: %@", d);
|
||||
fom = [GCSFolderManager defaultFolderManager];
|
||||
|
||||
if (d && fom)
|
||||
rc = [self processFolder: folder
|
||||
ofUser: username
|
||||
date: d
|
||||
date: d
|
||||
withFoM: fom];
|
||||
else
|
||||
rc = NO;
|
||||
|
||||
@@ -146,9 +146,6 @@
|
||||
[helpString appendFormat: @"\t%-20@-- %@\n",
|
||||
command, [currentTool objectAtIndex: 1]];
|
||||
}
|
||||
[helpString appendString: @"\n Visit https://www.sogo.nu/files/docs/SOGoInstallationGuide.html#_using_sogo_tool to get more infos"];
|
||||
|
||||
|
||||
|
||||
NSLog (@"%@", helpString);
|
||||
}
|
||||
|
||||
@@ -8,20 +8,20 @@
|
||||
/* Modules titles */
|
||||
"ACLs_title" = "Benutzerordner ACL-Management";
|
||||
/* Modules descriptions */
|
||||
"ACLs_description" = "<p>Dieses Modul ermöglicht die Verwaltung der Zugriffsrechte (ACLs) für die Kalender und Adressbücher jedes/jeder Benutzer*in.</p><p>Zum Ändern der Zugriffsrechte geben Sie den Namen des/der jeweiligen Benutzer*in in das Suchfeld ein. Wählen Sie den zu ändernden Ordner mit einem Doppelklick aus der Liste aus.</p>";
|
||||
"ACLs_description" = "<p>Dieses Modul ermöglicht die Verwaltung der Zugriffsrechte (ACLs) für die Kalender und Adressbücher jedes Benutzer.</p><p>Zum Ändern der Zugriffsrechte geben Sie den Namen des jeweiligen Benutzers in das Suchfeld ein. Wählen Sie den zu ändernden Ordner mit einem Doppelklick aus der Liste aus.</p>";
|
||||
"Name or Email" = "Name oder E-Mail";
|
||||
"ACLs saved" = "Berechtigungen gespeichert";
|
||||
/* Rights module: initial search message */
|
||||
"Start a search to edit the rights" = "Suche starten, um die Rechte zu ändern";
|
||||
/* Rights module: Empty search result */
|
||||
"No matching user" = "Kein/keine passende(r) Benutzer*in";
|
||||
"No matching user" = "Kein passender Benutzer";
|
||||
/* Rights module: no selection */
|
||||
"No resource selected" = "Keine Ressource ausgewählt";
|
||||
"Add User" = "Benutzer*in hinzufügen";
|
||||
"Subscribe User" = "Benutzer*in abonnieren";
|
||||
"Add User" = "Benutzer hinzufügen";
|
||||
"Subscribe User" = "Benutzer abonnieren";
|
||||
"Rights" = "Rechte";
|
||||
"Search Users" = "Benutzer*in suchen";
|
||||
"users found" = "Benutzer*innen gefunden";
|
||||
"Search Users" = "Benutzer suchen";
|
||||
"users found" = "Benutzer gefunden";
|
||||
"No resource" = "Keine Ressource";
|
||||
"Any Authenticated User" = "Alle authentifizierten Benutzer*innen";
|
||||
"Any Authenticated User" = "Alle authentifizierten Benutzer";
|
||||
"Public Access" = "Öffentlicher Zugang";
|
||||
@@ -10,7 +10,7 @@
|
||||
/* Modules descriptions */
|
||||
"ACLs_description" = "<p>アクセス制御リストの管理モジュールを使用すると、各ユーザーのカレンダーとアドレス帳のアクセス制御リストを変更できます。</p><p>ユーザーのフォルダのアクセス制御リストを変更するには、ウィンドウの上部の検索欄にユーザーのフォルダのACLを変更するには、ウィンドウの上部にある検索欄にユーザーの名前を入力して、目的のフォルダをダブルクリックしてください。</p>";
|
||||
"Name or Email" = "名前またはメール";
|
||||
"ACLs saved" = "ACLsが保存された";
|
||||
"ACLs saved" = "ACLs saved";
|
||||
/* Rights module: initial search message */
|
||||
"Start a search to edit the rights" = "権限編集の検索を開始";
|
||||
/* Rights module: Empty search result */
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
/* this file is in UTF-8 format! */
|
||||
|
||||
"Help" = "Pagalba";
|
||||
"Close" = "Uždaryti";
|
||||
"Close" = "Uždarytis";
|
||||
"Modules" = "Moduliai";
|
||||
/* Modules short names */
|
||||
"ACLs" = "ACLs";
|
||||
@@ -14,7 +14,7 @@
|
||||
/* Rights module: initial search message */
|
||||
"Start a search to edit the rights" = "Pradėti paiešką redaguoti savo teisėms";
|
||||
/* Rights module: Empty search result */
|
||||
"No matching user" = "Nėra atitinkamo naudotojo";
|
||||
"No matching user" = "Nerasta atitinkantis vartotojas";
|
||||
/* Rights module: no selection */
|
||||
"No resource selected" = "Nėra pasirinkta išteklių";
|
||||
"Add User" = "Pridėti vartotoją";
|
||||
|
||||
@@ -44,9 +44,6 @@
|
||||
"Expand" = "Expand";
|
||||
"Reduce" = "Reduce";
|
||||
|
||||
/* tooltip */
|
||||
"Refresh" = "Refresh";
|
||||
|
||||
/* generic.js */
|
||||
"Unable to subscribe to that folder!"
|
||||
= "غير قادر على الاشتراك في هذا المجلد!";
|
||||
|
||||
@@ -44,9 +44,6 @@
|
||||
"Expand" = "Expand";
|
||||
"Reduce" = "Reduce";
|
||||
|
||||
/* tooltip */
|
||||
"Refresh" = "Refresh";
|
||||
|
||||
/* generic.js */
|
||||
"Unable to subscribe to that folder!"
|
||||
= "Ezin da karpeta hori harpidetu!";
|
||||
|
||||
@@ -44,9 +44,6 @@
|
||||
"Expand" = "Expand";
|
||||
"Reduce" = "Reduce";
|
||||
|
||||
/* tooltip */
|
||||
"Refresh" = "Refresh";
|
||||
|
||||
/* generic.js */
|
||||
"Unable to subscribe to that folder!"
|
||||
= "Nije moguće se predplatiti na taj folder!";
|
||||
|
||||
@@ -44,9 +44,6 @@
|
||||
"Expand" = "Expandir";
|
||||
"Reduce" = "Reduzir";
|
||||
|
||||
/* tooltip */
|
||||
"Refresh" = "Atualizar";
|
||||
|
||||
/* generic.js */
|
||||
"Unable to subscribe to that folder!"
|
||||
= "Não foi possível inscrever-se nesta pasta!";
|
||||
@@ -109,7 +106,7 @@
|
||||
"Personal" = "Pessoal";
|
||||
"To Do" = "Tarefa";
|
||||
"Later" = "Adiar";
|
||||
"Forwarded" = "Encaminhado";
|
||||
"Forwarded" = "Forwarded";
|
||||
"Return Receipt Sent" = "Confirmação de recebimento enviada";
|
||||
"a2_Sunday" = "Dom";
|
||||
"a2_Monday" = "Seg";
|
||||
@@ -227,9 +224,9 @@
|
||||
"Move forward" = "Mover para frente";
|
||||
|
||||
/* Password Recovery */
|
||||
"passwordRecovery_Disabled" = "Desabilitado";
|
||||
"passwordRecovery_SecretQuestion" = "Pergunta secreta";
|
||||
"passwordRecovery_SecretQuestion1" = "Qual é o nome do seu primeiro animal de estimação?";
|
||||
"passwordRecovery_SecretQuestion2" = "Qual foi o seu primeiro carro?";
|
||||
"passwordRecovery_SecretQuestion3" = "Qual é o seu filme favorito ?";
|
||||
"passwordRecovery_SecondaryEmail" = "E-mail secundário";
|
||||
"passwordRecovery_Disabled" = "Disabled";
|
||||
"passwordRecovery_SecretQuestion" = "Secret question";
|
||||
"passwordRecovery_SecretQuestion1" = "What is the name of your first pet ?";
|
||||
"passwordRecovery_SecretQuestion2" = "What was your first car ?";
|
||||
"passwordRecovery_SecretQuestion3" = "What is your favorite movie ?";
|
||||
"passwordRecovery_SecondaryEmail" = "Secondary E-mail";
|
||||
@@ -44,9 +44,6 @@
|
||||
"Expand" = "Разгънете";
|
||||
"Reduce" = "Намалете";
|
||||
|
||||
/* tooltip */
|
||||
"Refresh" = "Refresh";
|
||||
|
||||
/* generic.js */
|
||||
"Unable to subscribe to that folder!"
|
||||
= "Не можете да получите достъп до папката.";
|
||||
|
||||
@@ -44,9 +44,6 @@
|
||||
"Expand" = "Expand";
|
||||
"Reduce" = "Reduce";
|
||||
|
||||
/* tooltip */
|
||||
"Refresh" = "Refresh";
|
||||
|
||||
/* generic.js */
|
||||
"Unable to subscribe to that folder!"
|
||||
= "No us podeu subscriure a aquesta carpeta!";
|
||||
|
||||
@@ -44,9 +44,6 @@
|
||||
"Expand" = "Expand";
|
||||
"Reduce" = "Reduce";
|
||||
|
||||
/* tooltip */
|
||||
"Refresh" = "Refresh";
|
||||
|
||||
/* generic.js */
|
||||
"Unable to subscribe to that folder!"
|
||||
= "不能订阅该目录!";
|
||||
|
||||
@@ -44,9 +44,6 @@
|
||||
"Expand" = "Expand";
|
||||
"Reduce" = "Reduce";
|
||||
|
||||
/* tooltip */
|
||||
"Refresh" = "Refresh";
|
||||
|
||||
/* generic.js */
|
||||
"Unable to subscribe to that folder!"
|
||||
= "無法訂閱這個資料匣!";
|
||||
|
||||
@@ -44,9 +44,6 @@
|
||||
"Expand" = "Expand";
|
||||
"Reduce" = "Reduce";
|
||||
|
||||
/* tooltip */
|
||||
"Refresh" = "Refresh";
|
||||
|
||||
/* generic.js */
|
||||
"Unable to subscribe to that folder!"
|
||||
= "Ne mogu se pretplatiti na tu mapu! ";
|
||||
|
||||
@@ -44,9 +44,6 @@
|
||||
"Expand" = "Expand";
|
||||
"Reduce" = "Reduce";
|
||||
|
||||
/* tooltip */
|
||||
"Refresh" = "Refresh";
|
||||
|
||||
/* generic.js */
|
||||
"Unable to subscribe to that folder!"
|
||||
= "Není možné se přihlásit k odebírání této složky!";
|
||||
|
||||
@@ -44,9 +44,6 @@
|
||||
"Expand" = "Udvid";
|
||||
"Reduce" = "Reducer";
|
||||
|
||||
/* tooltip */
|
||||
"Refresh" = "Opdater";
|
||||
|
||||
/* generic.js */
|
||||
"Unable to subscribe to that folder!"
|
||||
= "Det er ikke muligt at abonnere på denne mappe!";
|
||||
|
||||
@@ -44,9 +44,6 @@
|
||||
"Expand" = "Uitklappen";
|
||||
"Reduce" = "Verminderen";
|
||||
|
||||
/* tooltip */
|
||||
"Refresh" = "Refresh";
|
||||
|
||||
/* generic.js */
|
||||
"Unable to subscribe to that folder!"
|
||||
= "Het is niet mogelijk om te abonneren op deze map.";
|
||||
|
||||
Loaded 100 of 487 files, more files were not shown because too many files have changed in this diff.
Show more
Reference in new issue
Block a user