Compare commits

..
487 changed files with 2222 additions and 5847 deletions

No files matched your search

-3
View File
@@ -53,6 +53,3 @@ Tests/node_modules
Tests/lib/config.js
Tests/results.xml
Tests/results
# SAML2 autogenerated files
SoObjects/SOGo/SOGoSAML2Exceptions.*
+2 -2
View File
@@ -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
View File
@@ -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)
+7 -536
View File
@@ -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
------------
+1
View File
@@ -32,6 +32,7 @@
@interface SOGo : SoApplication
{
SOGoCache *cache;
NSTimer *timerCheckMemoryLimit;
}
- (NSDictionary *) currentLocaleConsideringLanguages:(NSArray *)_langs;
+29 -30
View File
@@ -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;
}
+2 -4
View File
@@ -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
View File
@@ -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
-6
View File
@@ -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
+153 -147
View File
@@ -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;
+35 -26
View File
@@ -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;
+5 -31
View File
@@ -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];
+20 -19
View File
@@ -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"])
+1 -1
View File
@@ -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";
+8 -79
View File
@@ -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 */
+53 -60
View File
@@ -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)
+42 -39
View File
@@ -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
+19 -20
View File
@@ -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";
+1 -1
View File
@@ -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])
+14 -38
View File
@@ -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;
+1 -6
View File
@@ -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;
+60 -90
View File
@@ -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
+35 -22
View File
@@ -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];
}
+42 -31
View File
@@ -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 */];
+8 -6
View File
@@ -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]; */
-1
View File
@@ -97,7 +97,6 @@ extern NSArray *SOGoMailCoreInfoKeys;
- (NSString *) cc;
- (NSString *) from;
- (NSString *) inReplyTo;
- (NSString *) references;
- (NSString *) messageId;
- (NSString *) received;
+2 -8
View File
@@ -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
View File
@@ -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];
-34
View File
@@ -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 */
-32
View File
@@ -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
+1 -7
View File
@@ -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 */
-136
View File
@@ -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
-1
View File
@@ -65,7 +65,6 @@
- (id) objectFromJSONString;
/* bare email addresses */
- (NSString *) mailDomain;
- (NSString *) pureEMailAddress;
- (NSString *) asQPSubjectString: (NSString *) encoding;
-24
View File
@@ -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&#x09;&#x0A;&#x0D;\\\\0]*f[\\s\\u200B&#x09;&#x0A;&#x0D;\\\\0]*o[\\s\\u200B&#x09;&#x0A;&#x0D;\\\\0]*r[\\s\\u200B&#x09;&#x0A;&#x0D;\\\\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&#x09;&#x0A;&#x0D;\\\\0]*/[\\s\\u200B&#x09;&#x0A;&#x0D;\\\\0]*f[\\s\\u200B&#x09;&#x0A;&#x0D;\\\\0]*o[\\s\\u200B&#x09;&#x0A;&#x0D;\\\\0]*r[\\s\\u200B&#x09;&#x0A;&#x0D;\\\\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];
+2 -1
View File
@@ -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;
}
-1
View File
@@ -106,7 +106,6 @@
//
// CAS support
//
- (NSString *) sha512HashTicket: (NSString* ) ticket;
- (NSString *) CASTicketFromIdentifier: (NSString *) identifier;
- (NSString *) CASSessionWithTicket: (NSString *) ticket;
- (void) setCASSession: (NSString *) casSession
+5 -33
View File
@@ -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];
}
+2 -13
View File
@@ -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)
-2
View File
@@ -29,8 +29,6 @@
@interface SOGoDAVAuthenticator : SoHTTPAuthenticator <SOGoAuthenticator>
WOContext *context;
+ (id) sharedSOGoDAVAuthenticator;
@end
+1 -12
View File
@@ -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];
-2
View File
@@ -123,8 +123,6 @@
SOGoTemplatesFolderName = "Templates";
SOGoMailComposeMessageType = "html";
SOGoMailComposeFontSize = "0";
SOGoMailHideInlineAttachments = NO;
SOGoMailDisplayFullEmail = NO;
SOGoMailDisplayRemoteInlineImages = "never";
SOGoMailCertificateEnabled = YES;
-1
View File
@@ -53,7 +53,6 @@
- (BOOL) forwardEnabled;
- (int) forwardConstraints;
- (NSArray *) forwardConstraintsDomains;
- (BOOL) notificationEnabled;
- (BOOL) vacationEnabled;
- (BOOL) vacationPeriodEnabled;
- (NSString *) vacationDefaultSubject;
+1 -6
View File
@@ -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"];
+24 -26
View File
@@ -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]
-45
View File
@@ -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 */
-131
View File
@@ -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
+1 -2
View File
@@ -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
+2 -7
View File
@@ -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",
+12 -4
View File
@@ -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)
+1 -1
View File
@@ -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
+2 -4
View File
@@ -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;
+14 -39
View File
@@ -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;
}
+2 -49
View File
@@ -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]))
{
-5
View File
@@ -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;
-10
View File
@@ -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
-68
View File
@@ -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
-4
View File
@@ -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__ */
+1 -84
View File
@@ -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 */
+1 -10
View File
@@ -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;
-30
View File
@@ -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"];
-13
View File
@@ -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
-18
View File
@@ -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)
{
+2 -3
View File
@@ -33,10 +33,9 @@
- (BOOL) isICal;
- (BOOL) isICal4;
- (BOOL) isMacOSXAddressBookApp;
- (BOOL) isMacOSXCalendarApp;
- (BOOL) isIPhoneAddressBookApp;
- (BOOL)isMacOSXCalendarApp;
- (BOOL)isIPhoneAddressBookApp;
- (BOOL) isAndroid;
- (BOOL) isMacOSXCalendar;
@end
+2 -10
View File
@@ -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
-1
View File
@@ -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");
-1
View File
@@ -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']}`)
+1 -1
View File
@@ -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
})
})
+1 -1
View File
@@ -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
+1 -1
View File
@@ -229,5 +229,5 @@ describe('Apple iCal', function() {
.toBe(0)
await webdav_su.deleteObject(otherResource)
}, 10000) // increase timeout for this long test
})
})
+1 -1
View File
@@ -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
View File
@@ -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;
}
+1 -8
View File
@@ -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];
}
+5 -5
View File
@@ -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
+50 -65
View File
@@ -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];
+1 -1
View File
@@ -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;
}
+3 -3
View File
@@ -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;
-3
View File
@@ -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