Compare commits

...
Author SHA1 Message Date
Francis Lachapelle f8aa338e64 feat(mail): handle multiple mail identities
Fixes #768, fixes #4602
2020-06-18 16:53:42 -04:00
Francis Lachapelle 2651b5aa9d chore(tool): check for invalid start/end dates in events 2020-06-18 10:56:10 -04:00
InverseBot ee4ec09897 chore(js/css): update generated files 2020-06-18 00:40:47 -04:00
Francis Lachapelle 7e21c6c6a7 refactor(preferences): replace comma-separated list of addresses by
md-chips

Fixes #5048
2020-06-17 17:46:46 -04:00
Francis Lachapelle 1432600fae fix(common(js)): initialize search field with pre-selected option
This is required because of regression in AngularJS Material.

Fixes #5044
2020-06-17 17:46:33 -04:00
InverseBot 97add602df chore(js/css): update generated files 2020-06-17 01:39:40 -04:00
Francis Lachapelle 35ebb7aaeb fix(mail(js)): fix message(s) deletion when overquota 2020-06-16 12:57:17 -04:00
Francis Lachapelle 200c353645 fix(mail(js)): use initial number of rows of textarea with sgAutogrow 2020-06-16 11:58:48 -04:00
Francis Lachapelle d39208efa0 fix(core): fix compilation of pkcs5_pbkdf2.c 2020-06-16 11:54:19 -04:00
InverseBot 348a40837a chore(js/css): update generated files 2020-06-16 00:24:58 -04:00
Extra Fu 0094d65bb9 Merge pull request #276 from the-nic/feat/pbkdf2
feat(core): Add PBKDF2 support
2020-06-12 14:13:23 -04:00
Ludovic Marcotte 0e95de31cf fix(core): fixed linked and packaging for zip->libzip work 2020-06-12 14:09:33 -04:00
Extra Fu 04673e4673 Merge pull request #281 from jkanefendt/use-libzip
Use libzip API instead of zip command line tool for creating zip archives
2020-06-12 14:00:20 -04:00
Ludovic Marcotte 6ec002f023 fix(core): always set the charset when sending IMIP replies 2020-06-12 08:42:00 -04:00
InverseBot 40d8e72c74 chore(js/css): update generated files 2020-06-11 00:39:29 -04:00
Francis Lachapelle 8692e647bd fix(web(js)): handle SAML assertion expiration 2020-06-10 14:56:46 -04:00
InverseBot e9dff9cdbf chore(js/css): update generated files 2020-06-10 00:39:05 -04:00
Francis Lachapelle cddfac82dd Revert "Revert "Revert "fix(core): added back instance caching for LDAP members"""
This reverts commit 056004bca1.
2020-06-09 15:15:06 -04:00
Francis Lachapelle cb08abfa67 Revert "fix(core): avoid caching group members per instance"
This reverts commit 0ff0d43e1e.
2020-06-09 15:14:47 -04:00
Ludovic Marcotte 1146038c76 fix(core): allow non top-level special folders and improved the doc around this 2020-06-09 14:38:01 -04:00
Francis Lachapelle 3ef94da9d6 fix(web(js)): handle SAML assertion expiration 2020-06-09 13:24:49 -04:00
NicolasandSkrupellos 27840093f5 feat(core): add lookupFields attribute in LDAP source
This field allows for looking up attributes not returned with the
default query, such as operational attributes.

Fixes #568

Co-authored-by: Skrupellos <skruppy@onmars.eu>
2020-06-09 11:52:07 -04:00
Johannes Kanefendt 9534816a21 Fixed some header comments 2020-06-09 13:55:08 +02:00
Johannes Kanefendt b951c72451 Use libzip API for creating ZIP archives instead of zip command line tool 2020-06-09 13:50:29 +02:00
InverseBot 0e6e802243 chore(js/css): update generated files 2020-06-09 00:39:20 -04:00
Francis Lachapelle 4292a45e62 refactor(preferences): replace comma-separated list of addresses by md-chips 2020-06-08 17:52:45 -04:00
Ludovic Marcotte 0ff0d43e1e fix(core): avoid caching group members per instance 2020-06-08 10:26:33 -04:00
Ludovic Marcotte 056004bca1 Revert "Revert "fix(core): added back instance caching for LDAP members""
This reverts commit ca5df1a153.
2020-06-08 09:53:44 -04:00
InverseBot dbf5179e69 chore(js/css): update generated files 2020-06-06 00:38:29 -04:00
Francis Lachapelle 433da56b23 fix(web(js)): handle SAML assertion expiration 2020-06-05 11:20:23 -04:00
InverseBot cf91b2dcbb chore(js/css): update generated files 2020-06-05 00:39:00 -04:00
Francis Lachapelle ca5df1a153 Revert "fix(core): added back instance caching for LDAP members"
This reverts commit b94175cc0c.
2020-06-04 16:13:45 -04:00
InverseBot ad9947e627 chore(js/css): update generated files 2020-06-04 00:39:00 -04:00
Ludovic Marcotte b94175cc0c fix(core): added back instance caching for LDAP members 2020-06-03 14:53:26 -04:00
Francis Lachapelle 4d42e3f176 chore(npm): update md to v1.1.22 2020-06-03 10:28:26 -04:00
Ludovic Marcotte 9f55cdc725 fix(core): added even better debugging for bogus groups 2020-06-03 07:46:57 -04:00
Nicolas Höft fc863bf63f fix: Fix for failing test in NSString+Utilities
The test in TestNSString+Utilities would otherwise fail:

======================================================================
FAIL: test_countOccurrencesOfString (TestNSString+Utilities.m:46)
----------------------------------------------------------------------
assertion failure

Simply leave the loop early if the substring length is zero to
avoid this issue
2020-06-03 08:17:08 +02:00
Nicolas Höft 2e0fc3ca09 feat(core): Add PBKDF2 support
Extend NSData+Crypto to support PBKDF2 with SHA1 HMAC as dovecot
is using it since v2.3.0.

The format hashed passwords is {PBKDF2}$1$<salt>$<rounds>$<hashed value in hex>

The implementation of pkcs#5 PBKDF2 is taken from openbsd (with minor
adjustments) as OpenSSL and GnuTLS would require quite new versions to
support this hash.
2020-06-03 08:17:08 +02:00
InverseBot 8f7df73a29 chore(js/css): update generated files 2020-06-03 01:43:03 -04:00
Francis Lachapelle 8b1b938899 refactor(preferences): replace comma-separated list of addresses by md-chips 2020-06-02 15:10:00 -04:00
Ludovic Marcotte 42587f7422 fix(core): improved debugging on bogus groups 2020-06-02 15:05:54 -04:00
InverseBot d3a4136516 chore(js/css): update generated files 2020-05-29 00:38:51 -04:00
Francis Lachapelle 6af55414fb fix(web(js)): handle SAML assertion expiration 2020-05-28 14:53:31 -04:00
Ludovic Marcotte 105ca88aef fix(core): improve debugging on invalid group sources 2020-05-28 14:02:09 -04:00
Francis Lachapelle 008b8e51cc chore(npm): update autoprefixer 2020-05-28 08:44:21 -04:00
Francis Lachapelle cd37e989db feat(core(js)): improve Google Authenticator on login page, add QR code
Fixes #5038
2020-05-28 08:41:10 -04:00
Francis Lachapelle c1acce0725 feat(core(js)): improve Google Authenticator on login page, add QR code
Fixes #5038
2020-05-27 18:01:17 -04:00
Francis Lachapelle a0320400d2 Merge pull request #277 from fpiecka/master
chore: Allow settings override in sogo-backup.sh
2020-05-27 10:59:38 -04:00
Ludovic Marcotte 05dc51ec30 feat(core): handle groups when setting ACLs (fixes #4171) 2020-05-26 10:03:16 -04:00
Ludovic Marcotte 5b6096e32c fix(core): improve debbuging when dealing with groups 2020-05-26 09:25:25 -04:00
Ludovic Marcotte e37ae5fec5 fix(core): disable ASM version of blowfish on i386 2020-05-21 08:51:56 -04:00
Fedor Piecka 789113bb44 Allow settings override in sogo-backup.sh
Allow a sysadmin to override BACKUP_DIR and DAYS_TO_KEEP by setting an environment variable.
2020-05-19 11:11:55 +02:00
InverseBot 513c17bc2f chore(js/css): update generated files 2020-05-16 00:40:13 -04:00
Extra Fu 30c0d8a425 Merge pull request #272 from the-nic/feat/blowfish
Add BLF-CRYPT password scheme support
2020-05-15 19:12:44 -04:00
Nicolas Höft 789b55c274 NSData+Crypto: add password verification function
This will allow for using specific verification, depending on the
scheme.
2020-05-15 22:53:15 +02:00
Ludovic Marcotte 798ad1502c fix(core): adjust syntax for Python > 2 2020-05-15 12:45:20 -04:00
Francis Lachapelle 07c06db69d refactor(mail(js)): replace ckEditor directive by sgCkeditor component
This refactoring

 1. simplifies updating the CKEditor source code;
 2. allows many instances of the CKEditor on the same page;
 3. fixes the cursor positioning on focus.
2020-05-15 11:37:56 -04:00
Ludovic Marcotte 9f861bd629 docs: Adjusted Thunderbird's v68 manifest info 2020-05-15 09:31:04 -04:00
Nicolas Höft c3a4f4aeb4 fix(core): NSData+String: Simplify generateSalt function
Instead of converting binary data to base64 in order to get printable
data, use a white list of characters allowed and select from those.

This also makes sure we will get the right amount of characters as
requested, not potentially longer strings.
2020-05-15 07:43:38 +02:00
Nicolas Höft 8c612fc0a2 feat(core): Add BLF-CRYPT scheme.
Dovecot promotes the use of BLF-CRYPT as a hashing scheme [1].
However, as not all libc-crypt() implementations support this scheme,
use the implementation added in a previous commit.

Also, update the references to the dovecot implementation and a small
test case for testing the blowfish implementation.

[1] https://doc.dovecot.org/configuration_manual/authentication/password_schemes/

Fixes #4958
2020-05-15 07:43:38 +02:00
Nicolas Höft 562f81f21f fix(core): NSData+String: Dont mix tabs and spaces 2020-05-15 07:43:38 +02:00
Nicolas Höft 3040c275d8 feat(core): Add blowfish implementation from openwall
Using version 1.3, see https://www.openwall.com/crypt/
2020-05-15 07:43:38 +02:00
Ludovic Marcotte 96986280ee fix(eas): avoid doing bogus truncation 2020-05-14 15:46:55 -04:00
Extra Fu b53d1254d9 Merge pull request #274 from the-nic/feature/smtp-ssl-tls
feat(core): support smtps
2020-05-14 12:15:51 -04:00
Nicolas Höft 589cfaa2f4 feat(core): Support smtps and STARTTLS for SMTP
Support uri schemes for SMTP, enabling STARTTLS and SMTPS for SMTP
connections.

The new format for configuration value SMTPServer supports passing a
URI scheme (either smtp:// or smtps://). To support old configurations,
if no scheme is given, smtp:// is assumed.

Fixes #31
2020-05-14 08:47:05 +02:00
Ludovic Marcotte 7ef507bdb4 fix(packaging): fixes for centos/rhel v8 support 2020-05-13 11:14:40 -04:00
InverseBot fb117f48c8 chore(js/css): update generated files 2020-05-13 01:03:39 -04:00
Ludovic Marcotte a102a94ac5 fix(packaging): enable mfa on focal 2020-05-12 13:44:10 -04:00
Francis Lachapelle f31a07fd74 Revert "fix(packaging): don't enable mfa on squeeze"
This reverts commit e9cc088153.
2020-05-12 11:20:42 -04:00
Francis Lachapelle 964e6f0cb1 fix(mail(js)): encode HTML entities when computing height of textarea
Fixes #5020
2020-05-12 11:16:32 -04:00
Ludovic Marcotte d7979871bc fix(packaging): fixed condition syntax 2020-05-12 07:59:00 -04:00
Ludovic Marcotte e9cc088153 fix(packaging): don't enable mfa on squeeze 2020-05-12 07:46:59 -04:00
InverseBot a0e5699e60 chore(js/css): update generated files 2020-05-08 10:40:46 -04:00
Francis Lachapelle 272fa8f898 fix(calendar(js)): fix event blocks width in day view
Fixes #5017
2020-05-08 09:51:55 -04:00
Francis Lachapelle 312817b7ae chore(debian): fix rules for MFA 2020-05-08 09:15:43 -04:00
Francis Lachapelle e8f0471bcf feat(core(js)): improve Google Authenticator on login page, add QR code
Closes #2722
2020-05-07 16:45:37 -04:00
Ludovic Marcotte f78300a12e feat(core): initial Google Authenticator support for 2FA 2020-05-07 07:22:24 -04:00
Francis Lachapelle 33d3154d15 chore(release): 4.3.2 2020-05-06 17:07:38 -04:00
Francis Lachapelle 7b5c787718 fix(core): LDAP group expansion must use all user sources 2020-05-05 12:18:37 -04:00
Francis Lachapelle 5da7903ce1 chore(tool): fallback to NGImap4ConnectionStringSeparator
When using a master user to update the Sieve scripts through sogo-tool,
we may not be able to detect the IMAP delimiter. Therefore, we must
fallback to the default NGImap4ConnectionStringSeparator.

Fixes #4919
2020-05-05 10:53:02 -04:00
InverseBot 1fa4b8871d chore(js/css): update generated files 2020-05-05 01:18:29 -04:00
Francis Lachapelle e7da4c19b8 fix(web(js)): improve encoding of folder paths in XHR calls
Fixes #4989
2020-05-04 18:44:20 -04:00
Ludovic Marcotte cbf3d89f3e Merge branch 'master' of https://github.com/inverse-inc/sogo 2020-05-04 12:57:40 -04:00
Ludovic Marcotte 7929fd394f fix(core): skip folder check during ACL subscribe 2020-05-04 12:57:11 -04:00
Francis Lachapelle 8f7b2bfbed fix(core): LDAP group expansion must use all user sources 2020-05-04 12:25:16 -04:00
root 8a4e79963f fix(core): skip folder check during ACL subscribe 2020-05-04 09:57:49 -04:00
InverseBot d29c00827c chore(js/css): update generated files 2020-05-03 01:18:12 -04:00
Francis Lachapelle f1b52a7486 chore(release): 4.3.1 2020-05-01 14:51:36 -04:00
Francis Lachapelle c7d3546be8 chore(js/css): update generated files 2020-05-01 14:51:36 -04:00
Francis Lachapelle 4742f2fd31 chore(npm): update development dependencies 2020-05-01 14:51:36 -04:00
Francis Lachapelle 9feefba545 chore(npm): update angular-ui-router 2020-05-01 14:51:35 -04:00
Francis Lachapelle c12b1395e7 chore(npm): update CKEditor to version 4.14.0 2020-05-01 14:51:35 -04:00
InverseBot 605c026425 chore(js/css): update generated files 2020-05-01 01:20:09 -04:00
Francis Lachapelle 807cefaa39 fix(mail(css)): minor improvements to the mail editor 2020-04-30 11:46:33 -04:00
Francis Lachapelle 2c050d847e fix(mail(js)): wrong argument to Mailbox.$_deleteMessages
Fixes #4986
2020-04-30 11:02:48 -04:00
Francis Lachapelle 158c5e45c4 fix(mail(js)): restore unseen count after deleting a mailbox 2020-04-30 10:52:28 -04:00
Francis Lachapelle 174b44ed50 fix(web(css)): truncate text of toolbar in multi-selection mode
Fixes #4623
2020-04-30 09:01:28 -04:00
InverseBot 260aa1971b chore(js/css): update generated files 2020-04-29 17:05:48 -04:00
Francis Lachapelle d99bbbb37e fix(core): escape quotes before sending SQL queries
Fixes #5010
2020-04-29 12:52:09 -04:00
Francis Lachapelle 09c76b3649 fix(core): escape quotes before sending SQL queries
Fixes #5010
2020-04-27 17:13:28 -04:00
Francis Lachapelle 04a6217512 fix(core): escape quotes before sending SQL queries
Fixes #5010
2020-04-23 13:49:15 -04:00
InverseBot 1801d0d60f chore(js/css): update generated files 2020-04-17 16:00:36 -04:00
Francis Lachapelle fd063fd5b3 fix(web(js)): handle SAML assertion expiration 2020-04-17 15:59:44 -04:00
Francis Lachapelle a4c51a90bb Remove reference to SOGoGroup.h 2020-04-17 15:16:23 -04:00
InverseBot 2fdb924aff chore(js/css): update generated files 2020-04-17 13:54:31 -04:00
Francis Lachapelle 824b38332c fix(preferences): avoid exception when parsing PreventInvitationsWhitelist
Fixes #5006
2020-04-17 12:11:01 -04:00
Francis Lachapelle 64461764c8 fix(web(js)): handle SAML assertion expiration 2020-04-17 09:50:43 -04:00
InverseBot 0683dafc22 chore(js/css): update generated files 2020-04-17 01:09:04 -04:00
Francis Lachapelle 058df21ada fix(calendar(js)): find a free slot for a maximum of 30 days 2020-04-16 17:11:50 -04:00
Francis Lachapelle 84f3fd5e1c i18n(sk): update Slovak translation 2020-04-16 09:37:41 -04:00
Francis Lachapelle 7b4e4f7345 i18n(pl): update Polish translation 2020-04-16 09:37:17 -04:00
Francis Lachapelle 543abb39d5 i18n(hu): update Hungarian translation 2020-04-16 09:36:49 -04:00
Francis Lachapelle f75af12db8 i18n(fr): update French translation 2020-04-16 09:36:29 -04:00
Francis Lachapelle 91d193fa0f i18n(nl): update Dutch translation 2020-04-16 09:36:00 -04:00
Francis Lachapelle 0e5e9ddb74 i18n(ca): update Catalan translation 2020-04-16 09:35:39 -04:00
Francis Lachapelle a41fb9e1a9 i18n(de): update German translation 2020-04-16 09:35:11 -04:00
Francis Lachapelle c61fe4a188 i18n(pt_BR): update Brazilian (Portuguese) translation 2020-04-16 09:33:47 -04:00
InverseBot 0b43f11d61 chore(js/css): update generated files 2020-04-14 01:09:06 -04:00
Francis Lachapelle 52bb3baa8b fix(mail(js)): url-encode folder path to handle special characters (%)
Fixes #4989
2020-04-13 17:28:11 -04:00
Francis Lachapelle 979e1342c7 docs: fix wrong default values for MailComposeMessageType and LoginModule
Fixes #4992
2020-04-06 15:38:45 -04:00
Francis Lachapelle 4019cb7556 chore(npm): update angular-ui-router 2020-04-03 15:42:30 -04:00
Francis Lachapelle 6af0058657 fix(calendar): use the calendar owner when generating freebusy information 2020-04-03 12:18:16 -04:00
Extra Fu a78f06e909 Merge pull request #234 from iredmail/master
Create directories with 077X
2020-04-02 09:53:04 -04:00
Extra Fu 62823815a7 Merge pull request #263 from jkanefendt/revise-group-expansion
Revision of the group member expansion interface
2020-04-02 09:52:21 -04:00
Ludovic Marcotte 78e50b7a2a docs: fix indent (fixes #4999) 2020-04-02 07:23:07 -04:00
Extra Fu 841d6896fc Merge pull request #271 from tfux/4988
fix(eas): properly encode DisplayTo (fixes #4995)
2020-03-30 06:35:22 -04:00
root 18ffd1a744 fix(eas): properly encode DisplayTo (fixes #4995) 2020-03-29 09:15:00 +02:00
Extra Fu 45eeb5fa24 Merge pull request #270 from tfux/4988
fix(eas): fix invalid DisplayTo (fixes #4988)
2020-03-22 14:04:59 -04:00
root b8f31069ed fix(eas): fix invalid DisplayTo (fixes #4988) 2020-03-22 17:15:18 +01:00
Francis Lachapelle e8e41f15e3 i18n(lv): update Latvian translation 2020-03-09 11:13:20 -04:00
Francis Lachapelle e3559d5ca3 i18n(cs): update Czech translation 2020-03-09 11:13:19 -04:00
Francis Lachapelle de5da7bd0d i18n(ro_RO): update Romanian translation 2020-03-09 11:13:19 -04:00
InverseBot bc09aeb830 chore(js/css): update generated files 2020-03-07 01:26:12 -05:00
Francis Lachapelle d1dbceb407 fix(mail): remove onpointerrawupdate event handler from HTML messages
Fixes #4979
2020-03-06 12:14:46 -05:00
Francis Lachapelle 2601ad4563 chore(tool): fix usage example of manage-acl
Fixes #4963
2020-03-06 10:55:31 -05:00
Francis Lachapelle da51482ce1 fix(mail): validate IMAP ACL compliance on main mail account 2020-03-02 14:04:47 -05:00
Ludovic Marcotte 62e25f6c13 fix(calendar(core)): avoid generating empty parameters list 2020-02-27 15:26:39 -05:00
Ludovic Marcotte 78292495bb fix(calendar(core)): check for array size before looking into 2020-02-27 14:46:21 -05:00
InverseBot 72efaa7432 chore(js/css): update generated files 2020-02-22 01:23:26 -05:00
Francis Lachapelle daaad938cb fix(mail(js)): disable autogrow of textarea in popup window
Fixes #4962
2020-02-21 11:54:27 -05:00
Francis Lachapelle 5e1f487e49 fix(calendar): fix first range of "busy off hours" in vFreeBusy response 2020-02-21 10:37:58 -05:00
InverseBot 989021d561 chore(js/css): update generated files 2020-02-20 01:27:48 -05:00
Francis Lachapelle 656410eb6b fix(preferences(html)): reject action of mail filter is now a textarea 2020-02-19 15:56:51 -05:00
Francis Lachapelle 4e2d5098c7 fix(mail(js)): limit number of messages to batch delete per API call 2020-02-19 11:10:38 -05:00
InverseBot 3433a2e765 chore(js/css): update generated files 2020-02-19 01:27:46 -05:00
Francis Lachapelle eabb40a0bf fix(css): improve contrast of toolbars w/input field 2020-02-18 16:24:36 -05:00
Francis Lachapelle 860d635c9c fix(web(css)): space issue with folders subscription dialog on Firefox
Fixes #4954
2020-02-13 09:15:26 -05:00
InverseBot 501edfc319 chore(js/css): update generated files 2020-02-13 01:26:06 -05:00
Francis Lachapelle 2b5e7a68d7 chore(tool): check for missing start/end dates in events 2020-02-12 11:35:56 -05:00
Francis Lachapelle 95b3e9d4fa fix(mail(js)): skrink autogrow md-input when content is removed 2020-02-12 10:59:06 -05:00
Extra Fu dda13e602b Merge pull request #269 from tfux/ics_import
fix(calendar): fallback to tz found in ics (fixes #4943)
2020-02-05 11:34:58 -05:00
InverseBot c49137e620 chore(js/css): update generated files 2020-02-05 02:13:22 -05:00
root 24fc9a950b fix(calendar): handle tz with until in rrule (fixes #4943) 2020-02-02 12:14:48 +01:00
InverseBot ad3a9c87f8 chore(js/css): update generated files 2020-02-01 01:20:57 -05:00
Francis Lachapelle 3e6cd3c53c perf(web): don't wait on Sieve server to render UIxPageFrame.wox 2020-01-31 16:51:33 -05:00
Francis Lachapelle bc963d53c6 fix(mail): wrap HTML part before re-encoding content
Avoid improper encoding when the HTML part is using an unsupported
encoding by libxml2.
2020-01-30 15:36:16 -05:00
root 57bbb255cc fix(calendar): fallback to tz found in ics 2020-01-29 22:18:29 +01:00
Francis Lachapelle 127a3c487d chore(release): 4.3.0 2020-01-21 09:53:16 -05:00
InverseBot d92cd9c3f3 chore(js/css): update generated files 2020-01-21 01:21:02 -05:00
Extra Fu c69382cb5d Merge pull request #268 from tfux/4911
fix(eas): sync reminder for invitation (fixes #4911)
2020-01-20 17:55:17 -05:00
Francis Lachapelle 88a675596c i18n(pt_BR): update Brazilian (Portuguese) translation 2020-01-20 16:34:17 -05:00
Francis Lachapelle b71df14b14 fix(mail(js)): bypass autogrow feature of md-input to fix scroll jumping 2020-01-20 14:19:11 -05:00
Francis Lachapelle 73dc86a6ed fix(mail(js)): bypass autogrow feature of md-input to fix scroll jumping
See https://github.com/angular/material/issues/3070
2020-01-20 14:10:57 -05:00
root 9221811fdc fix(eas): sync reminder for invitation (fixes #4911) 2020-01-20 18:50:49 +01:00
Francis Lachapelle 560c1dcd82 fix(calendar): adjust recurrent rule when importing a vEvent 2020-01-20 12:03:34 -05:00
Extra Fu 0e5514e425 Merge pull request #267 from tfux/4929
fix(eas): additional name fields (fixes #4929)
2020-01-20 11:04:06 -05:00
Francis Lachapelle 94af9bb780 chore(npm): update CKEditor to version 4.13.1 2020-01-17 12:27:45 -05:00
Francis Lachapelle 43e5662950 fix(calendar(css)): fix padding of sort handle of calendars 2020-01-17 11:48:02 -05:00
Francis Lachapelle 96f2c64320 chore(npm): update md to v1.1.21 2020-01-17 11:42:40 -05:00
Francis Lachapelle 001d76fd05 fix(calendar(js)): allow event invitations to be moved
Fixes #4926
2020-01-17 11:34:37 -05:00
Francis Lachapelle 62f64314c0 fix(mail): fix SMTP authentication when reporting spam/ham
Fixes #4941
2020-01-17 09:53:16 -05:00
root 3f94516e31 fix(eas): additional name fields (fixes #4929) 2020-01-16 22:17:57 +01:00
Francis Lachapelle d8fab6bb21 fix(calendar): restore [SOGoAppointmentObject resourceHasAutoAccepted]
Fixes #4923
2020-01-16 10:43:42 -05:00
Ludovic Marcotte 047a98b870 fix(eas): avoid generating broken XML ouput (fixes #4927) 2020-01-08 13:04:42 -05:00
Ludovic Marcotte f0980a9cbd feat(core): Added AES-128-CBC password scheme for SQL authentication.
This allows SOGo to use Plesk's database as an authentication source.
2020-01-06 15:47:47 -05:00
Francis Lachapelle 4216f9e726 chore: use TABLE variable in 9-table migration scripts
Fixes #4889
2020-01-06 15:10:43 -05:00
Francis Lachapelle ba1757fd05 chore: fix MySQL migration script to 9-table schema
Fix datatype of sogo_folder_info.c_location.

Fixes #4377
Fixes #4888
2020-01-06 13:43:40 -05:00
Francis Lachapelle 91ca8b8bec fix(calendar): restore [SOGoAppointmentObject resourceHasAutoAccepted]
This method is used for ActiveSync support.

Fixes #4923
2020-01-06 09:41:49 -05:00
InverseBot 5b089bbe14 chore(js/css): update generated files 2019-12-31 01:15:45 -05:00
InverseBot 9437ba8b9a chore(js/css): update generated files 2019-12-25 01:18:27 -05:00
Francis Lachapelle 94b171675f perf(css): remove unused selectors for layout 2019-12-24 16:18:32 -05:00
Francis Lachapelle aaaa16ed40 fix(calendar): fix monthly computation with month day mask
Fixes #4915
2019-12-24 14:56:49 -05:00
Francis Lachapelle d6d33f9f0b fix(tool): fix error handling when updating Sieve script 2019-12-19 15:22:24 -05:00
Ludovic Marcotte 4ed2c727a2 fix(eas): make sure there is always an attendee name (fixes #4910) 2019-12-19 09:03:17 -05:00
InverseBot ee1ffef5b5 chore(js/css): update generated files 2019-12-19 01:17:15 -05:00
Francis Lachapelle 6b17d5298c chore(js/css): update generated files 2019-12-17 15:00:49 -05:00
Francis Lachapelle e36d5dfb7c chore(release): 4.2.0 2019-12-17 14:43:59 -05:00
Francis Lachapelle 56e1583ccc chore(npm): update autoprefixer 2019-12-17 14:21:04 -05:00
Francis Lachapelle c1ccbae316 style(obj-c): replace tabs by spaces 2019-12-17 11:15:50 -05:00
Francis Lachapelle 91cefbdff1 docs: update asciidoc guides 2019-12-17 11:13:42 -05:00
Francis Lachapelle e65e0f191f i18n(sk): update Slovak translation 2019-12-17 11:11:58 -05:00
Francis Lachapelle bc172c5895 i18n(fr): update French translation 2019-12-17 11:11:43 -05:00
Francis Lachapelle ae42fd869a i18n(nl): update Dutch translation 2019-12-17 11:11:11 -05:00
Francis Lachapelle cd8f957774 i18n(cs): update Czech translation 2019-12-17 11:10:40 -05:00
Francis Lachapelle 7180b5988d fix(preferences): improve error handling with Sieve server 2019-12-16 14:54:03 -05:00
Francis Lachapelle 9aeecead6c fix(calendar(html)): don't cache list of week days
Fixes #4907
2019-12-16 11:10:07 -05:00
Francis Lachapelle 15d7c69d94 fix(calendar): adjust invalid dates when importing a vEvent
Fixes #4845
2019-12-16 10:48:26 -05:00
Francis Lachapelle a25e5acba4 chore(tool): conditionally activate the Sieve script 2019-12-13 15:19:59 -05:00
Ludovic Marcotte 0ceccdd612 fix(addressbook(core)): safety check from broken URLs 2019-12-13 13:54:34 -05:00
Francis Lachapelle 86643b4339 docs: convert NEWS to CHANGELOG.md 2019-12-11 12:41:13 -05:00
Francis Lachapelle edc01e9532 fix(calendar): allow fetching group members from contacts-only sources
If a group is invited to an event, search members within the known source
of the contact instead of relying on a global search among "canAuthenticate" sources.
2019-12-10 12:29:40 -05:00
InverseBot 9d1d12af54 chore(js/css): update generated files 2019-12-10 01:17:03 -05:00
Francis Lachapelle a303011237 fix(mail(js)): avoid exception when adding duplicated recipient 2019-12-09 12:34:48 -05:00
Francis Lachapelle 5ccc12639b fix(mail(html): expose UIxMailViewRecipientMenu in popup view 2019-12-09 12:29:58 -05:00
Francis Lachapelle 2048fb19cf fix(calendar(js)): avoid exception when adding duplicated attendee 2019-12-09 12:24:55 -05:00
Francis Lachapelle 46ade7640a feat(mail(js)): new button to expand recipients that are LDAP groups
Copy all object attributes when cloning the Message object in the popup
view.

Fixes #4902
2019-12-09 11:41:14 -05:00
Francis Lachapelle 2e46e89d58 refactor: replace calls to create GMT NSTimeZone instance 2019-12-09 08:50:50 -05:00
InverseBot eb13aa14b2 chore(js/css): update generated files 2019-12-05 01:17:54 -05:00
Francis Lachapelle 47075b40a2 fix(mail): wrap HTML part with HTML tags to render all content 2019-12-04 15:45:26 -05:00
Francis Lachapelle e71afc982e fix(web): improve contrast of toolbars w/input field 2019-12-04 12:36:48 -05:00
Johannes Kanefendt 948c89f5bb - Respect the user domain when dealing with groups
- Renamed protocol MembershipAwareSource to SOGoMembershipSource
2019-12-04 10:37:08 +01:00
InverseBot 7aaadd724d chore(js/css): update generated files 2019-12-04 01:20:08 -05:00
Francis Lachapelle 5923639154 fix(calendar): raise warning when MuiltipleBookings is set to -1 2019-12-03 16:42:36 -05:00
Francis Lachapelle fbdabc9615 fix(calendar(js)): fix refresh of attendees freebusy information
* refresh template with freebusy information of organizer
* fix event coverage in freebusy timeline when event spans a day

Related to bug #4899
2019-12-03 10:34:47 -05:00
Francis Lachapelle 3bb40e4024 fix(calendar): adjust invalid dates when importing a vEvent
Fixes #4845
2019-11-27 17:00:28 -05:00
Johannes Kanefendt 278d4ab4cb Return NSDictionary contact entries from [MembershipAwareSource membersForGroupWithUID] instead of SOGoUser objects.
Optimization for user sources that are capable of fetching lists of group members at once, might save a lot of expensive SOGoUser lookups.
2019-11-26 22:20:28 +01:00
Francis Lachapelle f872dc52c6 i18n(de): remove duplicated short date formats 2019-11-26 15:13:07 -05:00
Johannes Kanefendt 2e98929900 Removed tight coupling of group membership expansion and LDAPSource.
Any implementation of SOGoSource may now support group expansion by implementing the protocol MembershipAwareSource.
2019-11-26 15:29:02 +01:00
InverseBot 856b0654ab chore(js/css): update generated files 2019-11-22 01:17:37 -05:00
Francis Lachapelle 456a66b66b feat(mail(js)): new button to expand recipients that are LDAP groups 2019-11-21 21:51:27 -05:00
InverseBot f140bba03a chore(js/css): update generated files 2019-11-21 01:18:01 -05:00
Francis Lachapelle 123ccef770 (web) Improve contrast of toolbars w/input field 2019-11-20 12:18:42 -05:00
Francis Lachapelle 4628292fb3 chore(debian): don't call chown if pid file doesn't exist 2019-11-20 09:17:07 -05:00
InverseBot 06814c2dee chore(js/css): update generated files 2019-11-20 01:16:27 -05:00
InverseBot fa2fbd3177 chore(js/css): update generated files 2019-11-19 01:17:36 -05:00
Francis Lachapelle 729802222f fix(mail(js)): add missing library to save msg from popup window
Fixes #4879
2019-11-18 11:57:53 -05:00
Francis Lachapelle e458a78a10 i18n(ca): update Catalan translation
Fixes #4878
2019-11-18 11:34:22 -05:00
Francis Lachapelle 14b60cd756 fix(calendar(js)): avoid call to /members when expansion is disabled
Expose SOGoLDAPGroupExpansionEnabled in /jsonDefaults to avoid a call to
/members when the parameter is disabled.
2019-11-18 11:06:15 -05:00
InverseBot 9c299cf0de chore(js/css): update generated files 2019-11-16 01:18:07 -05:00
Francis Lachapelle 5b3d84ee24 refactor(preferences): conditionally activate the Sieve script
All the user defaults are now editable through the Preferences module,
even if an external Sieve script is enabled. However, the user can
disable the external Sieve script and force the activation of the
"sogo" Sieve script.
2019-11-15 14:37:35 -05:00
Francis Lachapelle 350677bbce chore(debian): don't call chown if pid file doesn't exist 2019-11-08 13:34:39 -05:00
InverseBot c6f532dea8 chore(js/css): update generated files 2019-11-08 01:57:42 -05:00
Ludovic Marcotte 2bc24eca82 fix(core): don't disable the current script if we aren't doing anything 2019-11-07 15:27:43 -05:00
Ludovic Marcotte ac91a303c9 feat(core): Allow the detection of external Sieve scripts
This allows SOGo users to keep existing Sieve scripts instead of
overwriting the SOGo ones by default.
2019-11-07 15:18:28 -05:00
Francis Lachapelle 70984def1f fix(preferences(js)): Lower constraints on auto-reply dates range
Fixes #4874
2019-11-07 11:37:57 -05:00
Francis Lachapelle 699849caec fix(calendar(js)): don't escape HTML characters in repeat select menu
Fixes #4875
2019-11-07 09:47:11 -05:00
Ludovic Marcotte 113f076981 chore(preferences): removed years' old commented code 2019-11-07 08:32:15 -05:00
Ludovic Marcotte 3a8336484c chore(core): removed NSLog calls 2019-11-07 08:17:35 -05:00
Ludovic Marcotte 4475ac651d feat(core): allow pre/appended Sieve scripts
Allow domain admins to now set Sieve scripts that can be prepended
and appended to all users' scripts. This can now be controller
using the SOGoSieveScriptHeaderTemplateFile and
SOGoSieveScriptFooterTemplateFile domain defaults.
2019-11-06 14:53:30 -05:00
Ludovic Marcotte c2e7f6a866 feat(preferences): allow hiding of vacation the vacation period
This is controllable using the new SOGoVacationPeriodEnabled
domain defaults variable.
2019-11-05 15:33:16 -05:00
Francis Lachapelle 32968092e2 Merge pull request #262 from Neustradamus/master
chore(apache): Space removal in configuration files
2019-11-03 21:03:07 -05:00
Neustradamus 1fd4d54d59 Empty line removal
Empty line removal
2019-11-02 19:18:19 +01:00
Neustradamus d9f059a9e3 Space removal
Space removal
2019-11-02 19:08:23 +01:00
InverseBot 83d52a16f4 chore(js/css): update generated files 2019-11-01 01:14:41 -04:00
Francis Lachapelle 55aaeaf9a6 Update ChangeLog 2019-10-31 13:26:11 -04:00
Francis Lachapelle 69cbe5d619 Preparation for release 4.1.1 2019-10-31 13:25:48 -04:00
Francis Lachapelle 82735fa8a7 chore(js/css): update generated files 2019-10-31 13:09:54 -04:00
Francis Lachapelle 33dce130ce chore(js): fix path of lodash library 2019-10-31 13:08:28 -04:00
Francis Lachapelle 372bd676aa chore: update commit message for generated files 2019-10-31 11:59:55 -04:00
Francis Lachapelle fc34befe47 docs: update CONTRIBUTING file 2019-10-31 11:59:38 -04:00
Francis Lachapelle eb4ad5e086 i18n(de): update German translation 2019-10-31 11:52:43 -04:00
Ludovic Marcotte a2a6bfab59 fix(core): avoid warning by including missing file 2019-10-31 09:04:10 -04:00
InverseBot 22bd650b72 (js/css) Update generated files 2019-10-31 02:07:08 -04:00
Francis Lachapelle d4f0ccd3f7 docs: update CONTRIBUTING file 2019-10-30 13:55:03 -04:00
Francis Lachapelle 8b45f2c731 fix(calendar(js)): swap start-end dates when delta is negative
When creating an event by drag'n'dropping from bottom to top, the
original coordinate become the start date.
2019-10-30 13:55:03 -04:00
Francis Lachapelle 060dea3818 style(mail(js)): add missing semi-colon 2019-10-30 13:55:03 -04:00
Francis Lachapelle 487bbf74c0 fix(calendar(js)): don't modify time when computing dates interval
Fixes #4861
2019-10-30 13:55:03 -04:00
Ludovic Marcotte d1e2418d65 (fix) fixed EAS provisioning support for Outlook/iOS (fixes #4853) 2019-10-30 13:44:13 -04:00
Francis Lachapelle 13ad6ff875 Initial CONTRIBUTING file 2019-10-30 11:13:40 -04:00
Francis Lachapelle f6ca946058 fix(calendar): don't allow RDATE unless already defined
Since RDATE are not properly supported in EAS, hide the possibility to
specify recurring dates unless the component being edited already
contains RDATE(s).
2019-10-30 11:04:59 -04:00
Francis Lachapelle 42bb3b288b (i18n) Update Polish translation
Fixes #4851
2019-10-29 11:03:27 -04:00
Ludovic Marcotte 61945734c7 (doc) quote default values (fixes #4827) 2019-10-28 15:14:35 -04:00
Ludovic Marcotte e866fd5fda (fix) fixed Apple Calendar creation (fixes #4813) 2019-10-28 14:39:39 -04:00
Ludovic Marcotte 7ff9921709 (fix) removed unused method 2019-10-25 13:34:41 -04:00
Ludovic Marcotte 34fcfe024b (fix) use the supplied Sieve creds to fetch the IMAP4 separator (fixes #4846) 2019-10-25 13:32:52 -04:00
InverseBot 2d5d1b4297 (js/css) Update generated files 2019-10-25 01:27:37 -04:00
Zhang Huangbin 7d38a48f68 Create directories with 077X
Create directories with 077X. If we run 2 SOGo instances by following official tutorial[1], the second SOGo instance cannot start due to no permission to write pid file or log file.

[1] https://sogo.nu/nc/support/faq/article/dedicated-separate-sogo-instance-for-activesync.html
2017-03-22 14:11:42 +08:00
352 changed files with 12995 additions and 8498 deletions

No files matched your search

+114
View File
@@ -0,0 +1,114 @@
# Contributing to SOGo
## Reporting Bugs and Suggesting Enhancements
If you encounter a possible bug with SOGo, you can access our
[bug tracking system](https://sogo.nu/bugs/).
Please make sure to respect the following guidelines when reporting a bug:
* verify that the bug you found is not already known or even fixed in the `master` version
* make the actual facts very clear; be precise, we need to be able to reproduce the problem
* explain your speculations, if any
* add a screenshot to the ticket if appropriate
## Submitting a Pull Request
Begin by reading [SOGo Developer's Guide](../Documentation/SOGoDevelopersGuide.asciidoc).
### Git Commit Guidelines
We have very precise rules over how our git commit messages can be formatted. This leads to **more
readable messages** that are easy to follow when looking through the **project history**.
It is important to note that we use the git commit messages to **generate** the
[CHANGELOG](../CHANGELOG.md) document. Improperly formatted commit messages may result in your
change not appearing in the CHANGELOG of the next release.
### Commit Message Format
Each commit message consists of a **header**, a **body** and a **footer**. The header has a special
format that includes a **type**, a **scope** and a **subject**:
```html
<type>(<scope>): <subject>
<BLANK LINE>
<body>
<BLANK LINE>
<footer>
```
> Any line of the commit message cannot be longer than 100 characters.
> This allows the message to be easier to read on GitHub as well as in various Git tools.
#### Type
Must be one of the following:
* **feat**: A new feature
* **fix**: A bug fix
* **docs**: Documentation only changes
* **i18n**: Change in localizable strings
* **style**: Changes that do not affect the meaning of the code (white-space, formatting, missing
semi-colons, etc)
* **refactor**: A code change that neither fixes a bug nor adds a feature
* **perf**: A code change that improves performance
* **test**: Adding missing tests
* **chore**: Changes to the build and packaging process or auxiliary tools (sogo-tool,
sogo-ealarms-notify) and libraries such as documentation generation
#### Scope
The scope could be anything that helps specifying the scope (or feature) that is changing.
Examples
* mail
* mail(js)
* calendar(css)
* addressbook
* preferences(js)
* core
* eas
#### Subject
The subject contains a succinct description of the change:
* use the imperative, present tense: "change" not "changed" nor "changes"
* don't capitalize first letter
* no dot (.) at the end
#### Body
Just as in the **subject**, use the imperative, present tense: "change" not "changed" nor "changes"
The body should include the motivation for the change and contrast this with previous behavior.
#### Footer
The footer should contain any information about **Breaking Changes** and is also the
place to reference [Mantis](https://sogo.nu/bus/) issues that this commit **Fixes** or **Resolves**.
> Breaking Changes are intended to be highlighted in the CHANGELOG as changes that will require
> community users to modify their code after updating to a version that contains this commit.
#### Sample Commit messages
```text
fix(calendar): don't raise exception when renaming with same name
this would break Apple Calendar.app when creating a new calendar
Fixes #4813
```
```text
feat(calendar(js)): optionally expand LDAP groups in attendees editor
* add `/members` action for LDIF groups
* add button to expand invited LDAP groups
Fixes #2506
```
```text
fix(core): set default Sieve port to 4190
BREAKING CHANGE: the default port for the SOGoSieveServer configuration default is now 4190 (was
2000).
You need to explicitly set the port if you use a different port.
Closes #4826
```
+31 -7
View File
@@ -99,7 +99,16 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
if ((o = [n flattenedValueAtIndex: 1 forKey: @""]))
[s appendFormat: @"<FirstName xmlns=\"Contacts:\">%@</FirstName>", [o activeSyncRepresentationInContext: context]];
if ((o = [n flattenedValueAtIndex: 2 forKey: @""]))
[s appendFormat: @"<MiddleName xmlns=\"Contacts:\">%@</MiddleName>", [o activeSyncRepresentationInContext: context]];
if ((o = [n flattenedValueAtIndex: 3 forKey: @""]))
[s appendFormat: @"<Title xmlns=\"Contacts:\">%@</Title>", [o activeSyncRepresentationInContext: context]];
if ((o = [n flattenedValueAtIndex: 4 forKey: @""]))
[s appendFormat: @"<Suffix xmlns=\"Contacts:\">%@</Suffix>", [o activeSyncRepresentationInContext: context]];
if ((o = [self workCompany]))
[s appendFormat: @"<CompanyName xmlns=\"Contacts:\">%@</CompanyName>", [o activeSyncRepresentationInContext: context]];
@@ -302,8 +311,10 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
{
CardElement *element;
NSMutableArray *addressLines, *other_addresses;
id o;
id o, l, m, f, p, s;
l = m = f = p = s = nil;
// Contact's note
if ((o = [[theValues objectForKey: @"Body"] objectForKey: @"Data"]))
[self setNote: o];
@@ -529,10 +540,23 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
if ((o = [theValues objectForKey: @"FileAs"]) || ![self _isGhosted: @"FileAs" inContext: context])
[self setFn: [theValues objectForKey: @"FileAs"]];
[self setNWithFamily: [theValues objectForKey: @"LastName"]
given: [theValues objectForKey: @"FirstName"]
additional: nil prefixes: nil suffixes: nil];
if ((o = [theValues objectForKey: @"LastName"]) || ![self _isGhosted: @"LastName" inContext: context])
l = o ? o : @"";
if ((o = [theValues objectForKey: @"FirstName"]) || ![self _isGhosted: @"FirstName" inContext: context])
f = o ? o : @"";
if ((o = [theValues objectForKey: @"MiddleName"]) || ![self _isGhosted: @"MiddleName" inContext: context])
m = o ? o : @"";
if ((o = [theValues objectForKey: @"Title"]) || ![self _isGhosted: @"Title" inContext: context])
p = o ? o : @"";
if ((o = [theValues objectForKey: @"Suffix"]) || ![self _isGhosted: @"Suffix" inContext: context])
s = o ? o : @"";
[self setNWithFamily: l given: f additional: m prefixes: p suffixes: s];
// IM information
if ((o = [theValues objectForKey: @"IMAddress"]) || ![self _isGhosted: @"IMAddress" inContext: context])
[[self uniqueChildWithTag: @"x-aim"]
+3 -3
View File
@@ -2012,9 +2012,8 @@ FIXME
{
bodyPreferenceType = [[folderMetadata objectForKey: @"FolderOptions"] objectForKey: @"BodyPreferenceType"];
// By default, send MIME mails. See #3146 for details.
if (!bodyPreferenceType)
bodyPreferenceType = @"4";
bodyPreferenceType = @"1";
mimeSupport = [[folderMetadata objectForKey: @"FolderOptions"] objectForKey: @"MIMESupport"];
mimeTruncation = [[folderMetadata objectForKey: @"FolderOptions"] objectForKey: @"MIMETruncation"];
@@ -2593,7 +2592,6 @@ FIXME
//[output appendFormat: @"<Status>%d</Status>", 14];
}
[output appendString: @"<Collections>"];
s = nil;
// We enter our loop detection change
@@ -2696,6 +2694,8 @@ FIXME
//
if (changeDetected || [[[context request] headerForKey: @"MS-ASProtocolVersion"] isEqualToString: @"2.5"] || [[[context request] headerForKey: @"MS-ASProtocolVersion"] isEqualToString: @"12.0"])
{
[output appendString: @"<Collections>"];
// We always return the last generated response.
// If we only return <Sync><Collections/></Sync>,
// iOS powered devices will simply crash.
+90 -15
View File
@@ -44,6 +44,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#import <NGCards/iCalCalendar.h>
#import <NGCards/iCalEvent.h>
#import <NGCards/iCalAlarm.h>
#import <NGCards/iCalPerson.h>
#import <NGExtensions/NGBase64Coding.h>
@@ -90,6 +91,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#import <Appointments/SOGoAppointmentFolder.h>
#import <Appointments/SOGoAppointmentFolders.h>
#import <Appointments/SOGoAppointmentObject.h>
#import <Appointments/iCalEntityObject+SOGo.h>
#import <Contacts/SOGoContactGCSFolder.h>
#import <Contacts/SOGoContactFolders.h>
@@ -707,11 +709,13 @@ void handle_eas_terminate(int signum)
inContext: context
acquire: NO];
// update the cache anyway regardless of any error; if the rename fails next folderSync will to the cleanup
[folderToUpdate renameTo: [NSString stringWithFormat: @"%@", [displayName stringByEncodingImap4FolderName]]];
if (folderType == ActiveSyncEventFolder)
nameInCache = [NSString stringWithFormat: @"vevent/%@", serverId];
{
nameInCache = [NSString stringWithFormat: @"vevent/%@", serverId];
// We do the rename only for EventFolder. TaskFolder and EventFolder have the same target on server.
// In case of an error we expect the foldersync to do the cleanup.
[folderToUpdate renameTo: [NSString stringWithFormat: @"%@", [displayName stringByEncodingImap4FolderName]]];
}
else
nameInCache = [NSString stringWithFormat: @"vtodo/%@",serverId];
@@ -1626,11 +1630,11 @@ void handle_eas_terminate(int signum)
// ServerId might have been set if LongId was defined in the initial request. If not, it is
// a normal ItemOperations (Fetch) to get a complete email
if (!serverId)
serverId = [[(id)[theDocumentElement getElementsByTagName: @"ServerId"] lastObject] textValue];
serverId = [[(id)[aFetch getElementsByTagName: @"ServerId"] lastObject] textValue];
bodyPreferenceType = [[(id)[[(id)[theDocumentElement getElementsByTagName: @"BodyPreference"] lastObject] getElementsByTagName: @"Type"] lastObject] textValue];
bodyPreferenceType = [[(id)[[(id)[aFetch getElementsByTagName: @"BodyPreference"] lastObject] getElementsByTagName: @"Type"] lastObject] textValue];
[context setObject: bodyPreferenceType forKey: @"BodyPreferenceType"];
mimeSupport = [[(id)[theDocumentElement getElementsByTagName: @"MIMESupport"] lastObject] textValue];
mimeSupport = [[(id)[aFetch getElementsByTagName: @"MIMESupport"] lastObject] textValue];
[context setObject: mimeSupport forKey: @"MIMESupport"];
// https://msdn.microsoft.com/en-us/library/gg675490%28v=exchg.80%29.aspx
@@ -1644,10 +1648,10 @@ void handle_eas_terminate(int signum)
[s appendString: @"<Fetch>"];
[s appendString: @"<Status>1</Status>"];
if ([[[(id)[theDocumentElement getElementsByTagName: @"LongId"] lastObject] textValue] length])
if ([[[(id)[aFetch getElementsByTagName: @"LongId"] lastObject] textValue] length])
{
[s appendString: @"<Class xmlns=\"AirSync:\">Email</Class>"];
[s appendFormat: @"<LongId xmlns=\"Search:\">%@</LongId>", [[(id)[theDocumentElement getElementsByTagName: @"LongId"] lastObject] textValue]];
[s appendFormat: @"<LongId xmlns=\"Search:\">%@</LongId>", [[(id)[aFetch getElementsByTagName: @"LongId"] lastObject] textValue]];
}
else
{
@@ -1671,8 +1675,75 @@ void handle_eas_terminate(int signum)
}
else
{
[theResponse setStatus: 500];
return;
NSMutableDictionary *uidCache, *folderMetadata;
NSString *easId;
id sogoObject, currentCollection, componentObject;
// ServerId might have been set if LongId was defined in the initial request. If not, it is
// a normal ItemOperations (Fetch).
if (!serverId)
serverId = [[(id)[aFetch getElementsByTagName: @"ServerId"] lastObject] textValue];
currentCollection = [self collectionFromId: realCollectionId type: folderType];
folderMetadata = [self _folderMetadataForKey: [self _getNameInCache: realCollectionId withType: folderType]];
uidCache = [folderMetadata objectForKey: @"UidCache"];
if (uidCache)
{
easId = [[uidCache allKeysForObject: serverId] objectAtIndex: 0];
if (easId)
{
if (debugOn)
[self logWithFormat: @"EAS - Found easId: %@ for serverId: %@", easId, serverId];
}
else
{
if (debugOn)
[self logWithFormat: @"EAS - Use original serverId: %@", serverId];
easId = serverId;
}
}
else
easId = serverId;
sogoObject = [currentCollection lookupName: [easId sanitizedServerIdWithType: folderType]
inContext: context
acquire: NO];
if (folderType == ActiveSyncContactFolder)
componentObject = [sogoObject vCard];
else
componentObject = [sogoObject component: NO secure: YES];
[s appendString: @"<Fetch>"];
[s appendString: @"<Status>1</Status>"];
if ([[[(id)[aFetch getElementsByTagName: @"LongId"] lastObject] textValue] length])
{
if (folderType == ActiveSyncContactFolder)
[s appendString: @"<Class xmlns=\"AirSync:\">Contacts</Class>"];
else if (folderType == ActiveSyncEventFolder)
[s appendString: @"<Class xmlns=\"AirSync:\">Calendar</Class>"];
else if (folderType == ActiveSyncTaskFolder)
[s appendString: @"<Class xmlns=\"AirSync:\">Task</Class>"];
[s appendFormat: @"<LongId xmlns=\"Search:\">%@</LongId>", [[(id)[aFetch getElementsByTagName: @"LongId"] lastObject] textValue]];
}
else
{
[s appendFormat: @"<CollectionId xmlns=\"AirSyncBase:\">%@</CollectionId>", collectionId];
[s appendFormat: @"<ServerId xmlns=\"AirSyncBase:\">%@</ServerId>", serverId];
}
[s appendString: @"<Properties>"];
[s appendString: [componentObject activeSyncRepresentationInContext: context]];
[s appendString: @"</Properties>"];
[s appendString: @"</Fetch>"];
}
}
@@ -1793,6 +1864,7 @@ void handle_eas_terminate(int signum)
{
NSString *realCollectionId, *requestId, *easRequestId, *participationStatus, *calendarId;
SOGoAppointmentObject *appointmentObject;
iCalAlarm *alarm = nil;
SOGoMailObject *mailObject;
NSMutableDictionary *uidCache, *folderMetadata;
NSMutableString *s, *nameInCache;
@@ -1890,6 +1962,9 @@ void handle_eas_terminate(int signum)
event = [[calendar events] lastObject];
calendarId = [event uid];
// We take the organizers's alarm to start with
alarm = [event firstSupportedAlarm];
// Fetch the SOGoAppointmentObject
collection = [[context activeUser] personalCalendarFolderInContext: context];
nameInCache = [NSString stringWithFormat: @"vevent/%@", [collection nameInContainer]];
@@ -1910,7 +1985,7 @@ void handle_eas_terminate(int signum)
{
appointmentObject = [[SOGoAppointmentObject alloc] initWithName: [NSString stringWithFormat: @"%@.ics", [event uid]]
inContainer: collection];
[appointmentObject saveComponent: event force: YES];
[appointmentObject saveCalendar: [event parent]];
}
if (uidCache && [calendarId length] > 64)
@@ -1947,10 +2022,10 @@ void handle_eas_terminate(int signum)
participationStatus = @"TENTATIVE";
else
participationStatus = @"DECLINED";
[appointmentObject changeParticipationStatus: participationStatus
withDelegate: nil
alarm: nil];
alarm: alarm];
[s appendString: @"<?xml version=\"1.0\" encoding=\"utf-8\"?>"];
[s appendString: @"<!DOCTYPE ActiveSync PUBLIC \"-//MICROSOFT//DTD ActiveSync//EN\" \"http://www.microsoft.com/\">"];
@@ -2592,7 +2667,7 @@ void handle_eas_terminate(int signum)
[s appendString: @"<?xml version=\"1.0\" encoding=\"utf-8\"?>"];
[s appendString: @"<!DOCTYPE ActiveSync PUBLIC \"-//MICROSOFT//DTD ActiveSync//EN\" \"http://www.microsoft.com/\">"];
[s appendString: @"<Provision xmlns=\"Provision:\">"];
[s appendString: @"<AllowHTMLEmail>1</AllowHTMLEmail>"];
[s appendString: @"<Status>1</Status><Policies><Policy><PolicyType>MS-EAS-Provisioning-WBXML</PolicyType><Status>2</Status></Policy></Policies>"];
[s appendString: @"</Provision>"];
d = [[s dataUsingEncoding: NSUTF8StringEncoding] xml2wbxml];
+41 -5
View File
@@ -258,6 +258,38 @@ struct GlobalObjectId {
return rc;
}
//
//
//
- (NSString *) _personalNameFrom: (NSArray *) enveloppeAddresses
{
NGImap4EnvelopeAddress *address;
NSString *email, *rc, *name;
NSMutableArray *addresses;
int i, max;
rc = nil;
max = [enveloppeAddresses count];
if (max > 0)
{
addresses = [NSMutableArray array];
for (i = 0; i < max; i++)
{
address = [enveloppeAddresses objectAtIndex: i];
name = [address personalName];
email = [NSString stringWithFormat: @"%@", (name ? name : [address baseEMail])];
if (email)
[addresses addObject: email];
}
rc = [addresses componentsJoinedByString: @";"];
}
return rc;
}
//
//
//
@@ -740,7 +772,8 @@ struct GlobalObjectId {
}
}
return [theContent substringToIndex: i];
if (i >= 0)
return [theContent substringToIndex: i];
}
*wasTruncated = 0;
@@ -859,7 +892,13 @@ struct GlobalObjectId {
// If there are multiple e-mail addresses, they are separated by commas."
value = [self _emailAddressesFrom: [[self envelope] to]];
if (value)
[s appendFormat: @"<To xmlns=\"Email:\">%@</To>", [value activeSyncRepresentationInContext: context]];
{
[s appendFormat: @"<To xmlns=\"Email:\">%@</To>", [value activeSyncRepresentationInContext: context]];
// DisplayTo - If there are multiple display names, they are separated by semi-colons.
value = [self _personalNameFrom: [[self envelope] to]];
if (value)
[s appendFormat: @"<DisplayTo xmlns=\"Email:\">%@</DisplayTo>", [value activeSyncRepresentationInContext: context]];
}
// From
value = [self _emailAddressesFrom: [[self envelope] from]];
@@ -881,9 +920,6 @@ struct GlobalObjectId {
else
[s appendFormat: @"<DateReceived xmlns=\"Email:\">%@</DateReceived>", [[NSDate date] activeSyncRepresentationInContext: context]];
// DisplayTo
[s appendFormat: @"<DisplayTo xmlns=\"Email:\">%@</DisplayTo>", [[context activeUser] login]];
// Cc - same syntax as the To field
value = [self _emailAddressesFrom: [[self envelope] cc]];
if (value)
+29 -5
View File
@@ -187,7 +187,10 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
attendee = [attendees objectAtIndex: i];
[s appendFormat: @"<Attendee_Email xmlns=\"Calendar:\">%@</Attendee_Email>", [[attendee rfc822Email] activeSyncRepresentationInContext: context]];
[s appendFormat: @"<Attendee_Name xmlns=\"Calendar:\">%@</Attendee_Name>", [[attendee cn] activeSyncRepresentationInContext: context]];
if ([[attendee cn] length])
[s appendFormat: @"<Attendee_Name xmlns=\"Calendar:\">%@</Attendee_Name>", [[attendee cn] activeSyncRepresentationInContext: context]];
else
[s appendFormat: @"<Attendee_Name xmlns=\"Calendar:\">%@</Attendee_Name>", [[attendee rfc822Email] activeSyncRepresentationInContext: context]];
attendee_status = [self _attendeeStatus: attendee];
@@ -905,8 +908,15 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
component: (id) component
{
NSString *status;
iCalAlarm *alarm;
iCalPerson *attendee;
id o;
attendee = [self userAsAttendee: [context activeUser]];
status = [attendee partStat];
alarm = nil;
// See: https://msdn.microsoft.com/en-us/library/ee202290(v=exchg.80).aspx
//
// 0 == Free
@@ -925,11 +935,25 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
else
status = @"TENTATIVE";
// There's no delegate in EAS
[(SOGoAppointmentObject *) component changeParticipationStatus: status
withDelegate: nil
alarm: nil];
}
if ((o = [theValues objectForKey: @"Reminder"]) && [o length])
{
if ([self hasAlarms])
alarm = [[self firstSupportedAlarm] mutableCopy];
else
alarm = [[iCalAlarm alloc] init];
[alarm takeActiveSyncValues: theValues inContext: context];
}
// There's no delegate in EAS
[(SOGoAppointmentObject *) component changeParticipationStatus: status
withDelegate: nil
alarm: alarm];
RELEASE(alarm);
}
@end
+1
View File
@@ -42,6 +42,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#import <NGCards/iCalCalendar.h>
#import <NGCards/iCalDateTime.h>
#import <NGCards/iCalTimeZone.h>
#import <NGCards/iCalTrigger.h>
#import <Appointments/iCalEntityObject+SOGo.h>
+1 -1
View File
@@ -28,7 +28,7 @@
ProxyPass /SOGo http://127.0.0.1:20000/SOGo interpolate
ProxyPass / http://127.0.0.1:20000/SOGo/dav/ interpolate
<Location />
<IfVersion < 2.4>
<IfVersion < 2.4>
Order deny,allow
Allow from all
</IfVersion>
+1 -2
View File
@@ -6,7 +6,7 @@ Alias /SOGo/WebServerResources/ \
<Directory /usr/lib/GNUstep/SOGo/>
AllowOverride None
<IfVersion < 2.4>
<IfVersion < 2.4>
Order deny,allow
Allow from all
</IfVersion>
@@ -99,4 +99,3 @@ ProxyPass /SOGo http://127.0.0.1:20000/SOGo retry=0
RewriteRule ^/.well-known/caldav/?$ /SOGo/dav [R=301]
RewriteRule ^/.well-known/carddav/?$ /SOGo/dav [R=301]
</IfModule>
+3085
View File
File diff suppressed because it is too large. Load diff
+216
View File
@@ -1,3 +1,219 @@
commit 69cbe5d619561b69426a305d4b5d4f3e95e6370b
Author: Francis Lachapelle <flachapelle@inverse.ca>
Date: Thu Oct 31 13:25:48 2019 -0400
Preparation for release 4.1.1
M Documentation/docinfo.xml
M Documentation/includes/global-attributes.asciidoc
M NEWS
M Version
commit 82735fa8a7d5d0cf742fc8c5792024df3a53e9a5
Author: Francis Lachapelle <flachapelle@inverse.ca>
Date: Thu Oct 31 13:09:54 2019 -0400
chore(js/css): update generated files
M UI/WebServerResources/js/Common.js
M UI/WebServerResources/js/Common.js.map
M UI/WebServerResources/js/Scheduler.services.js
M UI/WebServerResources/js/Scheduler.services.js.map
M UI/WebServerResources/js/vendor/lodash.js
M UI/WebServerResources/js/vendor/lodash.min.js
commit 33dce130ceb76ed851bb197e67aad7f28f43447c
Author: Francis Lachapelle <flachapelle@inverse.ca>
Date: Thu Oct 31 13:08:28 2019 -0400
chore(js): fix path of lodash library
M UI/WebServerResources/Gruntfile.js
commit 372bd676aaf6f274aa11bd58217016d80c1080f1
Author: Francis Lachapelle <flachapelle@inverse.ca>
Date: Thu Oct 31 11:59:55 2019 -0400
chore: update commit message for generated files
M UI/WebServerResources/GNUmakefile
commit fc34befe47bd1661e30a6fcf7fea6775c410f551
Author: Francis Lachapelle <flachapelle@inverse.ca>
Date: Thu Oct 31 11:59:38 2019 -0400
docs: update CONTRIBUTING file
M .github/CONTRIBUTING.md
commit eb4ad5e086f3261f0ddb39175aad3d258d5a88af
Author: Francis Lachapelle <flachapelle@inverse.ca>
Date: Thu Oct 31 11:52:43 2019 -0400
i18n(de): update German translation
M UI/MailerUI/German.lproj/Localizable.strings
commit a2a6bfab59f9be75ca671a49463fe8040a493d58
Author: Ludovic Marcotte <lmarcotte@inverse.ca>
Date: Thu Oct 31 09:04:10 2019 -0400
fix(core): avoid warning by including missing file
M ActiveSync/iCalToDo+ActiveSync.m
commit 22bd650b72249ff314484577e86cef4b0c8c73d2
Author: InverseBot <sogo@inverse.ca>
Date: Thu Oct 31 02:07:08 2019 -0400
(js/css) Update generated files
M UI/WebServerResources/js/Common.js
M UI/WebServerResources/js/Common.js.map
M UI/WebServerResources/js/Scheduler.services.js
M UI/WebServerResources/js/Scheduler.services.js.map
commit d4f0ccd3f726b78673b02ad47c34eef0dc35f207
Author: Francis Lachapelle <flachapelle@inverse.ca>
Date: Wed Oct 30 13:54:06 2019 -0400
docs: update CONTRIBUTING file
M .github/CONTRIBUTING.md
commit 8b45f2c731d1a015c1d9c9639b3e76488c696770
Author: Francis Lachapelle <flachapelle@inverse.ca>
Date: Wed Oct 30 13:46:07 2019 -0400
fix(calendar(js)): swap start-end dates when delta is negative
When creating an event by drag'n'dropping from bottom to top, the
original coordinate become the start date.
M UI/WebServerResources/js/Scheduler/CalendarListController.js
M UI/WebServerResources/js/Scheduler/Component.service.js
commit 060dea38182a52c19c4dd149b5b07f834870b550
Author: Francis Lachapelle <flachapelle@inverse.ca>
Date: Wed Oct 30 13:42:45 2019 -0400
style(mail(js)): add missing semi-colon
M UI/WebServerResources/js/Mailer/MessageEditorController.js
commit 487bbf74c042ed29e3da40a68cfaf769f8bcc381
Author: Francis Lachapelle <flachapelle@inverse.ca>
Date: Wed Oct 30 11:42:53 2019 -0400
fix(calendar(js)): don't modify time when computing dates interval
Fixes #4861
M UI/WebServerResources/js/Common/utils.js
commit d1e2418d65ee33496363cceac27d372c656aba04
Author: Ludovic Marcotte <lmarcotte@inverse.ca>
Date: Wed Oct 30 13:44:13 2019 -0400
(fix) fixed EAS provisioning support for Outlook/iOS (fixes #4853)
M ActiveSync/SOGoActiveSyncDispatcher+Sync.m
M ActiveSync/SOGoActiveSyncDispatcher.m
M NEWS
commit 13ad6ff8755e59670071212cfddd178bb8846ee9
Author: Francis Lachapelle <flachapelle@inverse.ca>
Date: Wed Oct 30 11:13:40 2019 -0400
Initial CONTRIBUTING file
A .github/CONTRIBUTING.md
commit f6ca946058f16ae32bbde966ff1c850e4979db0f
Author: Francis Lachapelle <flachapelle@inverse.ca>
Date: Wed Oct 30 11:04:59 2019 -0400
fix(calendar): don't allow RDATE unless already defined
Since RDATE are not properly supported in EAS, hide the possibility to
specify recurring dates unless the component being edited already
contains RDATE(s).
M UI/Scheduler/UIxCalMainView.m
M UI/Templates/SchedulerUI/UIxAppointmentEditorTemplate.wox
M UI/Templates/SchedulerUI/UIxCalMainView.wox
M UI/Templates/SchedulerUI/UIxTaskEditorTemplate.wox
M UI/WebServerResources/js/Scheduler/ComponentController.js
commit 42bb3b288b65842dc869be94f1f8b461dd326233
Author: Francis Lachapelle <flachapelle@inverse.ca>
Date: Tue Oct 29 11:02:47 2019 -0400
(i18n) Update Polish translation
Fixes #4851
M UI/PreferencesUI/Polish.lproj/Localizable.strings
commit 61945734c73117f595e19f960defa7238ebb41f9
Author: Ludovic Marcotte <lmarcotte@inverse.ca>
Date: Mon Oct 28 15:14:35 2019 -0400
(doc) quote default values (fixes #4827)
M Scripts/sogo.conf
commit e866fd5fdaa677879ef611ff72e57140ea664f43
Author: Ludovic Marcotte <lmarcotte@inverse.ca>
Date: Mon Oct 28 14:39:39 2019 -0400
(fix) fixed Apple Calendar creation (fixes #4813)
M NEWS
M SoObjects/Appointments/SOGoAppointmentFolderObject.m
M SoObjects/Appointments/SOGoAppointmentFolders.m
M SoObjects/SOGo/SOGoGCSFolder.m
M UI/Contacts/UIxContactFolderProperties.m
M UI/Scheduler/UIxCalendarProperties.m
commit 7ff992170900e6e51b5655bc9f5df93711d7f20d
Author: Ludovic Marcotte <lmarcotte@inverse.ca>
Date: Fri Oct 25 13:34:41 2019 -0400
(fix) removed unused method
M SoObjects/Mailer/SOGoMailAccount.h
M SoObjects/Mailer/SOGoMailAccount.m
commit 34fcfe024b8b8e02d2f7c3e26105d4075a3a07e8
Author: Ludovic Marcotte <lmarcotte@inverse.ca>
Date: Fri Oct 25 13:31:13 2019 -0400
(fix) use the supplied Sieve creds to fetch the IMAP4 separator (fixes #4846)
M NEWS
M SoObjects/SOGo/SOGoSieveManager.h
M SoObjects/SOGo/SOGoSieveManager.m
commit 2d5d1b4297480648cf0404f218ceaf5e5ae487bc
Author: InverseBot <sogo@inverse.ca>
Date: Fri Oct 25 01:27:37 2019 -0400
(js/css) Update generated files
M UI/WebServerResources/js/Common.js
M UI/WebServerResources/js/Common.js.map
M UI/WebServerResources/js/Scheduler.services.js
M UI/WebServerResources/js/Scheduler.services.js.map
commit df15fed8fddb66765765e343e3da6e46ac03de72
Author: Francis Lachapelle <flachapelle@inverse.ca>
Date: Thu Oct 24 10:43:20 2019 -0400
Update ChangeLog
M ChangeLog
commit 1c179f805da50d087ca07bdadb66c9563a41d45e
Author: Francis Lachapelle <flachapelle@inverse.ca>
Date: Thu Oct 24 10:42:30 2019 -0400
+63 -26
View File
@@ -1028,6 +1028,11 @@ _bindDN_ and _bindPassword_ will still be required to find the proper DN
|bindFields (optional)
|An array of fields to use when doing indirect binds.
|lookupFields (optional)
|Lookup fields for LDAP queries. Default is `(*)`. This can be utilized
to lookup operational fields (which are per default not part of the result)
such as `memberOf`: `lookupFields = ("*", "memberOf");`
|hostname
|A space-delimited list of LDAP URLs or LDAP hostnames.
@@ -1069,7 +1074,7 @@ Possible values are: `none`, `plain`, `crypt`, `md5`, `md5-crypt`,
(plus setting of the encoding with `.b64` or `.hex`).
For a more detailed description see
http://wiki.dovecot.org/Authentication/PasswordSchemes.
https://doc.dovecot.org/configuration_manual/authentication/password_schemes/.
Note that `cram-md5` is not actually using cram-md5 (due to the lack of
challenge-response mechanism), its just saving the intermediate MD5
@@ -1106,7 +1111,7 @@ not work for entries in this source and thus, freebusy lookups.
repository
|listRequiresDot (optional)
|If set to `YES`, listing of this LDAP source is only possible when performing a search (respecting the SOGoSearchMinimumWordLength parameter) or when explicitely typing a single dot.
|If set to `YES`, listing of this LDAP source is only possible when performing a search (respecting the SOGoSearchMinimumWordLength parameter) or when explicitly typing a single dot.
Defaults to `YES` when unset.
|ModulesConstraints (optional)
@@ -1659,12 +1664,14 @@ they have the same name as popular LDAP attributes (such as `givenName`,
|The default algorithm used for password encryption when changing
passwords. Possible values are: `none`, `plain`, `crypt`, `md5`,
`md5-crypt`, `smd5`, `cram-md5`, `ldap-md5`, and `sha`, `sha256`,
`sha512` and its ssha (e.g. `ssha` or `ssha256`) variants. Passwords can
have the scheme prepended in the form `{scheme}encryptedPass`.
`sha256-crypt`, `sha512`, `sha512-crypt`, its ssha (e.g. `ssha` or
`ssha256`) variants, `blf-crypt`, `PBKDF2`, and `sym-aes-128-cbc`.
Passwords can have the scheme prepended in the form
`{scheme}encryptedPass`.
If no scheme is given, _userPasswordAlgorithm_ is used instead. The
schemes listed above follow the algorithms described in
http://wiki.dovecot.org/Authentication/PasswordSchemes.
https://doc.dovecot.org/configuration_manual/authentication/password_schemes/.
Note that `cram-md5` is not actually using cram-md5 (due to the lack of
challenge-response mechanism), its just saving the intermediate MD5
@@ -1673,7 +1680,13 @@ context as Dovecot stores in its database. 
|prependPasswordScheme
|The default behaviour is to store newly set passwords without the
scheme (default: `NO`). This can be overridden by setting to `YES` and
will result in passwords stored as `{scheme}encryptedPass`. 
will result in passwords stored as `{scheme}encryptedPass`. For
`sym-aes-128-cbc`, always set this to `NO`.
|keyPath
|For `sym-aes-128-cbc`, a global key file is required. This value
must be set to the full path where the key file is. The key file
must also be readable by the `sogo` user.
|canAuthenticate
|If set to `YES`, this SQL source is used for authentication.
@@ -1737,7 +1750,7 @@ See the _Multi-domains Configuration_ section in this document for more
information.
|listRequiresDot (optional)
|If set to `YES`, listing of this SQL source is only possible when performing a search (respecting the SOGoSearchMinimumWordLength parameter) or when explicitely typing a single dot.
|If set to `YES`, listing of this SQL source is only possible when performing a search (respecting the SOGoSearchMinimumWordLength parameter) or when explicitly typing a single dot.
Defaults to `YES` when unset.
|ModulesConstraints (optional)
@@ -1806,10 +1819,16 @@ are:
|D |SOGoSMTPServer
|The DNS name or IP address of the SMTP server used when
_SOGoMailingMechanism_ is set to `smtp`.
Supported formats are: `smtp://domain:port`, `smtps://domain`,
`domain:port`, `smtp://domain:port/?tls=YES`. Using the option
`tls=YES` will enforce using STARTTLS smtp connections. Thus,
`smtp://localhost:587/?tls=YES` would use the default MUA port
on localhost with STARTTLS enforced.
|D |SOGoSMTPAuthenticationType
|Activate SMTP authentication and specifies which type is in use.
Current, only `PLAIN` is supported and other values will be ignored.
Current, only `PLAIN` is supported and other values will cause
the authentication to fail.
|S |WOSendMail
|The path of the sendmail binary.
@@ -1844,16 +1863,18 @@ messages.
Defaults to `Drafts` when unset.
Use a `/` as a hierarchy separator if referring to an IMAP subfolder.
For example: `INBOX/Drafts`.
Use a `/` as a hierarchy separator if referring to an IMAP subfolder.
For example: `INBOX/Drafts`. You must use a `/` even if your real
IMAP separator is a `.`.
|U |SOGoSentFolderName
|Parameter used to set the IMAP folder name used to store sent messages.
Defaults to `Sent` when unset.
Use a `/` as a hierarchy separator if referring to an IMAP subfolder.
For example: `INBOX/Sent`.
Use a `/` as a hierarchy separator if referring to an IMAP subfolder.
For example: `INBOX/Sent`. You must use a `/` even if your real
IMAP separator is a `.`.
|U |SOGoTrashFolderName
|Parameter used to set the IMAP folder name used to store deleted
@@ -1861,8 +1882,9 @@ messages.
Defaults to `Trash` when unset.
Use a `/` as a hierarchy separator if referring to an IMAP subfolder.
For example: `INBOX/Trash`.
Use a `/` as a hierarchy separator if referring to an IMAP subfolder.
For example: `INBOX/Trash`. You must use a `/` even if your real
IMAP separator is a `.`.
|U |SOGoJunkFolderName
|Parameter used to set the IMAP folder name used to store junk
@@ -1870,8 +1892,9 @@ messages.
Defaults to `Junk` when unset.
Use a `/` as a hierarchy separator if referring to an IMAP subfolder.
For example: `INBOX/Junk`. Also see the SOGoMailJunkSettings for
Use a `/` as a hierarchy separator if referring to an IMAP subfolder.
For example: `INBOX/Junk`. You must use a `/` even if your real
IMAP separator is a `.`. Also see the SOGoMailJunkSettings for
more options regarding junk/not-junk actions.
|D |SOGoIMAPCASServiceName
@@ -2009,7 +2032,7 @@ values are:
* `Mail`
* `Contacts`
Defaults to `Calendar` when unset.
Defaults to `Mail` when unset.
|S |SOGoFaviconRelativeURL
|Parameter used to specify the relative URL of the site favion.
@@ -2071,13 +2094,17 @@ Requires Sieve script support on the IMAP host.
Defaults to `NO` when unset.
When enabling this parameter, one may have to also enable the associated
|D |SOGoVacationPeriodEnabled
|When enabling this parameter, one may have to also enable the associated
cronjob in `/etc/cron.d/sogo` in order to activate automatic vacation
message activation and expiration.
message activation and expiration if your Sieve server does not support
the date extension.
See the _Cronjob — Vacation messages activation and expiration_ section
below for details.
Defaults to `YES` when unset.
|D |SOGoVacationDefaultSubject
|Parameter used to define a default vacation subject if user don't specify a
custom subject.
@@ -2134,6 +2161,16 @@ host.
Defaults to `NO` when unset.
|D |SOGoSieveScriptHeaderTemplateFile
|Parameter used to set the full path of the Sieve script that will be
automatically prepended to any Sieve scripts a user might define. The file
must be encoded in UTF-8 and it must also respect the RFC5228 syntax.
|D |SOGoSieveScriptFooterTemplateFile
|Parameter used to set the full path of the Sieve script that will be
automatically appended to any Sieve scripts a user might define. The file
must be encoded in UTF-8 and it must also respect the RFC5228 syntax.
|U |SOGoSieveFilters
|Parameter used to define initial Sieve scripts for users. The user
can still modify the scripts and the initial values will be written
@@ -2352,7 +2389,7 @@ Defaults to `below`.
* `text`
* `html`
Defaults to `text`.
Defaults to `html`.
|S |SOGoEnableEMailAlarms
|Parameter used to enable email-based alarms on events and tasks.
@@ -2443,7 +2480,7 @@ like this:
SOGoTrashFolderName = Trash;
SOGoJunkFolderName = Junk;
SOGoMailingMechanism = smtp;
SOGoSMTPServer = 127.0.0.1;
SOGoSMTPServer = "smtp://127.0.0.1";
SOGoUserSources = (
{
type = ldap;
@@ -2999,10 +3036,10 @@ To access your personal calendar:
`http://127.0.0.1/SOGo/dav/jdoe/Calendar/personal/`
* Click on Continue.
Apple Calendar and iOS
~~~~~~~~~~~~~~~~~~~~~~
Apple Calendar (macOS, iOS, iPadOS)
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Apple Calendar and Mac OS X and the calendar application on iOS can also be used
Apple Calendar on macOS, iOS, and iPadOS can also be used
as a client application for SOGo.
To configure the application so it works with SOGo, create a new account and specify,
@@ -3055,8 +3092,8 @@ 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).
Microsoft ActiveSync / Mobile Devices
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Microsoft ActiveSync
~~~~~~~~~~~~~~~~~~~~
You can synchronize contacts, emails, events and tasks from SOGo with
any mobile devices that support Microsoft ActiveSync. Microsoft Outlook
@@ -26,7 +26,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
http://www.sogo.nu/downloads/documentation.html.
https://sogo.nu/downloads/documentation.html.
Introduction
------------
@@ -263,7 +263,7 @@ The syntax is:
----
"<extension ID>"
=> array( "application" => "thunderbird",
"version" => "<exact version found in the extension's install.rdf file>",
"version" => "<exact version found in the extension's manifest.json file>",
"filename" => "<exact filename on the filesystem>" )
----
@@ -25,7 +25,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 http://www.sogo.nu/downloads/documentation.html.
at https://sogo.nu/downloads/documentation.html.
Introduction
------------
@@ -42,9 +42,8 @@ Project Links
The project source and binaries for installation are hosted on GitHub and SourceForge or can be downloaded from https://caldavsynchronizer.org[caldavsynchronizer.org]
https://github.com/aluxnimm/outlookcaldavsynchronizer
https://sourceforge.net/projects/outlookcaldavsynchronizer/
* https://github.com/aluxnimm/outlookcaldavsynchronizer
* https://sourceforge.net/projects/outlookcaldavsynchronizer/
Social Media
~~~~~~~~~~~~
+3 -3
View File
@@ -1,7 +1,7 @@
<!-- TODO have the build system take care of this -->
<releaseinfo>Version 4.1.0 - October 2019</releaseinfo>
<subtitle>for version 4.1.0</subtitle>
<date>2019-10-24</date>
<releaseinfo>Version 4.3.2 - May 2020</releaseinfo>
<subtitle>for version 4.3.2</subtitle>
<date>2020-05-06</date>
<legalnotice>
<para>Permission is granted to copy, distribute and/or modify this document under the terms of the GNU Free Documentation License, Version 1.2 or any later version published by the Free Software Foundation; with no Invariant Sections, no Front-Cover Texts, and no Back-Cover Texts. A copy of the license is included in the section entitled "GNU Free Documentation License".</para>
@@ -6,13 +6,13 @@
Authors:
- Inverse inc. <info@inverse.ca>
Copyright (C) 2008-2019 Inverse inc.
Copyright (C) 2008-2020 Inverse inc.
License: GFDL 1.2 or later. http://www.gnu.org/licenses/fdl.html
////
// TODO have the build system take care of this
:release_version: 4.1.0
:release_version: 4.3.2
// vim: set syntax=asciidoc tabstop=2 shiftwidth=2 expandtab:
-2946
View File
File diff suppressed because it is too large. Load diff
+1 -1
View File
@@ -28,7 +28,7 @@
@interface EOQualifier(GCS)
- (void)_gcsAppendToString:(NSMutableString *)_ms;
- (void) _gcsAppendToString: (NSMutableString *) _ms;
@end
+36 -25
View File
@@ -32,8 +32,8 @@
@implementation EOQualifier(GCS)
- (void)_appendAndQualifier:(EOAndQualifier *)_q
toString:(NSMutableString *)_ms
- (void) _appendAndQualifier: (EOAndQualifier *) _q
toString: (NSMutableString *) _ms
{
// TODO: move to EOQualifier category
NSArray *qs;
@@ -46,12 +46,12 @@
for (i = 0; i < count; i++) {
if (i != 0) [_ms appendString:@" AND "];
if (count > 1) [_ms appendString:@"("];
[[qs objectAtIndex:i] _gcsAppendToString:_ms];
[[qs objectAtIndex:i] _gcsAppendToString: _ms];
if (count > 1) [_ms appendString:@")"];
}
}
- (void)_appendOrQualifier:(EOAndQualifier *)_q
toString:(NSMutableString *)_ms
- (void)_appendOrQualifier: (EOAndQualifier *) _q
toString: (NSMutableString *) _ms
{
// TODO: move to EOQualifier category
NSArray *qs;
@@ -64,21 +64,21 @@
for (i = 0; i < count; i++) {
if (i != 0) [_ms appendString:@" OR "];
if (count > 1) [_ms appendString:@"("];
[[qs objectAtIndex:i] _gcsAppendToString:_ms];
[[qs objectAtIndex:i] _gcsAppendToString: _ms];
if (count > 1) [_ms appendString:@")"];
}
}
- (void)_appendNotQualifier:(EONotQualifier *)_q
toString:(NSMutableString *)_ms
- (void)_appendNotQualifier: (EONotQualifier *)_q
toString:(NSMutableString *) _ms
{
[_ms appendString:@" NOT ("];
[[_q qualifier] _gcsAppendToString:_ms];
[[_q qualifier] _gcsAppendToString: _ms];
[_ms appendString:@")"];
}
- (void)_appendKeyValueQualifier:(EOKeyValueQualifier *)_q
toString:(NSMutableString *)_ms
- (void) _appendKeyValueQualifier: (EOKeyValueQualifier *) _q
toString: (NSMutableString *) _ms
{
id val;
NSString *qKey, *qOperator, *qValue, *qFormat;
@@ -115,10 +115,13 @@
qOperator = @"=";
}
if ([val isKindOfClass:[NSNumber class]])
if ([val isKindOfClass: [NSNumber class]])
qValue = [val stringValue];
else if ([val isKindOfClass:[NSString class]]) {
qValue = [NSString stringWithFormat: @"'%@'", val];
else if ([val isKindOfClass: [NSString class]]) {
if ([(EOKeyValueQualifier *)self formatted])
qValue = val;
else
qValue = [NSString stringWithFormat: @"'%@'", val];
}
else {
qValue = @"NULL";
@@ -151,23 +154,31 @@
[_ms appendFormat: qFormat, qKey, qOperator, qValue];
}
- (void)_appendQualifier:(EOQualifier *)_q toString:(NSMutableString *)_ms {
- (void) _appendQualifier: (EOQualifier *) _q
toString: (NSMutableString *) _ms
{
if (_q == nil) return;
if ([_q isKindOfClass:[EOAndQualifier class]])
[self _appendAndQualifier:(id)_q toString:_ms];
else if ([_q isKindOfClass:[EOOrQualifier class]])
[self _appendOrQualifier:(id)_q toString:_ms];
else if ([_q isKindOfClass:[EOKeyValueQualifier class]])
[self _appendKeyValueQualifier:(id)_q toString:_ms];
else if ([_q isKindOfClass:[EONotQualifier class]])
[self _appendNotQualifier:(id)_q toString:_ms];
if ([_q isKindOfClass: [EOAndQualifier class]])
[self _appendAndQualifier: (id)_q
toString: _ms];
else if ([_q isKindOfClass: [EOOrQualifier class]])
[self _appendOrQualifier: (id)_q
toString:_ms];
else if ([_q isKindOfClass: [EOKeyValueQualifier class]])
[self _appendKeyValueQualifier: (id)_q
toString:_ms];
else if ([_q isKindOfClass: [EONotQualifier class]])
[self _appendNotQualifier: (id)_q
toString:_ms];
else
[self errorWithFormat:@"unknown qualifier: %@", _q];
}
- (void)_gcsAppendToString:(NSMutableString *)_ms {
[self _appendQualifier:self toString:_ms];
- (void) _gcsAppendToString: (NSMutableString *) _ms
{
[self _appendQualifier: self
toString: _ms];
}
@end /* EOQualifier(GCS) */
+20 -9
View File
@@ -38,20 +38,24 @@
- "c_version" (update revision of the file in the folder)
*/
@class NSArray;
@class NSDictionary;
@class NSException;
@class NSMutableArray;
@class NSMutableString;
@class NSNumber;
@class NSString;
@class NSURL;
@class NSNumber;
@class NSArray;
@class NSMutableArray;
@class NSException;
@class NSMutableString;
@class NSDictionary;
@class EOQualifier;
@class EOFetchSpecification;
@class EOAdaptor;
@class EOAdaptorChannel;
@class EOAttribute;
@class EOFetchSpecification;
@class EOKeyValueQualifier;
@class EOQualifier;
@class GCSChannelManager;
@class GCSFolderManager;
@class GCSFolderType;
@class GCSChannelManager;
@interface GCSFolder : NSObject
{
@@ -149,6 +153,13 @@
- (NSCalendarDate *) lastModificationDate;
/* helpers */
- (EOAttribute *) _attributeForColumn: (NSString *) _field;
- (void) _findQualifiers: (id) qualifier
withAdaptor: (EOAdaptor *) adaptor;
- (void) _formatQualifierValue: (EOKeyValueQualifier *) qualifier
withAdaptor: (EOAdaptor *) adaptor;
@end
#endif /* __GDLContentStore_GCSFolder_H__ */
+66
View File
@@ -380,10 +380,31 @@ static GCSStringFormatter *stringFormatter = nil;
- (NSString *) _sqlForQualifier: (EOQualifier *) qualifier
{
static EOAdaptor *adaptor = nil;
NSMutableString *ms;
if (qualifier)
{
if (!adaptor)
{
EOAdaptorContext *adaptorCtx;
EOAdaptorChannel *channel;
channel = [self acquireStoreChannel];
adaptorCtx = [channel adaptorContext];
adaptor = [adaptorCtx adaptor];
}
if ([qualifier isKindOfClass: [EOAndQualifier class]])
[self _findQualifiers: (id)qualifier withAdaptor: adaptor];
else if ([qualifier isKindOfClass: [EOOrQualifier class]])
[self _findQualifiers: (id)qualifier withAdaptor: adaptor];
else if ([qualifier isKindOfClass: [EOKeyValueQualifier class]])
[self _formatQualifierValue: (EOKeyValueQualifier *)qualifier withAdaptor: adaptor];
else if ([qualifier isKindOfClass: [EONotQualifier class]])
[self _formatQualifierValue: (EOKeyValueQualifier *)[(id)qualifier qualifier] withAdaptor: adaptor];
else
[self errorWithFormat:@"unknown qualifier: %@", qualifier];
ms = [NSMutableString stringWithCapacity:32];
[qualifier _gcsAppendToString: ms];
}
@@ -393,6 +414,51 @@ static GCSStringFormatter *stringFormatter = nil;
return ms;
}
- (void) _findQualifiers: (id) qualifier
withAdaptor: (EOAdaptor *) adaptor
{
NSArray *qs;
unsigned i, count;
if (qualifier == nil) return;
qs = [qualifier qualifiers];
if ((count = [qs count]) == 0)
return;
for (i = 0; i < count; i++) {
id q = [qs objectAtIndex: i];
if ([q isKindOfClass: [EOAndQualifier class]])
[self _findQualifiers: q withAdaptor: adaptor];
else if ([q isKindOfClass:[EOOrQualifier class]])
[self _findQualifiers: q withAdaptor: adaptor];
else if ([q isKindOfClass:[EOKeyValueQualifier class]])
[self _formatQualifierValue: (EOKeyValueQualifier *)q withAdaptor: adaptor];
else if ([q isKindOfClass:[EONotQualifier class]])
[self _formatQualifierValue: (EOKeyValueQualifier *)[q qualifier] withAdaptor: adaptor];
else
[self errorWithFormat:@"unknown qualifier: %@", q];
}
}
- (void) _formatQualifierValue: (EOKeyValueQualifier *) qualifier
withAdaptor: (EOAdaptor *) adaptor
{
NSString *field;
EOAttribute *attribute;
NSString *formattedValue;
field = [qualifier key];
attribute = [self _attributeForColumn: field];
if (attribute)
{
formattedValue = [adaptor formatValue: [qualifier value]
forAttribute: attribute];
[qualifier setValue: formattedValue];
[qualifier setFormatted: YES];
}
}
- (NSString *)_sqlForSortOrderings:(NSArray *)_so {
NSMutableString *sql;
unsigned i, count;
+6 -6
View File
@@ -1,8 +1,6 @@
/* CardVersitRenderer.m - this file is part of SOPE
*
* Copyright (C) 2006-2012 Inverse inc.
*
* Author: Wolfgang Sourdeau <wsourdeau@inverse.ca>
* Copyright (C) 2006-2020 Inverse inc.
*
* This file is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -49,7 +47,7 @@
- (NSString *) renderElement: (CardElement *) anElement
{
NSMutableString *rendering;
NSMutableString *rendering, *buffer;
NSDictionary *attributes;
NSMutableDictionary *values;
NSString *finalRendering, *tag;
@@ -73,10 +71,12 @@
attributes = [anElement attributes];
if ([attributes count])
{
[rendering appendString: @";"];
[attributes versitRenderInString: rendering
buffer = [NSMutableString string];
[attributes versitRenderInString: buffer
withKeyOrdering: [anElement orderOfAttributeKeys]
asAttributes: YES];
if ([buffer length])
[rendering appendFormat: @";%@", buffer];
}
/* values */
+1 -1
View File
@@ -192,7 +192,7 @@ static inline id GetProperty(NSDictionary *_set, NSString *_name) {
if ([val respondsToSelector:@selector(descriptionWithCalendarFormat:)]) {
static NSTimeZone *gmt = nil;
if (gmt == nil) gmt = [NSTimeZone timeZoneWithAbbreviation:@"GMT"];
if (gmt == nil) gmt = [NSTimeZone timeZoneForSecondsFromGMT: 0];
[val setTimeZone:gmt];
val = [val descriptionWithCalendarFormat:calfmt];
}
+1 -1
View File
@@ -31,7 +31,7 @@
static NSTimeZone *gmt = nil;
static inline void _setupGMT(void) {
if (gmt == nil)
gmt = [[NSTimeZone timeZoneWithAbbreviation:@"GMT"] retain];
gmt = [[NSTimeZone timeZoneForSecondsFromGMT: 0] retain];
}
@implementation NSCalendarDate(iCalRepresentation)
+1 -1
View File
@@ -337,7 +337,7 @@
second = 0;
}
utc = [NSTimeZone timeZoneWithAbbreviation: @"GMT"];
utc = [NSTimeZone timeZoneForSecondsFromGMT: 0];
date = [NSCalendarDate dateWithYear: year month: month
day: day hour: hour minute: minute
second: second
+1 -1
View File
@@ -107,7 +107,7 @@
NSCalendarDate *utcStart, *utcEnd;
NSTimeZone *uTZ;
uTZ = [NSTimeZone timeZoneWithAbbreviation: @"GMT"];
uTZ = [NSTimeZone timeZoneForSecondsFromGMT: 0];
utcStart = [start copy];
utcEnd = [end copy];
[utcStart setTimeZone: uTZ];
@@ -99,12 +99,12 @@ static BOOL NGMonthDaySet_fillWithByMonthDay (NGMonthDaySet *positiveDaySet,
if (dayInMonth > 31)
{
ok = NO;
continue; /* error, value to large */
continue; /* error, value too large */
}
if (dayInMonth < -31)
{
ok = NO;
continue; /* error, value to large */
continue; /* error, value too large */
}
/* adjust negative days */
@@ -160,7 +160,7 @@ static inline unsigned iCalDoWForNSDoW (int dow)
/* create range and check whether its in the requested range */
r = [[NGCalendarDateRange alloc] initWithStartDate: _startDate endDate: end];
if ([_r containsDateRange: r])
if ([_r doesIntersectWithDateRange: r])
[_ranges addObject: r];
[r release];
r = nil;
+1 -1
View File
@@ -300,7 +300,7 @@
= [(iCalDateTime *) [self uniqueChildWithTag: @"dtstart"] dateTime];
else if ([rrule untilDate] == nil || [refDate compare: [rrule untilDate]] == NSOrderedAscending)
tmpDate = [self _occurrenceForDate: refDate byRRule: rrule];
else if ([refDate compare: [rrule untilDate]] == NSOrderedDescending)
else if ([[self _occurrenceForDate: refDate byRRule: rrule] compare: [rrule untilDate] ] == NSOrderedAscending)
tmpDate = [rrule untilDate];
return tmpDate;
+2 -2
View File
@@ -4,9 +4,9 @@ set -o pipefail
#set -x
PROGNAME="$(basename $0)"
BACKUP_DIR=~sogo/backups
BACKUP_DIR=${BACKUP_DIR:-~sogo/backups}
SOGO_TOOL=/usr/sbin/sogo-tool
DAYS_TO_KEEP="30"
DAYS_TO_KEEP=${DAYS_TO_KEEP:-30}
DATE=$(date +%F_%H%M)
LOG="logger -t $PROGNAME -p daemon.info"
+6 -5
View File
@@ -24,9 +24,10 @@
//SOGoDraftsFolderName = Drafts;
//SOGoSentFolderName = Sent;
//SOGoTrashFolderName = Trash;
//SOGoIMAPServer = localhost;
//SOGoSieveServer = sieve://127.0.0.1:4190;
//SOGoSMTPServer = 127.0.0.1;
//SOGoJunkFolderName = Junk;
//SOGoIMAPServer = "localhost";
//SOGoSieveServer = "sieve://127.0.0.1:4190";
//SOGoSMTPServer = "smtp://127.0.0.1";
//SOGoMailDomain = acme.com;
//SOGoMailingMechanism = smtp;
//SOGoForceExternalLoginWithEmail = NO;
@@ -54,7 +55,7 @@
// bindPassword = qwerty;
// canAuthenticate = YES;
// displayName = "Shared Addresses";
// hostname = ldap://127.0.0.1:389;
// hostname = "ldap://127.0.0.1:389";
// id = public;
// isAddressBook = YES;
// }
@@ -72,7 +73,7 @@
// bindPassword = password;
// canAuthenticate = YES;
// displayName = "Public";
// hostname = ldap://127.0.0.1:389;
// hostname = "ldap://127.0.0.1:389";
// filter = "mail = '*'";
// id = directory;
// isAddressBook = YES;
@@ -76,7 +76,7 @@ fi
if ! [ "$CHECK" = "1" ]; then
cat >> $SQLFILE <<HERE
ALTER TABLE $TABLE MODIFY COLUMN c_location integer NULL;
ALTER TABLE $TABLE MODIFY COLUMN c_location varchar(2048) NULL;
CREATE TABLE sogo_store
(
@@ -195,7 +195,7 @@ do
echo "DROP TABLE $QUICKTABLE;" >> $SQLFILE
echo "DROP TABLE $STORETABLE;" >> $SQLFILE
echo "DROP TABLE $ACLTABLE;" >> $SQLFILE
echo "UPDATE sogo_folder_info SET c_location = NULL, c_quick_location = NULL, c_acl_location = NULL WHERE c_folder_id = $FOLDERID;" >> $SQLFILE
echo "UPDATE $TABLE SET c_location = NULL, c_quick_location = NULL, c_acl_location = NULL WHERE c_folder_id = $FOLDERID;" >> $SQLFILE
echo >> $SQLFILE
done
+1 -1
View File
@@ -193,7 +193,7 @@ do
echo "DROP TABLE $QUICKTABLE;" >> $SQLFILE
echo "DROP TABLE $STORETABLE;" >> $SQLFILE
echo "DROP TABLE $ACLTABLE;" >> $SQLFILE
echo "UPDATE sogo_folder_info SET c_location = NULL, c_quick_location = NULL, c_acl_location = NULL WHERE c_folder_id = $FOLDERID;" >> $SQLFILE
echo "UPDATE $TABLE SET c_location = NULL, c_quick_location = NULL, c_acl_location = NULL WHERE c_folder_id = $FOLDERID;" >> $SQLFILE
echo >> $SQLFILE
done < $TABLEFILE
@@ -7,6 +7,7 @@ vevent_class2 = "(Skrytá událost)";
vtodo_class0 = "(Veřejný úkol)";
vtodo_class1 = "(Soukromý úkol)";
vtodo_class2 = "(Skrytý úkol)";
/* Receipts */
"The event \"%{Summary}\" was created" = "Událost \"%{Summary}\" byla vytvořena";
"The event \"%{Summary}\" was deleted" = "Událost \"%{Summary}\" byla smazána";
@@ -14,6 +15,7 @@ vtodo_class2 = "(Skrytý úkol)";
"The following attendees(s) were notified" = "Následující účastníci byli upozorněni";
"The following attendees(s) were added" = "Následující účastníci byli přidáni";
"The following attendees(s) were removed" = "Následující účastníci byli odebráni";
/* IMIP messages */
"calendar_label" = "Kalendář";
"startDate_label" = "Začátek";
@@ -26,17 +28,20 @@ vtodo_class2 = "(Skrytý úkol)";
"comment_label" = "Komentář:";
"organizer_label" = "Organizátor";
"attendee_label" = "Účastník";
/* Invitation */
"Event Invitation: \"%{Summary}\"" = "Pozvání na událost: \"%{Summary}\"";
"(sent by %{SentBy}) " = "(poslal/a %{SentBy}) ";
"%{Organizer} %{SentByText}has invited you to %{Summary}.\n\nStart: %{StartDate}\nEnd: %{EndDate}\nDescription: %{Description}" = "%{Organizer} %{SentByText}Vás pozval/a na %{Summary}.\n\nZačátek: %{StartDate}\nKonec: %{EndDate}\nPopis: %{Description}";
"%{Organizer} %{SentByText}has invited you to %{Summary}.\n\nStart: %{StartDate} at %{StartTime}\nEnd: %{EndDate} at %{EndTime}\nDescription: %{Description}" = "%{Organizer} %{SentByText}Vás pozval/a na %{Summary}.\n\nZačátek: %{StartDate} v %{StartTime}\nKonec: %{EndDate} v %{EndTime}\nPopis: %{Description}";
/* Deletion */
"Event Cancelled: \"%{Summary}\"" = "Zrušení události: \"%{Summary}\"";
"%{Organizer} %{SentByText}has cancelled this event: %{Summary}.\n\nStart: %{StartDate}\nEnd: %{EndDate}\nDescription: %{Description}"
= "%{Organizer} %{SentByText}zrušil/a událost: %{Summary}.\n\nZačátek: %{StartDate}\nKonec: %{EndDate}\nPopis: %{Description}";
"%{Organizer} %{SentByText}has cancelled this event: %{Summary}.\n\nStart: %{StartDate} at %{StartTime}\nEnd: %{EndDate} at %{EndTime}\nDescription: %{Description}"
= "%{Organizer} %{SentByText}zrušil/a událost: %{Summary}.\n\nZačátek: %{StartDate} v %{StartTime}\nKonec: %{EndDate} v %{EndTime}\nPopis: %{Description}";
/* Update */
"The appointment \"%{Summary}\" for the %{OldStartDate} has changed"
= "Schůzka \"%{Summary}\" na %{OldStartDate} byla změněna";
@@ -46,6 +51,7 @@ vtodo_class2 = "(Skrytý úkol)";
= "Následující parametry byly ve schůzce \"%{Summary}\" změněny:";
"Please accept or decline those changes."
= "Prosím přijměte nebo odmítněte tyto změny.";
/* Reply */
"Accepted invitation: \"%{Summary}\"" = "Přijaté pozvání: \"%{Summary}\"";
"Declined invitation: \"%{Summary}\"" = "Odmítnuté pozvání: \"%{Summary}\"";
@@ -59,6 +65,15 @@ vtodo_class2 = "(Skrytý úkol)";
= "%{Attendee} %{SentByText}delegoval/a Vaše pozvání na %{Delegate}.";
"%{Attendee} %{SentByText}has not yet decided upon your event invitation."
= "%{Attendee} %{SentByText}dosud o Vašem pozvání k události nerozhodl/a.";
/* Reminder */
"Reminder: \"%{Summary}\" - %{StartDate}" = "Připomenutí: \"%{Summary}\" - %{StartDate}";
/* Resources */
"Cannot access resource: \"%{Cn} %{SystemEmail}\"" = "Nedostupný zdroj: \"%{Cn} %{SystemEmail}\"";
"Maximum number of simultaneous bookings (%{NumberOfSimultaneousBookings}) reached for resource \"%{Cn} %{SystemEmail}\". The conflicting event is \"%{EventTitle}\", and starts on %{StartDate}." = "Byl dosažen maximální počet současných rezervací\n(%{NumberOfSimultaneousBookings}) pro zdroj \"%{Cn} %{SystemEmail}\". Konfliktní událost je \"%{EventTitle}\" a začíná %{StartDate}.";
/* Participation role */
"Your participation is required to this event" = "Vaše účast je vyžadována";
"Your participation is optional to this event" = "Vaše účast je dobrovolná";
"Your participation is not required to this event" = "Vaše účast není vyžadována";
@@ -40,7 +40,7 @@
if ((self = [super init]))
{
address = nil;
timeZone = [NSTimeZone timeZoneWithAbbreviation: @"GMT"];
timeZone = [NSTimeZone timeZoneForSecondsFromGMT: 0];
[timeZone retain];
startDate = nil;
endDate = nil;
@@ -7,6 +7,7 @@ vevent_class2 = "(Eveniment confidenţial)";
vtodo_class0 = "(Sarcină publică)";
vtodo_class1 = "(Sarcină privată)";
vtodo_class2 = "(Sarcină confidenţială)";
/* Receipts */
"The event \"%{Summary}\" was created" = "Evenimentul \"%{Summary}\" a fost creat";
"The event \"%{Summary}\" was deleted" = "Evenimentul \"%{Summary}\" a fost șters";
@@ -14,6 +15,7 @@ vtodo_class2 = "(Sarcină confidenţială)";
"The following attendees(s) were notified" = "Următori participan(t/ți) au fost notificați";
"The following attendees(s) were added" = "Următori participan(t/ți) au fost adăugați";
"The following attendees(s) were removed" = "Următori participan(t/ți) au fost eliminați";
/* IMIP messages */
"calendar_label" = "Calendar";
"startDate_label" = "Start";
@@ -26,17 +28,20 @@ vtodo_class2 = "(Sarcină confidenţială)";
"comment_label" = "Comentariu";
"organizer_label" = "Organizator";
"attendee_label" = "Participant";
/* Invitation */
"Event Invitation: \"%{Summary}\"" = "Invitație la eveniment: \"%{Summary}\"";
"(sent by %{SentBy}) " = "(trimis de %{SentBy}) ";
"%{Organizer} %{SentByText}has invited you to %{Summary}.\n\nStart: %{StartDate}\nEnd: %{EndDate}\nDescription: %{Description}" = "%{Organizer} %{SentByText}va invitat la %{Summary}.\n\nÎnceput: %{StartDate}\nSfârșit: %{EndDate}\nDescriere: %{Description}";
"%{Organizer} %{SentByText}has invited you to %{Summary}.\n\nStart: %{StartDate} at %{StartTime}\nEnd: %{EndDate} at %{EndTime}\nDescription: %{Description}" = "%{Organizer} %{SentByText}va invitat la %{Summary}.\n\nÎnceput: %{StartDate} la %{StartTime}\nSfârșit: %{EndDate} la %{EndTime}\nDescriere: %{Description}";
/* Deletion */
"Event Cancelled: \"%{Summary}\"" = "Eveniment Anulat: \"%{Summary}\"";
"%{Organizer} %{SentByText}has cancelled this event: %{Summary}.\n\nStart: %{StartDate}\nEnd: %{EndDate}\nDescription: %{Description}"
= "%{Organizer} %{SentByText}a anulat acest eveniment: %{Summary}.\n\nÎnceput: %{StartDate}\nSfârșit: %{EndDate}\nDescriere: %{Description}";
"%{Organizer} %{SentByText}has cancelled this event: %{Summary}.\n\nStart: %{StartDate} at %{StartTime}\nEnd: %{EndDate} at %{EndTime}\nDescription: %{Description}"
= "%{Organizer} %{SentByText}a anulat acest eveniment: %{Summary}.\n\nÎnceput: %{StartDate} la %{StartTime}\nSfârșit: %{EndDate} la %{EndTime}\nDescriere: %{Description}";
/* Update */
"The appointment \"%{Summary}\" for the %{OldStartDate} has changed"
= "întâlnirea \"%{Summary}\" pentru %{OldStartDate} s-a schimbat";
@@ -46,6 +51,7 @@ vtodo_class2 = "(Sarcină confidenţială)";
= "Următorii parametri s-au schimbat în întâlnirea \"%{Summary}\":";
"Please accept or decline those changes."
= "Vă rugăm să acceptați sau să refuzați aceste modificări.";
/* Reply */
"Accepted invitation: \"%{Summary}\"" = "Invitație acceptată: \"%{Summary}\"";
"Declined invitation: \"%{Summary}\"" = "Invitație refuzată: \"%{Summary}\"";
@@ -59,6 +65,15 @@ vtodo_class2 = "(Sarcină confidenţială)";
= "%{Attendee} %{SentByText}a delegat invitația catre %{Delegate}.";
"%{Attendee} %{SentByText}has not yet decided upon your event invitation."
= "%{Attendee} %{SentByText}nu a decis încă cu privire la invitația dvs.";
/* Reminder */
"Reminder: \"%{Summary}\" - %{StartDate}" = " Memento: \"%{Summary}\" - %{StartDate}";
/* Resources */
"Cannot access resource: \"%{Cn} %{SystemEmail}\"" = "Nu pot accesa resursa: \"%{Cn} %{SystemEmail}\"";
"Maximum number of simultaneous bookings (%{NumberOfSimultaneousBookings}) reached for resource \"%{Cn} %{SystemEmail}\". The conflicting event is \"%{EventTitle}\", and starts on %{StartDate}." = "Numărul maxim de rezervările simultane(%{NumberOfSimultaneousBookings}) a fost atins pentru resursa \"%{Cn} %{SystemEmail}\". Evenimentul în conflict este \"%{EventTitle}\", și începe pe %{StartDate}.";
/* Participation role */
"Your participation is required to this event" = "Participarea ta este necesară pentru acest eveniment";
"Your participation is optional to this event" = " Participarea ta este opțională pentru acest eveniment";
"Your participation is not required to this event" = "Participarea ta nu este necesară pentru acest eveniment";
+78 -115
View File
@@ -1,5 +1,5 @@
/*
Copyright (C) 2007-2014 Inverse inc.
Copyright (C) 2007-2019 Inverse inc.
Copyright (C) 2004-2005 SKYRIX Software AG
This file is part of SOGo.
@@ -37,6 +37,7 @@
#import <NGCards/iCalTimeZonePeriod.h>
#import <NGCards/iCalToDo.h>
#import <NGCards/NSString+NGCards.h>
#import <NGExtensions/NSCalendarDate+misc.h>
#import <NGExtensions/NGCalendarDateRange.h>
#import <NGExtensions/NSNull+misc.h>
#import <NGExtensions/NSObject+Logs.h>
@@ -59,6 +60,7 @@
#import <SOGo/SOGoWebDAVValue.h>
#import <SOGo/WORequest+SOGo.h>
#import <SOGo/WOResponse+SOGo.h>
#import <SOGo/SOGoSource.h>
#import "iCalCalendar+SOGo.h"
#import "iCalRepeatableEntityObject+SOGo.h"
@@ -70,7 +72,6 @@
#import "SOGoTaskObject.h"
#import "SOGoWebAppointmentFolder.h"
#define defaultColor @"#AAAAAA"
@interface SOGoGCSFolder (SOGoPrivate)
@@ -425,11 +426,10 @@ static Class iCalEventK = nil;
response: (WOResponse *) theResponse
{
NSMutableDictionary *moduleSettings, *folderShowAlarms, *freeBusyExclusions;
NSString *subscriptionPointer;
NSString *subscriptionPointer, *domain;
NSMutableArray *allUsers;
SOGoUserSettings *us;
NSDictionary *dict;
SOGoUser *sogoUser;
BOOL rc;
int i;
@@ -437,34 +437,48 @@ static Class iCalEventK = nil;
if (rc)
{
dict = [[SOGoUserManager sharedUserManager] contactInfosForUserWithUIDorEmail: theIdentifier];
#warning Duplicated code from SOGoGCSFolder subscribeUserOrGroup
domain = [[context activeUser] domain];
dict = [[SOGoUserManager sharedUserManager] contactInfosForUserWithUIDorEmail: theIdentifier
inDomain: domain];
if ([[dict objectForKey: @"isGroup"] boolValue])
{
SOGoGroup *aGroup;
if (dict && [[dict objectForKey: @"isGroup"] boolValue])
{
id <SOGoSource> source;
aGroup = [SOGoGroup groupWithIdentifier: theIdentifier
inDomain: [[context activeUser] domain]];
allUsers = [NSMutableArray arrayWithArray: [aGroup members]];
source = [[SOGoUserManager sharedUserManager] sourceWithID: [dict objectForKey: @"SOGoSource"]];
if ([source conformsToProtocol:@protocol(SOGoMembershipSource)])
{
NSArray *members;
// We remove the active user from the group (if present) in order to
// not subscribe him to their own resource!
[allUsers removeObject: [context activeUser]];
}
else
{
sogoUser = [SOGoUser userWithLogin: theIdentifier roles: nil];
members = [(id<SOGoMembershipSource>)(source) membersForGroupWithUID: [dict objectForKey: @"c_uid"]];
allUsers = [NSMutableArray array];
if (sogoUser)
allUsers = [NSArray arrayWithObject: sogoUser];
else
allUsers = [NSArray array];
}
for (i = 0; i < [members count]; i++)
{
[allUsers addObject: [[members objectAtIndex: i] objectForKey: @"c_uid"]];
}
// We remove the active user from the group (if present) in order to
// not subscribe him to their own resource!
[allUsers removeObject: [[context activeUser] login]];
}
else
{
[self errorWithFormat: @"Inconsistency error - got group identifier (%@) from a source (%@) that does not support groups (%@).", theIdentifier, [dict objectForKey: @"SOGoSource"], NSStringFromClass([source class])];
return NO;
}
}
else
{
if (dict)
allUsers = [NSArray arrayWithObject: [dict objectForKey: @"c_uid"]];
else
allUsers = [NSArray array];
}
for (i = 0; i < [allUsers count]; i++)
{
sogoUser = [allUsers objectAtIndex: i];
us = [sogoUser userSettings];
us = [SOGoUserSettings settingsForUser: [allUsers objectAtIndex: i]];
moduleSettings = [us objectForKey: [container nameInContainer]];
if (!(moduleSettings
&& [moduleSettings isKindOfClass: [NSMutableDictionary class]]))
@@ -1467,8 +1481,7 @@ firstInstanceCalendarDateRange: (NGCalendarDateRange *) fir
#warning we do not take the participation status into account
while ((currentRecord = [ma nextObject]))
{
accessClass
= [[currentRecord objectForKey: @"c_classification"] intValue];
accessClass = [[currentRecord objectForKey: @"c_classification"] intValue];
role = roles[accessClass];
if (!role)
{
@@ -1497,13 +1510,17 @@ firstInstanceCalendarDateRange: (NGCalendarDateRange *) fir
EOQualifier *qualifier;
GCSFolder *folder;
NSMutableArray *fields, *ma;
NSMutableDictionary *currentRecord;
NSArray *records;
NSEnumerator *matchingRecords;
NSMutableArray *baseWhere;
NSString *where, *dateSqlString, *privacySQLString, *currentLogin;
NSCalendarDate *endDate;
NGCalendarDateRange *r;
BOOL rememberRecords, canCycle;
SOGoUser *ownerUser;
ownerUser = [SOGoUser userWithLogin: owner roles: nil];
rememberRecords = [self _checkIfWeCanRememberRecords: _fields];
canCycle = [_component isEqualToString: @"vevent"] || [_component isEqualToString: @"vtodo"];
// if (rememberRecords)
@@ -1661,11 +1678,21 @@ firstInstanceCalendarDateRange: (NGCalendarDateRange *) fir
}
currentLogin = [[context activeUser] login];
if (![currentLogin isEqualToString: owner]
&& !_includeProtectedInformation)
[self _fixupProtectedInformation: [ma objectEnumerator]
inFields: _fields
forUser: currentLogin];
if (![currentLogin isEqualToString: owner])
{
if (!_includeProtectedInformation)
[self _fixupProtectedInformation: [ma objectEnumerator]
inFields: _fields
forUser: currentLogin];
}
// Add owner to each record. It will be used when generating freebusy.
matchingRecords = [ma objectEnumerator];
while ((currentRecord = [matchingRecords nextObject]))
{
[currentRecord setObject: ownerUser forKey: @"owner"];
}
if (rememberRecords)
[self _rememberRecords: ma];
@@ -3392,7 +3419,6 @@ firstInstanceCalendarDateRange: (NGCalendarDateRange *) fir
NSMutableDictionary *timezones, *uids;
NSString *tzId, *uid, *originalUid;
iCalEntityObject *element;
iCalDateTime *startDate;
iCalTimeZone *timezone;
iCalCalendar *masterCalendar;
iCalEvent *event;
@@ -3436,92 +3462,29 @@ firstInstanceCalendarDateRange: (NGCalendarDateRange *) fir
timezone = nil;
element = [components objectAtIndex: i];
// Use the timezone of the start date.
startDate = (iCalDateTime *) [element uniqueChildWithTag: @"dtstart"];
if (startDate)
{
tzId = [startDate value: 0 ofAttribute: @"tzid"];
if ([tzId length])
timezone = [timezones valueForKey: tzId];
else
{
// If the start date is a "floating time", let's use the user's timezone
// during the import for both the start and end dates. This is similar
// to what we do in SOGoAppointmentObject: -_adjustFloatingTimeInRequestCalendar:
NSString *s;
s = [[startDate valuesAtIndex: 0 forKey: @""] objectAtIndex: 0];
if ([element isKindOfClass: iCalEventK] &&
![(iCalEvent *)element isAllDay] &&
![s hasSuffix: @"Z"] &&
![s hasSuffix: @"z"])
{
iCalDateTime *endDate;
int delta;
timezone = [iCalTimeZone timeZoneForName: [[[self->context activeUser] userDefaults] timeZoneName]];
[calendar addTimeZone: timezone];
delta = [[timezone periodForDate: [startDate dateTime]] secondsOffsetFromGMT];
event = (iCalEvent *)element;
[event setStartDate: [[event startDate] dateByAddingYears: 0 months: 0 days: 0 hours: 0 minutes: 0 seconds: -delta]];
[startDate setTimeZone: timezone];
endDate = (iCalDateTime *) [element uniqueChildWithTag: @"dtend"];
if (endDate)
{
[event setEndDate: [[event endDate] dateByAddingYears: 0 months: 0 days: 0 hours: 0 minutes: 0 seconds: -delta]];
[endDate setTimeZone: timezone];
}
}
}
if ([element isKindOfClass: iCalEventK])
if ([element isKindOfClass: iCalEventK])
{
event = (iCalEvent *)element;
timezone = [event adjustInContext: self->context withTimezones: timezones];
if ([event recurrenceId])
{
event = (iCalEvent *)element;
if (![event hasEndDate] && ![event hasDuration])
// Event is an occurrence of a repeating event
if ((uid = [uids valueForKey: [event uid]]))
{
// No end date, no duration
if ([event isAllDay])
[event setDuration: @"P1D"];
else
[event setDuration: @"PT1H"];
[self errorWithFormat: @"Importing event with no end date; setting duration to %@ for UID = %@", [event duration], [event uid]];
}
//
// We check for broken all-day events (like the ones coming from the "WebCalendar" tool) where
// the start date is equal to the end date. This clearly violates the RFC:
//
// 3.8.2.2. Date-Time End
// The value MUST be later in time than the value of the "DTSTART" property.
//
if ([event isAllDay] && [[event startDate] isEqual: [event endDate]])
{
[event setEndDate: [[event startDate] dateByAddingYears: 0 months: 0 days: 1 hours: 0 minutes: 0 seconds: 0]];
[self errorWithFormat: @"Fixed broken all-day event; setting end date to %@ for UID = %@", [event endDate], [event uid]];
}
if ([event recurrenceId])
{
// Event is an occurrence of a repeating event
if ((uid = [uids valueForKey: [event uid]]))
SOGoAppointmentObject *master = [self lookupName: uid
inContext: context
acquire: NO];
if (master)
{
SOGoAppointmentObject *master = [self lookupName: uid
inContext: context
acquire: NO];
if (master)
{
// Associate the occurrence to the master event and skip the actual import process
masterCalendar = [master calendar: NO secure: NO];
[masterCalendar addToEvents: event];
if (timezone)
[masterCalendar addTimeZone: timezone];
[master saveCalendar: masterCalendar];
continue;
}
// Associate the occurrence to the master event and skip the actual import process
masterCalendar = [master calendar: NO secure: NO];
[masterCalendar addToEvents: event];
if (timezone)
[masterCalendar addTimeZone: timezone];
[master saveCalendar: masterCalendar];
continue;
}
}
}
@@ -1,8 +1,6 @@
/* SOGoAppointmentFolderObject.m - this file is part of SOGo
*
* Copyright (C) 2010-2012 Inverse inc.
*
* Author: Wolfgang Sourdeau <wsourdeau@inverse.ca>
* Copyright (C) 2010-2019 Inverse inc.
*
* This file is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -378,7 +378,7 @@ static SoSecurityManager *sm = nil;
methodSel = SOGoSelectorForPropertySetter (currentName);
if ([newFolder respondsToSelector: methodSel])
[newFolder performSelector: methodSel
withObject: value];
withObject: value];
}
}
@@ -1,5 +1,5 @@
/*
Copyright (C) 2007-2018 Inverse inc.
Copyright (C) 2007-2020 Inverse inc.
This file is part of SOGo
@@ -63,7 +63,6 @@
fromRequest: (WORequest *) rq;
- (BOOL) resourceHasAutoAccepted;
@end
#endif /* __Appointments_SOGoAppointmentObject_H__ */
+25 -70
View File
@@ -1,5 +1,5 @@
/*
Copyright (C) 2007-2019 Inverse inc.
Copyright (C) 2007-2020 Inverse inc.
This file is part of SOGo
@@ -47,13 +47,14 @@
#import <SOGo/NSObject+DAV.h>
#import <SOGo/NSString+Utilities.h>
#import <SOGo/SOGoDateFormatter.h>
#import <SOGo/SOGoGroup.h>
#import <SOGo/SOGoUserManager.h>
#import <SOGo/SOGoUser.h>
#import <SOGo/SOGoUserSettings.h>
#import <SOGo/SOGoDomainDefaults.h>
#import <SOGo/WORequest+SOGo.h>
#import "iCalCalendar+SOGo.h"
#import "iCalEvent+SOGo.h"
#import "iCalEventChanges+SOGo.h"
#import "iCalEntityObject+SOGo.h"
#import "iCalPerson+SOGo.h"
@@ -768,13 +769,15 @@
if ([user isResource])
{
// If we always force the auto-accept if numberOfSimultaneousBookings <= 0 (ie., no limit
// We always force the auto-accept if numberOfSimultaneousBookings <= 0 (ie., no limit
// is imposed) or if numberOfSimultaneousBookings is greater than the number of
// overlapping events
// overlapping events.
// When numberOfSimultaneousBookings is set to -1, only force the auto-accept
// once the conflict has been raised and the action is forced by the user.
if ([user numberOfSimultaneousBookings] <= 0 ||
[user numberOfSimultaneousBookings] > [fbInfo count])
{
if (currentAttendee)
if (currentAttendee && ([user numberOfSimultaneousBookings] >= 0 || forceSave))
{
[[currentAttendee attributes] removeObjectForKey: @"RSVP"];
[currentAttendee setParticipationStatus: iCalPersonPartStatAccepted];
@@ -811,7 +814,7 @@
// We are dealing with a normal attendee. Lets check if we have conflicts, unless
// we are being asked to force the save anyway
//
else if (!forceSave)
if (!forceSave && !_resourceHasAutoAccepted)
{
NSMutableDictionary *info;
NSMutableArray *conflicts;
@@ -865,7 +868,7 @@
// set the resource as one!
[[currentAttendee attributes] removeObjectForKey: @"RSVP"];
[currentAttendee setParticipationStatus: iCalPersonPartStatAccepted];
_resourceHasAutoAccepted = YES;
_resourceHasAutoAccepted = YES;
}
}
@@ -1624,7 +1627,7 @@ inRecurrenceExceptionsForEvent: (iCalEvent *) theEvent
iCalPerson *attendee;
NSException *ex;
SOGoUser *ownerUser, *delegatedUser;
NSString *recurrenceTime, *delegatedUid;
NSString *recurrenceTime, *delegatedUid, *domain;
event = nil;
ex = nil;
@@ -1670,10 +1673,15 @@ inRecurrenceExceptionsForEvent: (iCalEvent *) theEvent
if ([event isAttendee: [[delegate email] rfc822Email]])
ex = [NSException exceptionWithHTTPStatus: 409
reason: @"delegate is a participant"];
else if ([SOGoGroup groupWithEmail: [[delegate email] rfc822Email]
inDomain: [ownerUser domain]])
ex = [NSException exceptionWithHTTPStatus: 409
reason: @"delegate is a group"];
else {
NSDictionary *dict;
domain = [[context activeUser] domain];
dict = [[SOGoUserManager sharedUserManager] contactInfosForUserWithUIDorEmail: [[delegate email] rfc822Email]
inDomain: domain];
if (dict && [[dict objectForKey: @"isGroup"] boolValue])
ex = [NSException exceptionWithHTTPStatus: 409
reason: @"delegate is a group"];
}
}
if (ex == nil)
{
@@ -1930,6 +1938,7 @@ inRecurrenceExceptionsForEvent: (iCalEvent *) theEvent
{
NSArray *allEvents;
iCalEvent *event;
iCalTimeZone *tz;
NSUInteger i;
int j;
@@ -1939,15 +1948,9 @@ inRecurrenceExceptionsForEvent: (iCalEvent *) theEvent
{
event = [allEvents objectAtIndex: i];
if (![event hasEndDate] && ![event hasDuration])
{
// No end date, no duration
if ([event isAllDay])
[event setDuration: @"P1D"];
else
[event setDuration: @"PT1H"];
[self warnWithFormat: @"Invalid event: no end date; setting duration to %@", [event duration]];
}
tz = [event adjustInContext: context withTimezones: nil];
if (tz)
[rqCalendar addTimeZone: tz];
if ([event organizer])
{
@@ -1994,53 +1997,6 @@ inRecurrenceExceptionsForEvent: (iCalEvent *) theEvent
}
}
//
// This is similar to what we do in SOGoAppointmentFolder: -importCalendar:
//
- (void) _adjustFloatingTimeInRequestCalendar: (iCalCalendar *) rqCalendar
{
iCalDateTime *startDate, *endDate;
NSString *startDateAsString;
SOGoUserDefaults *ud;
NSArray *allEvents;
iCalTimeZone *tz;
iCalEvent *event;
int i, delta;
allEvents = [rqCalendar events];
for (i = 0; i < [allEvents count]; i++)
{
event = [allEvents objectAtIndex: i];
if ([event isAllDay])
continue;
startDate = (iCalDateTime *)[event uniqueChildWithTag: @"dtstart"];
startDateAsString = [[startDate valuesAtIndex: 0 forKey: @""] objectAtIndex: 0];
if (![startDate timeZone] &&
![startDateAsString hasSuffix: @"Z"] &&
![startDateAsString hasSuffix: @"z"])
{
ud = [[context activeUser] userDefaults];
tz = [iCalTimeZone timeZoneForName: [ud timeZoneName]];
if ([rqCalendar addTimeZone: tz])
{
delta = [[tz periodForDate: [startDate dateTime]] secondsOffsetFromGMT];
[event setStartDate: [[event startDate] dateByAddingYears: 0 months: 0 days: 0 hours: 0 minutes: 0 seconds: -delta]];
[startDate setTimeZone: tz];
endDate = (iCalDateTime *) [event uniqueChildWithTag: @"dtend"];
if (endDate)
{
[event setEndDate: [[event endDate] dateByAddingYears: 0 months: 0 days: 0 hours: 0 minutes: 0 seconds: -delta]];
[endDate setTimeZone: tz];
}
}
}
}
}
- (void) _decomposeGroupsInRequestCalendar: (iCalCalendar *) rqCalendar
{
@@ -2163,7 +2119,6 @@ inRecurrenceExceptionsForEvent: (iCalEvent *) theEvent
[self _adjustEventsInRequestCalendar: calendar];
[self adjustClassificationInRequestCalendar: calendar];
[self _adjustPartStatInRequestCalendar: calendar];
[self _adjustFloatingTimeInRequestCalendar: calendar];
}
//
@@ -2308,7 +2263,7 @@ inRecurrenceExceptionsForEvent: (iCalEvent *) theEvent
if (!oldEvent && !newEvent)
{
// We check if we only have to deal with the MASTER event
if ([newEvents count] == [oldEvents count])
if ([oldEvents count] && [newEvents count] == [oldEvents count])
{
oldEvent = [oldEvents objectAtIndex: 0];
newEvent = [newEvents objectAtIndex: 0];
@@ -1,8 +1,6 @@
/* SOGoAptMailICalReply.h - this file is part of SOGo
*
* Copyright (C) 2007 Inverse inc.
*
* Author: Wolfgang Sourdeau <wsourdeau@inverse.ca>
* Copyright (C) 2007-2020 Inverse inc.
*
* This file is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -1,8 +1,6 @@
/* SOGoAptMailICalReply - this file is part of SOGo
*
* Copyright (C) 2010-2012 Inverse inc.
*
* Author: Wolfgang Sourdeau <wsourdeau@inverse.ca>
* Copyright (C) 2010-2020 Inverse inc.
*
* This file is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -1,5 +1,5 @@
/*
Copyright (C) 2006-2019 Inverse inc.
Copyright (C) 2006-2020 Inverse inc.
Copyright (C) 2000-2005 SKYRIX Software AG
This file is part of SOGo.
+42 -28
View File
@@ -46,7 +46,7 @@
#import <SOGo/NSString+Utilities.h>
#import <SOGo/SOGoBuild.h>
#import <SOGo/SOGoMailer.h>
#import <SOGo/SOGoGroup.h>
#import <SOGo/SOGoSource.h>
#import <SOGo/SOGoPermissions.h>
#import <SOGo/SOGoUser.h>
#import <SOGo/SOGoUserSettings.h>
@@ -235,7 +235,8 @@
tags = [NSArray arrayWithObjects: @"DTSTAMP", @"DTSTART", @"DTEND", @"DUE", @"EXDATE", @"EXRULE", @"RRULE", @"RECURRENCE-ID", nil];
uid = [[component uid] asCryptedPassUsingScheme: @"ssha256"
withSalt: [[settings userSalt] dataUsingEncoding: NSASCIIStringEncoding]
andEncoding: encHex];
andEncoding: encHex
keyPath: nil];
children = [[[[component children] copy] autorelease] objectEnumerator];
@@ -525,7 +526,7 @@
iCalPerson *currentAttendee;
NSEnumerator *enumerator;
NSAutoreleasePool *pool;
SOGoGroup *group;
NSDictionary *dict;
BOOL eventWasModified;
unsigned int i, j;
@@ -549,37 +550,49 @@
pool = [[NSAutoreleasePool alloc] init];
}
group = [SOGoGroup groupWithEmail: [currentAttendee rfc822Email]
inDomain: domain];
if (group)
{
dict = [[SOGoUserManager sharedUserManager] contactInfosForUserWithUIDorEmail: [currentAttendee rfc822Email]
inDomain: domain];
if (dict && [[dict objectForKey: @"isGroup"] boolValue])
{
iCalPerson *person;
NSArray *members;
SOGoUser *user;
NSDictionary *user;
id <SOGoSource> source;
// We did decompose a group...
[allAttendees removeObject: currentAttendee];
members = [group members];
for (i = 0; i < [members count]; i++)
{
user = [members objectAtIndex: i];
eventWasModified = YES;
source = [[SOGoUserManager sharedUserManager] sourceWithID: [dict objectForKey: @"SOGoSource"]];
if ([source conformsToProtocol:@protocol(SOGoMembershipSource)])
{
members = [(id<SOGoMembershipSource>)(source) membersForGroupWithUID: [dict objectForKey: @"c_uid"]];
for (i = 0; i < [members count]; i++)
{
user = [members objectAtIndex: i];
eventWasModified = YES;
// If the organizer is part of the group, we skip it from
// the addition to the attendees' list
if ([user hasEmail: organizerEmail])
continue;
person = [self iCalPersonWithUID: [user login]];
[person setTag: @"ATTENDEE"];
[person setParticipationStatus: [currentAttendee participationStatus]];
[person setRsvp: [currentAttendee rsvp]];
[person setRole: [currentAttendee role]];
if (![allAttendees containsObject: person])
[allAttendees addObject: person];
}
// If the organizer is part of the group, we skip it from
// the addition to the attendees' list
if ([[user objectForKey: @"c_emails"] containsObject: organizerEmail])
continue;
person = [self iCalPersonWithUID: [user objectForKey: @"c_uid"]];
[person setTag: @"ATTENDEE"];
[person setParticipationStatus: [currentAttendee participationStatus]];
[person setRsvp: [currentAttendee rsvp]];
[person setRole: [currentAttendee role]];
if (![allAttendees containsObject: person])
[allAttendees addObject: person];
}
}
else
{
[self errorWithFormat: @"Inconsistency error - got group identifier (%@) from a source (%@) that does not support groups.", [currentAttendee rfc822Email], [dict objectForKey: @"SOGoSource"]];
RELEASE(pool);
return NO;
}
}
j++;
@@ -968,7 +981,8 @@
[headerMap setObject: @"1.0" forKey: @"MIME-Version"];
if (textOnly)
[headerMap setObject: @"text/html" forKey: @"content-type"];
[headerMap setObject: @"text/html; charset=utf-8"
forKey: @"content-type"];
else
[headerMap setObject: @"multipart/mixed" forKey: @"content-type"];
+17 -4
View File
@@ -372,10 +372,23 @@
if ([endDate compare: currentEndDate] == NSOrderedAscending)
currentEndDate = endDate;
[infos addObject: [NSDictionary dictionaryWithObjectsAndKeys:
[NSNumber numberWithBool: YES], @"c_isopaque",
([currentStartDate compare: startDate] == NSOrderedAscending)? startDate : currentStartDate, @"startDate",
currentEndDate, @"endDate", nil]];
if ([currentStartDate compare: startDate] == NSOrderedAscending)
{
if ([startDate compare: currentEndDate] == NSOrderedAscending)
{
[infos addObject: [NSDictionary dictionaryWithObjectsAndKeys:
[NSNumber numberWithBool: YES], @"c_isopaque",
startDate, @"startDate",
currentEndDate, @"endDate", nil]];
}
}
else
{
[infos addObject: [NSDictionary dictionaryWithObjectsAndKeys:
[NSNumber numberWithBool: YES], @"c_isopaque",
currentStartDate, @"startDate",
currentEndDate, @"endDate", nil]];
}
if (currentEndDate != endDate
&& ([currentEndDate dayOfWeek] == 6 || [currentEndDate dayOfWeek] == 0))
+3 -1
View File
@@ -1,6 +1,6 @@
/* iCalEvent+SOGo.h - this file is part of SOGo
*
* Copyright (C) 2007-2015 Inverse inc.
* Copyright (C) 2007-2019 Inverse inc.
*
* This file is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -23,6 +23,7 @@
#import <NGCards/iCalEvent.h>
@class iCalTimeZone;
@class NSMutableDictionary;
@interface iCalEvent (SOGoExtensions)
@@ -30,6 +31,7 @@
- (BOOL) isStillRelevant;
- (NSTimeInterval) occurenceInterval;
- (void) updateRecurrenceRulesUntilDate: (NSCalendarDate *) previousEndDate;
- (iCalTimeZone *) adjustInContext: (WOContext *) context withTimezones: (NSDictionary *) timezones;
@end
+139 -21
View File
@@ -27,9 +27,10 @@
#import <NGCards/iCalCalendar.h>
#import <NGCards/iCalDateTime.h>
#import <NGCards/iCalTimeZone.h>
#import <NGCards/iCalPerson.h>
#import <NGCards/iCalRecurrenceRule.h>
#import <NGCards/iCalTimeZone.h>
#import <NGCards/iCalTimeZonePeriod.h>
#import <NGCards/NSString+NGCards.h>
#import <SoObjects/SOGo/CardElement+SOGo.h>
@@ -68,7 +69,7 @@
//
- (NSMutableDictionary *) quickRecordFromContent: (NSString *) theContent
container: (id) theContainer
nameInContainer: (NSString *) nameInContainer
nameInContainer: (NSString *) nameInContainer
{
NSMutableDictionary *row;
NSCalendarDate *startDate, *endDate;
@@ -125,15 +126,15 @@
boolTmp = ((isAllDay) ? 1 : 0);
[row setObject: [NSNumber numberWithInt: boolTmp]
forKey: @"c_isallday"];
forKey: @"c_isallday"];
boolTmp = ((([self isRecurrent] || [self recurrenceId])) ? 1 : 0);
[row setObject: [NSNumber numberWithInt: boolTmp]
forKey: @"c_iscycle"];
forKey: @"c_iscycle"];
boolTmp = (([self isOpaque]) ? 1 : 0);
[row setObject: [NSNumber numberWithInt: boolTmp]
forKey: @"c_isopaque"];
forKey: @"c_isopaque"];
[row setObject: [NSNumber numberWithInt: [self priorityNumber]]
forKey: @"c_priority"];
forKey: @"c_priority"];
[row setObject: title forKey: @"c_title"];
if ([location isNotNull]) [row setObject: location forKey: @"c_location"];
@@ -151,8 +152,8 @@
startDate = [timeZone computedDateForDate: startDate];
}
[row setObject: [self quickRecordDateAsNumber: startDate
withOffset: 0
forAllDay: isAllDay]
withOffset: 0
forAllDay: isAllDay]
forKey: @"c_startdate"];
}
if ([endDate isNotNull])
@@ -167,8 +168,8 @@
endDate = [timeZone computedDateForDate: endDate];
}
[row setObject: [self quickRecordDateAsNumber: endDate
withOffset: ((isAllDay) ? -1 : 0)
forAllDay: isAllDay]
withOffset: ((isAllDay) ? -1 : 0)
forAllDay: isAllDay]
forKey: @"c_enddate"];
}
@@ -274,7 +275,7 @@
[row setObject: [self comment] forKey: @"c_description"];
else
[row setObject: [NSNull null] forKey: @"c_description"];
return row;
}
@@ -305,11 +306,11 @@
// The until date must match the time of the end date
offset = [[self endDate] timeIntervalSinceDate: previousEndDate];
untilDate = [untilDate dateByAddingYears:0
months:0
days:0
hours:0
minutes:0
seconds:offset];
months:0
days:0
hours:0
minutes:0
seconds:offset];
[rule setUntilDate: untilDate];
}
}
@@ -324,11 +325,11 @@
// The until date must match the time of the end date
offset = [[self endDate] timeIntervalSinceDate: previousEndDate];
untilDate = [untilDate dateByAddingYears:0
months:0
days:0
hours:0
minutes:0
seconds:offset];
months:0
days:0
hours:0
minutes:0
seconds:offset];
[rule setUntilDate: untilDate];
}
}
@@ -444,4 +445,121 @@
}
}
- (iCalTimeZone *) adjustInContext: (WOContext *) context
withTimezones: (NSDictionary *) timezones
{
iCalDateTime *startDate, *endDate;
iCalTimeZone *timezone;
NSCalendarDate *date;
NSString *startDateAsString, *timezoneId;
SOGoUserDefaults *ud;
int delta;
delta = 0;
timezone = nil;
startDate = (iCalDateTime *) [self uniqueChildWithTag: @"dtstart"];
endDate = (iCalDateTime *) [self uniqueChildWithTag: @"dtend"];
if (![startDate dateTime])
{
if ([endDate dateTime])
{
// End date but no start date
delta = 60*60; // 1 hour
[self setStartDate: [[self endDate] dateByAddingYears: 0 months: 0 days: 0 hours: 0 minutes: 0 seconds: -delta]];
}
else
{
// No start date, no end date; start the event at the first "working" hour
date = [[NSCalendarDate calendarDate] beginOfDayForUser: [context activeUser]];
[self setStartDate: date];
}
startDate = (iCalDateTime *) [self uniqueChildWithTag: @"dtstart"];
[self errorWithFormat: @"Event with no start date; setting start date to %@ for UID %@", [startDate dateTime], [self uid]];
}
if ([startDate dateTime])
{
timezoneId = [startDate value: 0 ofAttribute: @"tzid"];
if ([timezoneId length])
{
timezone = [iCalTimeZone timeZoneForName: timezoneId];
// Fallback to tz (VTIMEZONE) found in the ics.
if (!timezone)
timezone = [timezones valueForKey: timezoneId];
}
else
{
startDateAsString = [[startDate valuesAtIndex: 0 forKey: @""] objectAtIndex: 0];
if (!([self isAllDay] ||
[startDateAsString hasSuffix: @"Z"] ||
[startDateAsString hasSuffix: @"z"]))
{
// The start date is a "floating time", let's use the user's timezone
// for both the start and end dates.
ud = [[context activeUser] userDefaults];
timezone = [iCalTimeZone timeZoneForName: [ud timeZoneName]];
delta = [[timezone periodForDate: [startDate dateTime]] secondsOffsetFromGMT];
[self setStartDate: [[self startDate] dateByAddingYears: 0 months: 0 days: 0 hours: 0 minutes: 0 seconds: -delta]];
[startDate setTimeZone: timezone];
[self errorWithFormat: @"Event with no timezone; setting timezone %@ to start date for UID %@", [timezone tzId], [self uid]];
if ([endDate dateTime])
{
[self setEndDate: [[self endDate] dateByAddingYears: 0 months: 0 days: 0 hours: 0 minutes: 0 seconds: -delta]];
[endDate setTimeZone: timezone];
[self errorWithFormat: @"Event with no timezone; setting timezone %@ to end date for UID %@", [timezone tzId], [self uid]];
}
}
}
}
if (![endDate dateTime] && ![self hasDuration])
{
// No end date, no duration
if ([self isAllDay])
[self setDuration: @"P1D"];
else
[self setDuration: @"PT1H"];
[self errorWithFormat: @"Event with no end date; setting duration to %@ for UID %@", [self duration], [self uid]];
}
//
// We check for broken all-day events (like the ones coming from the "WebCalendar" tool) where
// the start date is equal to the end date. This clearly violates the RFC:
//
// 3.8.2.2. Date-Time End
// The value MUST be later in time than the value of the "DTSTART" property.
//
if ([self isAllDay] && [[self startDate] isEqual: [self endDate]])
{
[self setEndDate: [[self startDate] dateByAddingYears: 0 months: 0 days: 1 hours: 0 minutes: 0 seconds: 0]];
[self errorWithFormat: @"Broken all-day event; setting end date to %@ for UID %@", [self endDate], [self uid]];
}
if ([self isRecurrent])
{
NSArray *rules;
iCalRecurrenceRule *rule;
rules = [self recurrenceRules];
if ([rules count] > 0)
{
// Consider first rule only
rule = [rules objectAtIndex: 0];
if ([rule repeatCount] && [rule untilDate])
{
[self errorWithFormat: @"Recurrent event with both repeat count (%i) and until date (%@); dropping until date", [rule repeatCount], [rule untilDate]];
[rule setUntil: nil];
}
}
}
return timezone;
}
@end
@@ -1,2 +1,5 @@
"OtherUsersFolderName" = "Ostatní uživatelé";
"SharedFoldersName" = "Sdílené složky";
"Report: Marked messages as junk" = "Hlášení: Označené zprávy jsou nevyžádané";
"Report: Marked messages as not junk" = "Hlášení: Označené zprávy nejsou nevyžádané";
@@ -1,2 +1,5 @@
"OtherUsersFolderName" = "Alți utilizatori";
"SharedFoldersName" = "Foldere partajate";
"Report: Marked messages as junk" = "Raport: Marcare mesaje selectate ca junk ";
"Report: Marked messages as not junk" = "Raport: Marcare mesaje selectate ca non-junk ";
+4 -3
View File
@@ -995,7 +995,7 @@ static NSString *userAgent = nil;
- (void) fetchMailForForwarding: (SOGoMailObject *) sourceMail
{
NSDictionary *info, *attachment;
NSString *signature, *nl;
NSString *signature, *nl, *space;
SOGoUserDefaults *ud;
[sourceMail fetchCoreInfos];
@@ -1031,8 +1031,9 @@ static NSString *userAgent = nil;
signature = [[self mailAccountFolder] signature];
if ([signature length])
{
nl = (isHTML ? @"<br/>" : @"\n");
[self setText: [NSString stringWithFormat: @"%@%@-- %@%@", nl, nl, nl, signature]];
nl = (isHTML ? @"<br />" : @"\n");
space = (isHTML ? @"&nbsp;" : @" ");
[self setText: [NSString stringWithFormat: @"%@%@--%@%@%@", nl, nl, space, nl, signature]];
}
attachment = [NSDictionary dictionaryWithObjectsAndKeys:
[sourceMail filenameForForward], @"filename",
+8 -5
View File
@@ -1,5 +1,5 @@
/*
Copyright (C) 2009-2017 Inverse inc.
Copyright (C) 2009-2020 Inverse inc.
This file is part of SOGo.
@@ -34,6 +34,7 @@
*/
@class NSArray;
@class NSException;
@class NSMutableDictionary;
@class NSMutableArray;
@class NSString;
@@ -75,16 +76,18 @@ typedef enum {
/* capabilities */
- (BOOL) hasCapability: (NSString *) capability;
- (NSString *) imap4Separator;
- (BOOL) supportsQuotas;
- (BOOL) supportsQResync;
- (id) getInboxQuota;
- (BOOL) updateFilters;
- (BOOL) updateFiltersWithUsername: (NSString *) theUsername
andPassword: (NSString *) thePassword;
- (NSException *) updateFilters;
- (NSException *) updateFiltersAndForceActivation: (BOOL) forceActivation;
- (NSException *) updateFiltersWithUsername: (NSString *) theUsername
andPassword: (NSString *) thePassword
forceActivation: (BOOL) forceActivation;
- (NSArray *) identities;
- (NSDictionary *) defaultIdentity;
- (NSString *) signature;
- (NSString *) encryption;
+45 -20
View File
@@ -1,5 +1,5 @@
/*
Copyright (C) 2007-2016 Inverse inc.
Copyright (C) 2007-2020 Inverse inc.
This file is part of SOGo.
@@ -257,18 +257,6 @@ static NSString *inboxFolderName = @"INBOX";
return [capabilities containsObject: capability];
}
- (NSString *) imap4Separator
{
NGImap4Client *imapClient;
imapClient = [[self imap4Connection] client];
if (![imapClient delimiter])
[imapClient list: @"INBOX" pattern: @""];
return [imapClient delimiter];
}
- (BOOL) supportsQuotas
{
return [self hasCapability: @"quota"];
@@ -312,19 +300,32 @@ static NSString *inboxFolderName = @"INBOX";
return inboxQuota;
}
- (BOOL) updateFilters
- (NSException *) updateFiltersAndForceActivation: (BOOL) forceActivation
{
return [self updateFiltersWithUsername: nil andPassword: nil];
return [self updateFiltersWithUsername: nil
andPassword: nil
forceActivation: forceActivation];
}
- (BOOL) updateFiltersWithUsername: (NSString *) theUsername
- (NSException *) updateFilters
{
return [self updateFiltersWithUsername: nil
andPassword: nil
forceActivation: NO];
}
- (NSException *) updateFiltersWithUsername: (NSString *) theUsername
andPassword: (NSString *) thePassword
forceActivation: (BOOL) forceActivation
{
SOGoSieveManager *manager;
manager = [SOGoSieveManager sieveManagerForUser: [context activeUser]];
return [manager updateFiltersForAccount: self withUsername: theUsername andPassword: thePassword];
return [manager updateFiltersForAccount: self
withUsername: theUsername
andPassword: thePassword
forceActivation: forceActivation];
}
@@ -666,13 +667,37 @@ static NSString *inboxFolderName = @"INBOX";
return identities;
}
- (NSDictionary *) defaultIdentity
{
NSDictionary *defaultIdentity, *currentIdentity;
unsigned int count, max;
defaultIdentity = nil;
[self identities];
max = [identities count];
for (count = 0; count < max; count++)
{
currentIdentity = [identities objectAtIndex: count];
if ([[currentIdentity objectForKey: @"isDefault"] boolValue])
{
defaultIdentity = currentIdentity;
break;
}
}
return defaultIdentity; // can be nil
}
- (NSString *) signature
{
NSDictionary *identity;
NSString *signature;
[self identities];
if ([identities count] > 0)
signature = [[identities objectAtIndex: 0] objectForKey: @"signature"];
identity = [self defaultIdentity];
if (identity)
signature = [identity objectForKey: @"signature"];
else
signature = nil;
+35 -48
View File
@@ -21,7 +21,7 @@
*/
#import <Foundation/NSValue.h>
#import <Foundation/NSTask.h>
#import <Foundation/NSFileHandle.h>
#import <NGObjWeb/NSException+HTTP.h>
#import <NGObjWeb/WOContext+SoObjects.h>
@@ -58,7 +58,6 @@
#import <SOGo/NSObject+DAV.h>
#import <SOGo/SOGoPermissions.h>
#import <SOGo/SOGoSystemDefaults.h>
#import <SOGo/SOGoGroup.h>
#import <SOGo/SOGoUser.h>
#import <SOGo/SOGoUserFolder.h>
#import <SOGo/SOGoUserManager.h>
@@ -66,6 +65,7 @@
#import <SOGo/WORequest+SOGo.h>
#import <SOGo/WOResponse+SOGo.h>
#import <SOGo/SOGoMailer.h>
#import <SOGo/SOGoZipArchiver.h>
#import "EOQualifier+MailDAV.h"
#import "SOGoMailAccount.h"
@@ -524,9 +524,9 @@ _compareFetchResultsByMODSEQ (id entry1, id entry2, void *data)
NSDictionary *msgs;
NSArray *messages;
NSData *content, *zipContent;
NSTask *zipTask;
NSMutableArray *zipTaskArguments;
WOResponse *response;
SOGoZipArchiver *archiver;
NSFileHandle *zipFileHandle;
int i;
if (!archiveName)
@@ -541,21 +541,15 @@ _compareFetchResultsByMODSEQ (id entry1, id entry2, void *data)
return (WOResponse *)error;
}
zipPath = [[SOGoSystemDefaults sharedSystemDefaults] zipPath];
fm = [NSFileManager defaultManager];
if (![fm fileExistsAtPath: zipPath])
{
zipPath = [NSString stringWithFormat: @"%@/%@", spoolPath, archiveName];
archiver = [SOGoZipArchiver archiverAtPath: zipPath];
if (archiver == nil) {
[self errorWithFormat: @"Failed to create zip archive at %@", spoolPath];
error = [NSException exceptionWithHTTPStatus: 500
reason: @"zip not available"];
reason: @"Internal server error"];
return (WOResponse *)error;
}
zipTask = [[NSTask alloc] init];
[zipTask setCurrentDirectoryPath: spoolPath];
[zipTask setLaunchPath: zipPath];
zipTaskArguments = [NSMutableArray arrayWithObjects: nil];
[zipTaskArguments addObject: @"SavedMessages.zip"];
}
msgs = (NSDictionary *)[self fetchUIDs: uids
parts: [NSArray arrayWithObject: @"BODY.PEEK[]"]];
@@ -564,30 +558,26 @@ _compareFetchResultsByMODSEQ (id entry1, id entry2, void *data)
for (i = 0; i < [messages count]; i++)
{
content = [[[messages objectAtIndex: i] objectForKey: @"body[]"] objectForKey: @"data"];
fileName = [NSString stringWithFormat:@"%@/%@.eml", spoolPath, [uids objectAtIndex: i]];;
[content writeToFile: fileName atomically: YES];
[zipTaskArguments addObject:
[NSString stringWithFormat:@"%@.eml", [uids objectAtIndex: i]]];
}
[zipTask setArguments: zipTaskArguments];
[zipTask launch];
[zipTask waitUntilExit];
[zipTask release];
zipContent = [[NSData alloc] initWithContentsOfFile:
[NSString stringWithFormat: @"%@/SavedMessages.zip", spoolPath]];
for (i = 0; i < [zipTaskArguments count]; i++)
{
fileName = [zipTaskArguments objectAtIndex: i];
[fm removeFileAtPath:
[NSString stringWithFormat: @"%@/%@", spoolPath, fileName] handler: nil];
fileName = [NSString stringWithFormat:@"%@.eml", [uids objectAtIndex: i]];
[archiver putFileWithName: fileName andData: content];
}
[archiver close];
response = [context response];
// Check if SOPE has support for serving files directly
if ([response respondsToSelector: @selector(setContentFile:)]) {
zipFileHandle = [NSFileHandle fileHandleForReadingAtPath: zipPath];
[response setContentFile: zipFileHandle];
} else {
zipContent = [[NSData alloc] initWithContentsOfFile:zipPath];
[response setContent:zipContent];
[zipContent release];
}
[fm removeFileAtPath: zipPath handler: nil];
baseName = [archiveName stringByDeletingPathExtension];
extension = [archiveName pathExtension];
if ([extension length] > 0)
@@ -605,9 +595,6 @@ _compareFetchResultsByMODSEQ (id entry1, id entry2, void *data)
[response setHeader: [NSString stringWithFormat: @"attachment; filename=\"%@\"",
qpFileName]
forKey: @"Content-Disposition"];
[response setContent: zipContent];
[zipContent release];
return response;
}
@@ -884,7 +871,6 @@ _compareFetchResultsByMODSEQ (id entry1, id entry2, void *data)
// We reached the limit or the end of the array
if ((i%limit) == 0 || i == [uids count]-1)
{
id <SOGoAuthenticator> authenticator;
NGMimeMessageGenerator *generator;
[messageToSend setBody: body];
@@ -892,13 +878,11 @@ _compareFetchResultsByMODSEQ (id entry1, id entry2, void *data)
generator = [[[NGMimeMessageGenerator alloc] init] autorelease];
data = [generator generateMimeFromPart: messageToSend];
authenticator = [SOGoDAVAuthenticator sharedSOGoDAVAuthenticator];
error = [[SOGoMailer mailerWithDomainDefaults: [[context activeUser] domainDefaults]]
sendMailData: data
toRecipients: [NSArray arrayWithObject: recipient]
sender: [[identities objectAtIndex: 0] objectForKey: @"email"]
withAuthenticator: authenticator
withAuthenticator: [self authenticatorInContext: context]
inContext: context];
if (error)
@@ -1425,15 +1409,18 @@ _compareFetchResultsByMODSEQ (id entry1, id entry2, void *data)
- (NSString *) _sogoACLUIDToIMAPUID: (NSString *) uid
{
SOGoGroup *group;
NSString *domain;
NSDictionary *dict;
SOGoUser *user;
if ([uid isEqualToString: defaultUserID])
return uid;
group = [SOGoGroup groupWithIdentifier: uid
inDomain: [[context activeUser] domain]];
if (group)
domain = [[context activeUser] domain];
dict = [[SOGoUserManager sharedUserManager] contactInfosForUserWithUIDorEmail: uid
inDomain: domain];
if (dict && [[dict objectForKey: @"isGroup"] boolValue])
return [[[[context activeUser] domainDefaults] imapAclGroupIdPrefix]
stringByAppendingString: uid];
+4 -3
View File
@@ -234,14 +234,15 @@
- (NSString *) signature
{
NSString *signature, *mailSignature, *nl;
NSString *signature, *mailSignature, *nl, *space;
signature = [[sourceMail mailAccountFolder] signature];
if ([signature length])
{
nl = (htmlComposition ? @"<br/>" : @"\n");
mailSignature = [NSString stringWithFormat: @"-- %@%@", nl, signature];
nl = (htmlComposition ? @"<br />" : @"\n");
space = (htmlComposition ? @"&nbsp;" : @" ");
mailSignature = [NSString stringWithFormat: @"--%@%@%@", space, nl, signature];
}
else
mailSignature = @"";
+27 -42
View File
@@ -20,9 +20,9 @@
02111-1307, USA.
*/
#import <Foundation/NSTask.h>
#import <Foundation/NSURL.h>
#import <Foundation/NSValue.h>
#import <Foundation/NSFileHandle.h>
#import <NGObjWeb/WOContext+SoObjects.h>
#import <NGObjWeb/WORequest.h>
@@ -49,6 +49,7 @@
#import <SOGo/SOGoUser.h>
#import <SOGo/SOGoUserDefaults.h>
#import <SOGo/NSCalendarDate+SOGo.h>
#import <SOGo/SOGoZipArchiver.h>
#import "NSString+Mail.h"
#import "NSData+Mail.h"
@@ -962,17 +963,18 @@ static BOOL debugSoParts = NO;
- (WOResponse *) archiveAllFilesinArchiveNamed: (NSString *) archiveName
{
#warning duplicated code from [SOGoMailFolder archiveUIDs]
NSArray *attachments;
NSData *body, *zipContent;
NSDictionary *currentAttachment;
NSException *error;
NSFileManager *fm;
NSMutableArray *zipTaskArguments;
NSString *spoolPath, *name, *fileName, *baseName, *extension, *zipPath, *qpFileName;
NSTask *zipTask;
NSString *spoolPath, *name, *baseName, *extension, *zipPath, *qpFileName;
SOGoMailFolder *folder;
WOResponse *response;
unsigned int max, count;
SOGoZipArchiver *archiver;
NSFileHandle *zipFileHandle;;
if (!archiveName)
archiveName = @"attachments.zip";
@@ -988,22 +990,15 @@ static BOOL debugSoParts = NO;
return (WOResponse *)error;
}
// Prepare execution of zip
zipPath = [[SOGoSystemDefaults sharedSystemDefaults] zipPath];
fm = [NSFileManager defaultManager];
if (![fm fileExistsAtPath: zipPath])
{
zipPath = [NSString stringWithFormat: @"%@/%@", spoolPath, archiveName];
archiver = [SOGoZipArchiver archiverAtPath: zipPath];
if (archiver == nil) {
[self errorWithFormat: @"Failed to create zip archive at %@", spoolPath];
error = [NSException exceptionWithHTTPStatus: 500
reason: @"zip not available"];
reason: @"Internal server error"];
return (WOResponse *)error;
}
zipTask = [[NSTask alloc] init];
[zipTask setCurrentDirectoryPath: spoolPath];
[zipTask setLaunchPath: zipPath];
zipTaskArguments = [NSMutableArray arrayWithObjects: nil];
[zipTaskArguments addObject: @"attachments.zip"];
}
// Fetch attachments and write them on disk
attachments = [self fetchFileAttachments];
@@ -1013,32 +1008,25 @@ static BOOL debugSoParts = NO;
currentAttachment = [attachments objectAtIndex: count];
body = [currentAttachment objectForKey: @"body"];
name = [[currentAttachment objectForKey: @"filename"] asSafeFilename];
fileName = [NSString stringWithFormat:@"%@/%@", spoolPath, name];
[body writeToFile: fileName atomically: YES];
[zipTaskArguments addObject: [NSString stringWithFormat: @"./%@", name]];
[archiver putFileWithName: name andData: body];
}
// Zip files
[zipTask setArguments: zipTaskArguments];
[zipTask launch];
[zipTask waitUntilExit];
[zipTask release];
zipContent = [[NSData alloc] initWithContentsOfFile:
[NSString stringWithFormat: @"%@/attachments.zip", spoolPath]];
[archiver close];
// Delete attachments from disk
max = [zipTaskArguments count];
for (count = 0; count < max; count++)
{
fileName = [zipTaskArguments objectAtIndex: count];
[fm removeFileAtPath:
[NSString stringWithFormat: @"%@/%@", spoolPath, fileName] handler: nil];
}
// Prepare response
response = [context response];
// Check if SOPE has support for serving files directly
if ([response respondsToSelector: @selector(setContentFile:)]) {
zipFileHandle = [NSFileHandle fileHandleForReadingAtPath: zipPath];
[response setContentFile: zipFileHandle];
} else {
zipContent = [[NSData alloc] initWithContentsOfFile:zipPath];
[response setContent:zipContent];
[zipContent release];
}
[fm removeFileAtPath: zipPath handler: nil];
baseName = [archiveName stringByDeletingPathExtension];
extension = [archiveName pathExtension];
if ([extension length] > 0)
@@ -1054,9 +1042,6 @@ static BOOL debugSoParts = NO;
[response setHeader: [NSString stringWithFormat: @"attachment; filename=\"%@\"",
qpFileName]
forKey: @"Content-Disposition"];
[response setContent: zipContent];
[zipContent release];
return response;
}
+6 -6
View File
@@ -75,7 +75,6 @@ SOGo_HEADER_FILES = \
SOGoWebDAVAclManager.h \
SOGoWebDAVValue.h \
SOGoMailer.h \
SOGoGroup.h \
SOGoUser.h \
\
CardElement+SOGo.h \
@@ -87,7 +86,8 @@ SOGo_HEADER_FILES = \
WOContext+SOGo.h \
\
SOGoCredentialsFile.h \
SOGoTextTemplateFile.h
SOGoTextTemplateFile.h \
SOGoZipArchiver.h
all::
@touch SOGoBuild.m
@@ -155,7 +155,6 @@ SOGo_OBJC_FILES = \
SOGoWebDAVAclManager.m \
SOGoWebDAVValue.m \
SOGoMailer.m \
SOGoGroup.m \
SOGoUser.m \
\
CardElement+SOGo.m \
@@ -167,9 +166,10 @@ SOGo_OBJC_FILES = \
WOContext+SOGo.m \
\
SOGoCredentialsFile.m \
SOGoTextTemplateFile.m
SOGoTextTemplateFile.m \
SOGoZipArchiver.m
SOGo_C_FILES += lmhash.c
SOGo_C_FILES += lmhash.c aes.c crypt_blowfish.c pkcs5_pbkdf2.c
SOGo_RESOURCE_FILES = \
SOGoDefaults.plist \
@@ -206,7 +206,7 @@ endif
ADDITIONAL_TOOL_LIBS += -Lobj -lSOGo$(LIBRARY_NAME_SUFFIX)
ADDITIONAL_INCLUDE_DIRS += -I../../SOPE/
ADDITIONAL_LIB_DIRS += -L../../SOPE/GDLContentStore/obj/
ADDITIONAL_LDFLAGS += -lmemcached
ADDITIONAL_LDFLAGS += -lmemcached -lzip
-include GNUmakefile.preamble
ifneq ($(FHS_INSTALL_ROOT),)
+4
View File
@@ -43,6 +43,10 @@ ADDITIONAL_CPPFLAGS += $(LASSO_CFLAGS)
SOGo_LIBRARIES_DEPEND_UPON += $(LASSO_LIBS)
endif
ifeq ($(HAS_LIBRARY_oath), yes)
SOGo_LIBRARIES_DEPEND_UPON += $(MFA_LIBS)
endif
ifeq ($(findstring openbsd, $(GNUSTEP_HOST_OS)), openbsd)
SOGo_LIBRARIES_DEPEND_UPON += -lcrypto
else
+3 -1
View File
@@ -30,7 +30,7 @@
@class NSMutableDictionary;
@class NSString;
@interface LDAPSource : NSObject <SOGoDNSource>
@interface LDAPSource : NSObject <SOGoDNSource, SOGoMembershipSource>
{
int _queryLimit;
int _queryTimeout;
@@ -74,6 +74,7 @@
NSArray *_groupObjectClasses;
NSDictionary *_modulesConstraints;
NSArray *_lookupFields;
BOOL _passwordPolicy;
BOOL _updateSambaNTLMPasswords;
@@ -109,6 +110,7 @@ groupObjectClasses: (NSArray *) newGroupObjectClasses
IMAPLoginField: (NSString *) newIMAPLoginField
SieveHostField: (NSString *) newSieveHostField
bindFields: (id) newBindFields
lookupFields: (NSArray *) newLookupFields
kindField: (NSString *) newKindField
andMultipleBookingsField: (NSString *) newMultipleBookingsField;
+200 -34
View File
@@ -34,6 +34,8 @@
#import "NSString+Crypto.h"
#import "SOGoCache.h"
#import "SOGoSystemDefaults.h"
#import "SOGoUser.h"
#import "SOGoUserManager.h"
#import "LDAPSource.h"
@@ -107,6 +109,8 @@ static Class NSStringK;
_passwordPolicy = NO;
_updateSambaNTLMPasswords = NO;
_lookupFields = [NSArray arrayWithObject: @"*"];
[_lookupFields retain];
_kindField = nil;
_multipleBookingsField = nil;
@@ -155,6 +159,7 @@ static Class NSStringK;
[_MSExchangeHostname release];
[_modifiers release];
[_displayName release];
[_lookupFields release];
[super dealloc];
}
@@ -190,6 +195,7 @@ static Class NSStringK;
IMAPLoginField: [udSource objectForKey: @"IMAPLoginFieldName"]
SieveHostField: [udSource objectForKey: @"SieveHostFieldName"]
bindFields: [udSource objectForKey: @"bindFields"]
lookupFields: [udSource objectForKey: @"lookupFields"]
kindField: [udSource objectForKey: @"KindFieldName"]
andMultipleBookingsField: [udSource objectForKey: @"MultipleBookingsFieldName"]];
@@ -225,7 +231,7 @@ static Class NSStringK;
if (!_contactInfoAttribute)
_contactInfoAttribute = [dd ldapContactInfoAttribute];
[_contactInfoAttribute retain];
udQueryLimit = [udSource objectForKey: @"SOGoLDAPQueryLimit"];
if (udQueryLimit)
_queryLimit = [udQueryLimit intValue];
@@ -261,7 +267,7 @@ static Class NSStringK;
if ([udSource objectForKey: @"updateSambaNTLMPasswords"])
_updateSambaNTLMPasswords = [[udSource objectForKey: @"updateSambaNTLMPasswords"] boolValue];
ASSIGN(_MSExchangeHostname, [udSource objectForKey: @"MSExchangeHostname"]);
}
@@ -329,6 +335,7 @@ groupObjectClasses: (NSArray *) newGroupObjectClasses
IMAPLoginField: (NSString *) newIMAPLoginField
SieveHostField: (NSString *) newSieveHostField
bindFields: (id) newBindFields
lookupFields: (NSArray *) newLookupFields
kindField: (NSString *) newKindField
andMultipleBookingsField: (NSString *) newMultipleBookingsField
{
@@ -375,6 +382,8 @@ groupObjectClasses: (NSArray *) newGroupObjectClasses
ASSIGN(_bindFields, [newBindFields componentsSeparatedByString: @","]);
}
}
if (newLookupFields)
ASSIGN(_lookupFields, newLookupFields);
if (newKindField)
ASSIGN(_kindField, [newKindField lowercaseString]);
if (newMultipleBookingsField)
@@ -583,7 +592,7 @@ groupObjectClasses: (NSArray *) newGroupObjectClasses
didBind = [bindConnection bindWithMethod: @"simple"
binddn: userDN
credentials: _pwd];
else
else
didBind = [bindConnection bindWithMethod: @"simple"
binddn: userDN
credentials: _pwd
@@ -617,7 +626,8 @@ groupObjectClasses: (NSArray *) newGroupObjectClasses
- (NSString *) _encryptPassword: (NSString *) thePassword
{
NSString *pass;
pass = [thePassword asCryptedPassUsingScheme: _userPasswordAlgorithm];
pass = [thePassword asCryptedPassUsingScheme: _userPasswordAlgorithm
keyPath: nil];
if (pass == nil)
{
@@ -639,14 +649,14 @@ groupObjectClasses: (NSArray *) newGroupObjectClasses
NSArray *changes;
BOOL didChange;
attr = [[NGLdapAttribute alloc] initWithAttributeName: theAttribute];
[attr addStringValue: theValue];
mod = [NGLdapModification replaceModification: attr];
changes = [NSArray arrayWithObject: mod];
if ([bindConnection bindWithMethod: @"simple"
binddn: theUserDN
credentials: theUserPassword])
@@ -656,7 +666,7 @@ groupObjectClasses: (NSArray *) newGroupObjectClasses
}
else
didChange = NO;
RELEASE(attr);
return didChange;
@@ -669,7 +679,7 @@ groupObjectClasses: (NSArray *) newGroupObjectClasses
oldPassword: (NSString *) oldPassword
newPassword: (NSString *) newPassword
perr: (SOGoPasswordPolicyError *) perr
{
NGLdapConnection *bindConnection;
NSString *userDN;
@@ -718,7 +728,7 @@ groupObjectClasses: (NSArray *) newGroupObjectClasses
// We don't use a password policy - we simply use
// a modify-op to change the password
NSString* encryptedPass;
if ([_userPasswordAlgorithm isEqualToString: @"none"])
{
encryptedPass = newPassword;
@@ -727,7 +737,7 @@ groupObjectClasses: (NSArray *) newGroupObjectClasses
{
encryptedPass = [self _encryptPassword: newPassword];
}
if (encryptedPass != nil)
{
*perr = PolicyNoError;
@@ -747,7 +757,7 @@ groupObjectClasses: (NSArray *) newGroupObjectClasses
userDN: userDN
password: newPassword
connection: bindConnection];
[self _ldapModifyAttribute: @"sambaLMPassword"
withValue: [newPassword asLMHash]
userDN: userDN
@@ -770,7 +780,7 @@ groupObjectClasses: (NSArray *) newGroupObjectClasses
}
}
NS_ENDHANDLER ;
[bindConnection release];
return didChange;
}
@@ -914,11 +924,11 @@ groupObjectClasses: (NSArray *) newGroupObjectClasses
[qs appendFormat: @" AND %@", _filter];
qualifier = [EOQualifier qualifierWithQualifierFormat: qs];
if ([_scope caseInsensitiveCompare: @"BASE"] == NSOrderedSame)
if ([_scope caseInsensitiveCompare: @"BASE"] == NSOrderedSame)
entries = [ldapConnection baseSearchAtBaseDN: _baseDN
qualifier: qualifier
attributes: attributes];
else if ([_scope caseInsensitiveCompare: @"ONE"] == NSOrderedSame)
else if ([_scope caseInsensitiveCompare: @"ONE"] == NSOrderedSame)
entries = [ldapConnection flatSearchAtBaseDN: _baseDN
qualifier: qualifier
attributes: attributes];
@@ -1122,7 +1132,7 @@ groupObjectClasses: (NSArray *) newGroupObjectClasses
ldifRecord = [ldapEntry asDictionary];
[ldifRecord setObject: self forKey: @"source"];
[ldifRecord setObject: [ldapEntry dn] forKey: @"dn"];
// We get our objectClass attribute values. We lowercase
// everything for ease of search after.
o = [ldapEntry objectClasses];
@@ -1250,7 +1260,6 @@ groupObjectClasses: (NSArray *) newGroupObjectClasses
NSEnumerator *entries;
NSMutableArray *contacts;
EOQualifier *qualifier;
NSArray *attributes;
contacts = [NSMutableArray array];
@@ -1258,20 +1267,19 @@ groupObjectClasses: (NSArray *) newGroupObjectClasses
{
ldapConnection = [self _ldapConnection];
qualifier = [self _qualifierForFilter: match onCriteria: criteria];
attributes = [NSArray arrayWithObject: @"*"];
if ([_scope caseInsensitiveCompare: @"BASE"] == NSOrderedSame)
entries = [ldapConnection baseSearchAtBaseDN: _baseDN
qualifier: qualifier
attributes: attributes];
attributes: _lookupFields];
else if ([_scope caseInsensitiveCompare: @"ONE"] == NSOrderedSame)
entries = [ldapConnection flatSearchAtBaseDN: _baseDN
qualifier: qualifier
attributes: attributes];
else /* we do it like before */
attributes: _lookupFields];
else /* we do it like before */
entries = [ldapConnection deepSearchAtBaseDN: _baseDN
qualifier: qualifier
attributes: attributes];
attributes: _lookupFields];
while ((currentEntry = [entries nextObject]))
[contacts addObject:
[self _convertLDAPEntryToContact: currentEntry]];
@@ -1284,23 +1292,21 @@ groupObjectClasses: (NSArray *) newGroupObjectClasses
{
NGLdapConnection *ldapConnection;
NSEnumerator *entries;
NSArray *attributes;
ldapConnection = [self _ldapConnection];
attributes = [NSArray arrayWithObject: @"*"];
if ([_scope caseInsensitiveCompare: @"BASE"] == NSOrderedSame)
entries = [ldapConnection baseSearchAtBaseDN: _baseDN
qualifier: theQualifier
attributes: attributes];
attributes: _lookupFields];
else if ([_scope caseInsensitiveCompare: @"ONE"] == NSOrderedSame)
entries = [ldapConnection flatSearchAtBaseDN: _baseDN
qualifier: theQualifier
attributes: attributes];
attributes: _lookupFields];
else
entries = [ldapConnection deepSearchAtBaseDN: _baseDN
qualifier: theQualifier
attributes: attributes];
attributes: _lookupFields];
return [entries nextObject];
}
@@ -1354,7 +1360,7 @@ groupObjectClasses: (NSArray *) newGroupObjectClasses
NGLdapEntry *entry;
EOQualifier *qualifier;
NSString *login;
login = nil;
qualifier = nil;
@@ -1372,6 +1378,30 @@ groupObjectClasses: (NSArray *) newGroupObjectClasses
return login;
}
- (NSDictionary *) lookupContactEntryByDN: (NSString *) theDN
{
NGLdapConnection *ldapConnection;
NGLdapEntry *ldapEntry;
EOQualifier *qualifier;
NSDictionary *ldifRecord;
ldifRecord = nil;
qualifier = nil;
ldapConnection = [self _ldapConnection];
if (_filter)
qualifier = [EOQualifier qualifierWithQualifierFormat: _filter];
ldapEntry = [ldapConnection entryAtDN: theDN
qualifier: qualifier
attributes: [NSArray arrayWithObject: @"*"]];
if (ldapEntry)
ldifRecord = [self _convertLDAPEntryToContact: ldapEntry];
return ldifRecord;
}
- (NSString *) lookupDNByLogin: (NSString *) theLogin
{
return [[SOGoCache sharedCache] distinguishedNameForLogin: theLogin];
@@ -1383,24 +1413,24 @@ groupObjectClasses: (NSArray *) newGroupObjectClasses
EOQualifier *qualifier;
NGLdapEntry *ldapEntry;
NSString *s;
if ([theValue length] > 0 && [theAttributes count] > 0)
{
if ([theAttributes count] == 1)
{
s = [NSString stringWithFormat: @"(%@='%@')",
[theAttributes lastObject], SafeLDAPCriteria(theValue)];
}
else
{
NSString *fieldFormat;
fieldFormat = [NSString stringWithFormat: @"(%%@='%@')", SafeLDAPCriteria(theValue)];
s = [[theAttributes stringsWithFormat: fieldFormat]
componentsJoinedByString: @" OR "];
}
qualifier = [EOQualifier qualifierWithQualifierFormat: s];
ldapEntry = [self _lookupLDAPEntry: qualifier];
}
@@ -1797,6 +1827,7 @@ _makeLDAPChanges (NGLdapConnection *ldapConnection,
IMAPLoginField: nil
SieveHostField: nil
bindFields: nil
lookupFields: nil
kindField: nil
andMultipleBookingsField: nil];
[ab setListRequiresDot: NO];
@@ -1927,7 +1958,7 @@ _makeLDAPChanges (NGLdapConnection *ldapConnection,
reason: @"user addressbooks"
@" are not supported"
userInfo: nil];
return result;
}
@@ -1990,4 +2021,139 @@ _makeLDAPChanges (NGLdapConnection *ldapConnection,
}
}
#define CHECK_CLASS(o) ({ \
if ([o isKindOfClass: [NSString class]]) \
o = [NSArray arrayWithObject: o]; \
})
- (NSArray *) membersForGroupWithUID: (NSString *) uid
{
NSMutableArray *dns, *uids, *logins;
NSString *dn, *login;
SOGoUserManager *um;
NSDictionary *d;
SOGoUser *user;
NSArray *o;
NSAutoreleasePool *pool;
int i, c;
NGLdapEntry *entry;
NSMutableArray *members = nil;
if ([uid hasPrefix: @"@"])
uid = [uid substringFromIndex: 1];
entry = [self lookupGroupEntryByUID: uid inDomain: nil];
if (entry)
{
members = [NSMutableArray new];
uids = [NSMutableArray array];
dns = [NSMutableArray array];
logins = [NSMutableArray array];
// We check if it's a static group
// Fetch "members" - we get DNs
d = [entry asDictionary];
o = [d objectForKey: @"member"];
CHECK_CLASS(o);
if (o) [dns addObjectsFromArray: o];
// Fetch "uniqueMembers" - we get DNs
o = [d objectForKey: @"uniquemember"];
CHECK_CLASS(o);
if (o) [dns addObjectsFromArray: o];
// Fetch "memberUid" - we get UID (like login names)
o = [d objectForKey: @"memberuid"];
CHECK_CLASS(o);
if (o) [uids addObjectsFromArray: o];
c = [dns count] + [uids count];
// We deal with a static group, let's add the members
if (c)
{
um = [SOGoUserManager sharedUserManager];
// We add members for whom we have their associated DN
for (i = 0; i < [dns count]; i++)
{
pool = [NSAutoreleasePool new];
dn = [dns objectAtIndex: i];
login = [um getLoginForDN: [dn lowercaseString]];
user = [SOGoUser userWithLogin: login roles: nil];
if (user)
{
[logins addObject: login];
[members addObject: [NSDictionary dictionaryWithObject: login
forKey: @"c_uid"]];
}
[pool release];
}
// We add members for whom we have their associated login name
for (i = 0; i < [uids count]; i++)
{
pool = [NSAutoreleasePool new];
login = [uids objectAtIndex: i];
user = [SOGoUser userWithLogin: login roles: nil];
if (user)
{
[logins addObject: login];
[members addObject: [NSDictionary dictionaryWithObject: login
forKey: @"c_uid"]];
}
[pool release];
}
// We are done fetching members, let's cache the members of the group
// (ie., their UIDs) in memcached to speed up -hasMemberWithUID.
[[SOGoCache sharedCache] setValue: [logins componentsJoinedByString: @","]
forKey: [NSString stringWithFormat: @"%@+%@", uid, _domain]];
}
else
{
// We deal with a dynamic group, let's search all users for whom
// memberOf is equal to our group's DN.
// We also need to look for labelelURI?
}
}
return members;
}
//
//
//
- (BOOL) groupWithUIDHasMemberWithUID: (NSString *) uid
memberUid: (NSString *) memberUid
{
BOOL rc;
NSString *key, *value;;
NSArray *a;
rc = NO;
if ([uid hasPrefix: @"@"])
uid = [uid substringFromIndex: 1];
key = [NSString stringWithFormat: @"%@+%@", uid, _domain];
value = [[SOGoCache sharedCache] valueForKey: key];
// If the value isn't in memcached, that probably means -members was never called.
// We call it only once here.
if (!value)
{
[self membersForGroupWithUID: uid];
value = [[SOGoCache sharedCache] valueForKey: key];
}
a = [value componentsSeparatedByString: @","];
rc = [a containsObject: memberUid];
return rc;
}
@end
+21 -19
View File
@@ -1,31 +1,32 @@
/* NSCalendarDate+SOGo.h - this file is part of SOGo
Copyright (C) 2000-2004 SKYRIX Software AG
This file is part of OGo
OGo is free software; you can redistribute it and/or modify it under
the terms of the GNU Lesser General Public License as published by the
Free Software Foundation; either version 2, or (at your option) any
later version.
OGo is distributed in the hope that it will be useful, but WITHOUT ANY
WARRANTY; without even the implied warranty of MERCHANTABILITY or
FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
License for more details.
You should have received a copy of the GNU Lesser General Public
License along with OGo; see the file COPYING. If not, write to the
Free Software Foundation, 59 Temple Place - Suite 330, Boston, MA
02111-1307, USA.
*/
*
* Copyright (C) 2019 Inverse inc.
*
* This file is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 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 NSCALENDARDATE_SCHEDULER_H
#define NSCALENDARDATE_SCHEDULER_H
#import <Foundation/NSCalendarDate.h>
@class NSCalendarDate;
@class NSString;
@class NSTimeZone;
@class SOGoUser;
@interface NSCalendarDate (SOGoExtensions)
@@ -34,6 +35,7 @@
inTimeZone: (NSTimeZone *) timeZone;
- (BOOL) isDateInSameMonth: (NSCalendarDate *) _other;
- (NSCalendarDate *) beginOfDayForUser: (SOGoUser *) user;
- (NSString *) shortDateString;
- (NSString *) rfc822DateString;
+41 -19
View File
@@ -1,29 +1,31 @@
/* NSCalendarDate+SOGo.m - this file is part of SOGo
Copyright (C) 2000-2004 SKYRIX Software AG
This file is part of OGo
OGo is free software; you can redistribute it and/or modify it under
the terms of the GNU Lesser General Public License as published by the
Free Software Foundation; either version 2, or (at your option) any
later version.
OGo is distributed in the hope that it will be useful, but WITHOUT ANY
WARRANTY; without even the implied warranty of MERCHANTABILITY or
FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
License for more details.
You should have received a copy of the GNU Lesser General Public
License along with OGo; see the file COPYING. If not, write to the
Free Software Foundation, 59 Temple Place - Suite 330, Boston, MA
02111-1307, USA.
*/
*
* Copyright (C) 2019 Inverse inc.
*
* This file is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 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/NSTimeZone.h>
#import <Foundation/NSValue.h>
#import <NGExtensions/NSCalendarDate+misc.h>
#import <SOGo/SOGoUser.h>
#import <SOGo/SOGoUserDefaults.h>
#import "NSCalendarDate+SOGo.h"
static NSString *rfc822Days[] = {@"Sun", @"Mon", @"Tue", @"Wed", @"Thu",
@@ -96,6 +98,26 @@ static NSString *rfc822Months[] = {@"", @"Jan", @"Feb", @"Mar", @"Apr",
return str;
}
- (NSCalendarDate *) beginOfDayForUser: (SOGoUser *) user
{
NSCalendarDate *date;
NSTimeZone *timeZone;
SOGoUserDefaults *ud;
ud = [user userDefaults];
timeZone = [ud timeZone];
[self setTimeZone: timeZone];
date = [self beginOfDay];
date = [date addYear: 0
month: 0
day: 0
hour: 0 - [date hourOfDay] + [ud dayStartHour]
minute: 0 - [date minuteOfHour]
second: 0];
return date;
}
- (NSString *) rfc822DateString
{
int timeZoneShift, tzSeconds;
+14 -6
View File
@@ -1,7 +1,7 @@
/* NSData+Crypto.h - this file is part of SOGo
*
* Copyright (C) 2012 Nicolas Höft
* Copyright (C) 2012-2016 Inverse inc.
* Copyright (C) 2012-2020 Inverse inc.
*
* Author: Nicolas Höft
* Inverse inc.
@@ -32,7 +32,12 @@
@interface NSData (SOGoCryptoExtension)
- (NSData *) asCryptedPassUsingScheme: (NSString *) passwordScheme
withSalt: (NSData *) theSalt;
withSalt: (NSData *) theSalt
keyPath: (NSString *) theKeyPath;
- (BOOL) verifyUsingScheme: (NSString *) passwordScheme
withPassword: (NSData *) thePassword
keyPath: (NSString *) theKeyPath;
- (NSData *) asLM;
- (NSData *) asMD4;
@@ -46,19 +51,22 @@
- (NSData *) asSSHA512UsingSalt: (NSData *) theSalt;
- (NSData *) asSHA256CryptUsingSalt: (NSData *) theSalt;
- (NSData *) asSHA512CryptUsingSalt: (NSData *) theSalt;
- (NSData *) asSymAES128CBCUsingIV: (NSString *) theIV
keyPath: (NSString *) theKeyPath;
- (NSData *) asCramMD5;
- (NSData *) asPBKDF2SHA1UsingSalt: (NSData *) theSalt;
- (NSData *) asCryptUsingSalt: (NSData *) theSalt;
- (NSData *) asMD5CryptUsingSalt: (NSData *) theSalt;
- (NSData *) asBlowfishCryptUsingSalt: (NSData *) theSalt;
- (NSData *) extractSalt: (NSString *) theScheme;
+ (NSData *) generateSaltForLength: (unsigned int) theLength
withBase64: (BOOL) doBase64;
withPrintable: (BOOL) doPrintable;
+ (NSData *) generateSaltForLength: (unsigned int) theLength;
+ (NSString *) encodeDataAsHexString: (NSData* ) theData;
+ (NSData *) decodeDataFromHexString: (NSString* ) theString;
+ (NSString *) encodeDataAsHexString: (NSData *) theData;
+ (NSData *) decodeDataFromHexString: (NSString *) theString;
@end
+281 -39
View File
@@ -1,7 +1,7 @@
/* NSData+Crypto.m - this file is part of SOGo
*
* Copyright (C) 2012 Nicolas Höft
* Copyright (C) 2012-2016 Inverse inc.
* Copyright (C) 2012, 2020 Nicolas Höft
* Copyright (C) 2012-2020 Inverse inc.
* Copyright (C) 2012 Jeroen Dekkers
*
* Author: Nicolas Höft
@@ -49,7 +49,10 @@
#error this module requires either gnutls or openssl
#endif
#include "aes.h"
#include "crypt_blowfish.h"
#include "lmhash.h"
#include "pkcs5_pbkdf2.h"
#import <Foundation/NSArray.h>
#import <NGExtensions/NGBase64Coding.h>
@@ -61,6 +64,15 @@ static BOOL check_gnutls_init(void);
static void _nettle_md5_compress(uint32_t *digest, const uint8_t *input);
#endif
#define BLF_CRYPT_DEFAULT_COMPLEXITY (5)
#define BLF_CRYPT_SALT_LEN (16)
#define BLF_CRYPT_BUFFER_LEN (128)
#define BLF_CRYPT_PREFIX_LEN (7+22+1) /* $2.$nn$ + salt */
#define BLF_CRYPT_PREFIX "$2y"
static const char salt_chars[] =
"./0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
@implementation NSData (SOGoCryptoExtension)
@@ -133,7 +145,7 @@ static void _nettle_md5_compress(uint32_t *digest, const uint8_t *input);
*/
+ (NSData *) generateSaltForLength: (unsigned int) theLength
{
return [NSData generateSaltForLength: theLength withBase64: NO];
return [NSData generateSaltForLength: theLength withPrintable: NO];
}
/**
@@ -141,15 +153,16 @@ static void _nettle_md5_compress(uint32_t *digest, const uint8_t *input);
* with doBase64 == YES then the data will be longer than theLength
*
* @param theLength Length of the binary data to be generated in bytes
* @param doBase64 Convert the data into Base-64 before retuning it, be aware that this makes the binary data longer
* @param doPrintable Use only printable characters
* @return Pseudo-random binary data with length theLength or nil, if an error occured
*/
+ (NSData *) generateSaltForLength: (unsigned int) theLength
withBase64: (BOOL) doBase64
withPrintable: (BOOL) doPrintable
{
char *buf;
int fd;
NSData *data;
unsigned int i;
fd = open("/dev/urandom", O_RDONLY);
@@ -158,12 +171,14 @@ static void _nettle_md5_compress(uint32_t *digest, const uint8_t *input);
buf = (char *)malloc(theLength);
read(fd, buf, theLength);
close(fd);
data = [NSData dataWithBytesNoCopy: buf length: theLength freeWhenDone: YES];
if(doBase64 == YES)
if (doPrintable == YES)
{
return [data dataByEncodingBase64WithLineLength: 1024];
for (i = 0; i < theLength; i++)
{
buf[i] = salt_chars[buf[i] % (sizeof(salt_chars)-1)];
}
}
data = [NSData dataWithBytesNoCopy: buf length: theLength freeWhenDone: YES];
return data;
}
return nil;
@@ -174,10 +189,11 @@ static void _nettle_md5_compress(uint32_t *digest, const uint8_t *input);
*
* @param passwordScheme The scheme to use for hashing/encryption.
* @param theSalt The salt to be used. If none is given but needed, it will be generated
* @return Binary data from the encryption by the specified scheme. On error the funciton returns nil.
* @return Binary data from the encryption by the specified scheme. On error the function returns nil.
*/
- (NSData *) asCryptedPassUsingScheme: (NSString *) passwordScheme
withSalt: (NSData *) theSalt
keyPath: (NSString *) theKeyPath
{
if ([passwordScheme caseInsensitiveCompare: @"none"] == NSOrderedSame ||
[passwordScheme caseInsensitiveCompare: @"plain"] == NSOrderedSame ||
@@ -243,18 +259,88 @@ static void _nettle_md5_compress(uint32_t *digest, const uint8_t *input);
{
return [self asSHA512CryptUsingSalt: theSalt];
}
else if ([passwordScheme caseInsensitiveCompare: @"blf-crypt"] == NSOrderedSame)
{
return [self asBlowfishCryptUsingSalt: theSalt];
}
else if ([passwordScheme caseInsensitiveCompare: @"pbkdf2"] == NSOrderedSame)
{
return [self asPBKDF2SHA1UsingSalt: theSalt];
}
else if ([[passwordScheme lowercaseString] hasPrefix: @"sym"])
{
// We first support one sym cipher, AES-128-CBC. If something else is provided
// we return nil for now. Example of what theSalt might contain:
// $AES-128-CBC$cinlbHKnyBApySphVCz6yA==$Z9hjCXfMhz4xbXkW+aMkAw==
// If theSalt is empty, that means we are not validating a password
// but rather changing it. In this case, we generate an IV.
NSString *cipher, *iv;
cipher = nil;
iv = nil;
if ([theSalt length])
{
NSString *s;
NSArray *a;
s = [[NSString alloc] initWithData: theSalt encoding: NSUTF8StringEncoding];
[s autorelease];
a = [s componentsSeparatedByString: @"$"];
cipher = [a objectAtIndex: 1];
iv = [a objectAtIndex: 2];
}
else
{
if ([passwordScheme caseInsensitiveCompare: @"sym-aes-128-cbc"] == NSOrderedSame)
cipher = @"AES-128-CBC";
}
if ([cipher caseInsensitiveCompare: @"AES-128-CBC"] == NSOrderedSame)
return [self asSymAES128CBCUsingIV: iv
keyPath: theKeyPath];
}
// in case the scheme was not detected, return nil
return nil;
}
/**
* Verify the given password data is equivalent with the
* clear text password using the passed encryption scheme
*
* @param passwordScheme The password scheme to use for comparison
* @param thePassword
*/
- (BOOL) verifyUsingScheme: (NSString *) passwordScheme
withPassword: (NSData *) thePassword
keyPath: (NSString *) theKeyPath
{
NSData *passwordCrypted;
NSData *salt;
salt = [self extractSalt: passwordScheme];
if (salt == nil)
return NO;
// encrypt self with the salt an compare the results
passwordCrypted = [thePassword asCryptedPassUsingScheme: passwordScheme
withSalt: salt
keyPath: theKeyPath];
// return always false when there was a problem
if (passwordCrypted == nil)
return NO;
return [self isEqual: passwordCrypted];
}
- (NSData *) asLM
{
NSData *out;
unsigned char buf[14];
unsigned char *o;
unsigned int len;
memset(buf, 0, 14);
len = ([self length] >= 14 ? 14 : [self length]);
[self getBytes: buf length: len];
@@ -282,7 +368,7 @@ static void _nettle_md5_compress(uint32_t *digest, const uint8_t *input);
#if defined(HAVE_GNUTLS)
if (!check_gnutls_init())
return nil;
md4_buffer([self bytes], [self length], md4);
#elif defined(HAVE_OPENSSL)
MD4([self bytes], [self length], md4);
@@ -339,7 +425,7 @@ static void _nettle_md5_compress(uint32_t *digest, const uint8_t *input);
int i;
int len;
NSData *key;
if ([self length] > 64)
{
key = [self asMD5];
@@ -355,7 +441,7 @@ static void _nettle_md5_compress(uint32_t *digest, const uint8_t *input);
// make sure the rest of the bytes is zero
memset(inner + len, 0, 64 - len);
memcpy(outer, inner, 64);
for (i = 0; i < 64; i++)
{
inner[i] ^= 0x36;
@@ -476,6 +562,48 @@ static void _nettle_md5_compress(uint32_t *digest, const uint8_t *input);
return [NSData dataWithBytes: sha length: SHA512_DIGEST_LENGTH];
}
- (NSData *) asSymAES128CBCUsingIV: (NSString *) theIV
keyPath: (NSString *) theKeyPath
{
NSData *iv_d, *key_d, *cipherdata;
NSMutableString *result;
NSString *s;
char ciphertext[256], *iv_s, *key_s, *pass;
unsigned int len;
len = ceil((double)[self length]/16) * 16;
if (theIV)
iv_d = [theIV dataByDecodingBase64];
else
{
iv_d = [NSData generateSaltForLength: len];
theIV = [iv_d stringByEncodingBase64];
}
iv_s = calloc([iv_d length]+1, sizeof(char));
strncpy(iv_s, [iv_d bytes], [iv_d length]);
key_d = [NSData dataWithContentsOfFile: theKeyPath];
key_s = calloc([key_d length]+1, sizeof(char));
strncpy(key_s, [key_d bytes], [key_d length]);
pass = calloc(len, sizeof(char));
strncpy(pass, [self bytes], [self length]);
AES128_CBC_encrypt_buffer((uint8_t*)ciphertext, (uint8_t*)pass, (uint32_t)len, (const uint8_t*)key_s, (const uint8_t*)iv_s);
cipherdata = [NSData dataWithBytes: ciphertext length: 16];
s = [[NSString alloc] initWithData: [cipherdata dataByEncodingBase64WithLineLength: 1024]
encoding: NSASCIIStringEncoding];
result = [NSMutableString string];
[result appendFormat: @"$AES-128-CBC$%@$%@", theIV, s];
RELEASE(s);
return [result dataUsingEncoding: NSUTF8StringEncoding];
}
/**
* Hash the data with SSHA. Uses openssl functions to generate it.
*
@@ -486,11 +614,12 @@ static void _nettle_md5_compress(uint32_t *digest, const uint8_t *input);
*/
- (NSData *) asSSHAUsingSalt: (NSData *) theSalt
{
//
//
NSMutableData *sshaData;
// generate salt, if not available
if ([theSalt length] == 0) theSalt = [NSData generateSaltForLength: 8];
if ([theSalt length] == 0)
theSalt = [NSData generateSaltForLength: 8];
// put the pass and salt together as one data array
sshaData = [NSMutableData dataWithData: self];
@@ -517,7 +646,8 @@ static void _nettle_md5_compress(uint32_t *digest, const uint8_t *input);
NSMutableData *sshaData;
// generate salt, if not available
if ([theSalt length] == 0) theSalt = [NSData generateSaltForLength: 8];
if ([theSalt length] == 0)
theSalt = [NSData generateSaltForLength: 8];
// put the pass and salt together as one data array
sshaData = [NSMutableData dataWithData: self];
@@ -544,7 +674,8 @@ static void _nettle_md5_compress(uint32_t *digest, const uint8_t *input);
NSMutableData *sshaData;
// generate salt, if not available
if ([theSalt length] == 0) theSalt = [NSData generateSaltForLength: 8];
if ([theSalt length] == 0)
theSalt = [NSData generateSaltForLength: 8];
// put the pass and salt together as one data array
sshaData = [NSMutableData dataWithData: self];
@@ -571,7 +702,8 @@ static void _nettle_md5_compress(uint32_t *digest, const uint8_t *input);
NSMutableData *smdData;
// generate salt, if not available
if ([theSalt length] == 0) theSalt = [NSData generateSaltForLength: 8];
if ([theSalt length] == 0)
theSalt = [NSData generateSaltForLength: 8];
// put the pass and salt together as one data array
smdData = [NSMutableData dataWithData: self];
@@ -597,9 +729,10 @@ static void _nettle_md5_compress(uint32_t *digest, const uint8_t *input);
if ([theSalt length] == 0)
{
// make sure these characters are all printable by using base64
theSalt = [NSData generateSaltForLength: 8 withBase64: YES];
// make sure these characters are all printable
theSalt = [NSData generateSaltForLength: 8 withPrintable: YES];
}
cryptString = [[NSString alloc] initWithData: self encoding: NSUTF8StringEncoding];
saltData = [NSMutableData dataWithData: [[NSString stringWithFormat:@"$%@$", magic] dataUsingEncoding: NSUTF8StringEncoding]];
@@ -668,7 +801,8 @@ static void _nettle_md5_compress(uint32_t *digest, const uint8_t *input);
// crypt() works with strings, so convert NSData to strings
cryptString = [[NSString alloc] initWithData: self encoding: NSUTF8StringEncoding];
if ([theSalt length] == 0) theSalt = [NSData generateSaltForLength: 8 withBase64: YES];
if ([theSalt length] == 0)
theSalt = [NSData generateSaltForLength: 8 withPrintable: YES];
saltString = [[NSString alloc] initWithData: theSalt encoding: NSUTF8StringEncoding];
@@ -682,6 +816,106 @@ static void _nettle_md5_compress(uint32_t *digest, const uint8_t *input);
return [NSData dataWithBytes: buf length: strlen(buf)];
}
/**
* Hash the data using blowfish-crypt
* @param theSalt The salt to be used must not be nil, if empty, one will be generated
*/
- (NSData *) asBlowfishCryptUsingSalt: (NSData *) theSalt
{
NSString *cleartext;
char hashed_password[BLF_CRYPT_BUFFER_LEN];
char magic_salt[BLF_CRYPT_PREFIX_LEN]; // contains $2.$nn$ + salt
if ([theSalt length] == 0)
{
// generate a salt with default complexity if none was provided
NSData* salt = [NSData generateSaltForLength: BLF_CRYPT_SALT_LEN];
if (_crypt_gensalt_blowfish_rn(BLF_CRYPT_PREFIX, BLF_CRYPT_DEFAULT_COMPLEXITY,
[salt bytes], BLF_CRYPT_SALT_LEN,
magic_salt, BLF_CRYPT_PREFIX_LEN) == NULL)
return nil;
}
else
{
const char* salt = [theSalt bytes];
if ([theSalt length] < BLF_CRYPT_PREFIX_LEN ||
salt[0] != '$' || salt[1] != '2' ||
salt[2] < 'a' || salt[2] > 'z' ||
salt[3] != '$')
{
return nil;
}
memcpy(magic_salt, salt, BLF_CRYPT_PREFIX_LEN);
}
cleartext = [[NSString alloc] initWithData: self encoding: NSUTF8StringEncoding];
const char* password = [cleartext UTF8String];
char* bf_res = _crypt_blowfish_rn(password, magic_salt,
hashed_password, BLF_CRYPT_BUFFER_LEN);
[cleartext autorelease];
if (bf_res == NULL)
return nil;
return [NSData dataWithBytes: hashed_password length: strlen(hashed_password)];
}
- (NSData *) asPBKDF2SHA1UsingSalt: (NSData *) theSalt
{
NSString *saltString;
unsigned char hashed_password[PBKDF2_KEY_SIZE_SHA1] = {0};
int rounds = 0;
if ([theSalt length] == 0)
{
// generate a salt with default complexity if none was provided
NSData* saltData = [NSData generateSaltForLength: PBKDF2_SALT_LEN withPrintable: YES];
saltString = [[NSString alloc] initWithData: saltData encoding: NSUTF8StringEncoding];
[saltString autorelease];
}
else
{
NSString *saltAndRounds;
NSArray *saltAndRoundsComponents;
saltAndRounds = [[NSString alloc] initWithData: theSalt encoding: NSUTF8StringEncoding];
// salt is expected to be of the form salt$rounds
saltAndRoundsComponents = [saltAndRounds componentsSeparatedByString: @"$"];
AUTORELEASE(saltAndRounds);
if ([saltAndRoundsComponents count] != 2)
{
return nil;
}
saltString = [saltAndRoundsComponents objectAtIndex: 0];
rounds = [[saltAndRoundsComponents objectAtIndex: 1] intValue];
}
if (rounds == 0)
rounds = PBKDF2_DEFAULT_ROUNDS;
const char* password = [self bytes];
const unsigned char* salt = (const unsigned char*)[saltString UTF8String];
#if defined(HAVE_GNUTLS)
if (!check_gnutls_init())
return nil;
#endif
if (pkcs5_pbkdf2(password, [self length], salt, PBKDF2_SALT_LEN,
hashed_password, PBKDF2_KEY_SIZE_SHA1,
rounds) != 0)
{
return nil;
}
NSData *passwordData =
[NSData dataWithBytesNoCopy: hashed_password length: PBKDF2_KEY_SIZE_SHA1 freeWhenDone: NO];
NSString *hexHash = [NSData encodeDataAsHexString: passwordData];
NSString* result = [NSString stringWithFormat: @"$1$%@$%u$%@", saltString, rounds, hexHash];
return [result dataUsingEncoding:NSUTF8StringEncoding];
}
/**
* Get the salt from a password encrypted with a specied scheme
*
@@ -698,19 +932,22 @@ static void _nettle_md5_compress(uint32_t *digest, const uint8_t *input);
// for the ssha schemes the salt is appended at the endif
// so the range with the salt are bytes after each digest length
if ([theScheme caseInsensitiveCompare: @"crypt"] == NSOrderedSame)
if ([theScheme caseInsensitiveCompare: @"crypt"] == NSOrderedSame ||
[theScheme caseInsensitiveCompare: @"blf-crypt"] == NSOrderedSame)
{
// for crypt schemes simply use the whole string
// for (blf-)crypt schemes simply use the whole string
// the crypt() function is able to extract it by itself
r = NSMakeRange(0, len);
}
else if ([theScheme caseInsensitiveCompare: @"md5-crypt"] == NSOrderedSame ||
[theScheme caseInsensitiveCompare: @"sha256-crypt"] == NSOrderedSame ||
[theScheme caseInsensitiveCompare: @"sha512-crypt"] == NSOrderedSame)
[theScheme caseInsensitiveCompare: @"sha256-crypt"] == NSOrderedSame ||
[theScheme caseInsensitiveCompare: @"sha512-crypt"] == NSOrderedSame ||
[theScheme caseInsensitiveCompare: @"pbkdf2"] == NSOrderedSame)
{
// md5-crypt is generated the following "$1$<salt>$<encrypted pass>"
// sha256-crypt is generated the following "$5$<salt>$<encrypted pass>"
// sha512-crypt is generated the following "$6$<salt>$<encrypted pass>"
// pbkdf2 is generated as "$1$<salt>$<rounds>$<encrypted pass>"
NSString *cryptString;
NSArray *cryptParts;
@@ -725,21 +962,21 @@ static void _nettle_md5_compress(uint32_t *digest, const uint8_t *input);
}
// second is the identifier of md5-crypt/sha256-crypt or sha512-crypt
else if ([[cryptParts objectAtIndex: 1] caseInsensitiveCompare: @"1"] == NSOrderedSame ||
[[cryptParts objectAtIndex: 1] caseInsensitiveCompare: @"5"] == NSOrderedSame ||
[[cryptParts objectAtIndex: 1] caseInsensitiveCompare: @"6"] == NSOrderedSame)
[[cryptParts objectAtIndex: 1] caseInsensitiveCompare: @"5"] == NSOrderedSame ||
[[cryptParts objectAtIndex: 1] caseInsensitiveCompare: @"6"] == NSOrderedSame)
{
// third is the salt; convert it to NSData
if ([cryptParts count] == 4)
return [[cryptParts objectAtIndex: 2] dataUsingEncoding: NSUTF8StringEncoding];
else
{
NSString *saltWithRounds;
// third is the salt; convert it to NSData
if ([cryptParts count] == 4)
return [[cryptParts objectAtIndex: 2] dataUsingEncoding: NSUTF8StringEncoding];
else
{
NSString *saltWithRounds;
saltWithRounds = [NSString stringWithFormat: @"%@$%@", [cryptParts objectAtIndex: 2], [cryptParts objectAtIndex: 3]];
saltWithRounds = [NSString stringWithFormat: @"%@$%@", [cryptParts objectAtIndex: 2], [cryptParts objectAtIndex: 3]];
return [saltWithRounds dataUsingEncoding: NSUTF8StringEncoding];
}
}
return [saltWithRounds dataUsingEncoding: NSUTF8StringEncoding];
}
}
// nothing good
return [NSData data];
}
@@ -759,6 +996,11 @@ static void _nettle_md5_compress(uint32_t *digest, const uint8_t *input);
{
r = NSMakeRange(MD5_DIGEST_LENGTH, len - MD5_DIGEST_LENGTH);
}
else if ([[theScheme lowercaseString] hasPrefix: @"sym"])
{
// For sym we return everything
r = NSMakeRange(0, len);
}
else
{
// return empty string on unknown scheme
+7 -4
View File
@@ -1,7 +1,7 @@
/* NSString+Crypto.h - this file is part of SOGo
*
* Copyright (C) 2012 Nicolas Höft
* Copyright (C) 2012-2016 Inverse inc.
* Copyright (C) 2012-2020 Inverse inc.
*
* Author: Nicolas Höft
* Inverse inc.
@@ -39,15 +39,18 @@ typedef enum {
@interface NSString (SOGoCryptoExtension)
- (BOOL) isEqualToCrypted: (NSString *) cryptedPassword
withDefaultScheme: (NSString *) theScheme;
withDefaultScheme: (NSString *) theScheme
keyPath: (NSString *) theKeyPath;
- (NSString *) asCryptedPassUsingScheme: (NSString *) passwordScheme
withSalt: (NSData *) theSalt
andEncoding: (keyEncoding) encoding;
andEncoding: (keyEncoding) encoding
keyPath: (NSString *) theKeyPath;
// this method uses the default encoding (base64, plain, hex)
// and generates a salt when necessary
- (NSString *) asCryptedPassUsingScheme: (NSString *) passwordScheme;
- (NSString *) asCryptedPassUsingScheme: (NSString *) passwordScheme
keyPath: (NSString *) theKeyPath;
- (NSArray *) splitPasswordWithDefaultScheme: (NSString *) defaultScheme;
+24 -31
View File
@@ -1,7 +1,7 @@
/* NSString+Crypto.m - this file is part of SOGo
*
* Copyright (C) 2012 Nicolas Höft
* Copyright (C) 2012-2015 Inverse inc.
* Copyright (C) 2012-2019 Inverse inc.
*
* Author: Nicolas Höft
* Inverse inc.
@@ -41,17 +41,17 @@
{
NSRange r;
int len;
len = [self length];
if (len == 0)
return @"";
if ([self characterAtIndex:0] != '{')
return @"";
r = [self rangeOfString:@"}" options:(NSLiteralSearch)];
if (r.length == 0)
return @"";
r.length = (r.location - 1);
r.location = 1;
return [[self substringWithRange:r] lowercaseString];
@@ -73,7 +73,7 @@
NSString *scheme;
NSString *pass;
NSArray *encodingAndScheme;
NSRange range;
int selflen, len;
@@ -91,7 +91,7 @@
encodingAndScheme = [NSString getDefaultEncodingForScheme: scheme];
pass = [self substringWithRange: range];
// Returns an array with [scheme, password, encoding]
return [NSArray arrayWithObjects: [encodingAndScheme objectAtIndex: 1], pass, [encodingAndScheme objectAtIndex: 0], nil];
}
@@ -106,14 +106,13 @@
*/
- (BOOL) isEqualToCrypted: (NSString *) cryptedPassword
withDefaultScheme: (NSString *) theScheme
keyPath: (NSString *) theKeyPath
{
NSArray *passInfo;
NSString *selfCrypted;
NSString *pass;
NSString *scheme;
NSData *salt;
NSData *decodedData;
NSNumber *encodingNumber;
NSData *passwordData;
keyEncoding encoding;
// split scheme and pass
@@ -121,8 +120,7 @@
scheme = [passInfo objectAtIndex: 0];
pass = [passInfo objectAtIndex: 1];
encodingNumber = [passInfo objectAtIndex: 2];
encoding = [encodingNumber intValue];
encoding = [[passInfo objectAtIndex: 2] intValue];
if (encoding == encHex)
{
@@ -153,35 +151,26 @@
decodedData = [pass dataUsingEncoding: NSUTF8StringEncoding];
}
salt = [decodedData extractSalt: scheme];
// encrypt self with the salt an compare the results
selfCrypted = [self asCryptedPassUsingScheme: scheme
withSalt: salt
andEncoding: encoding];
// return always false when there was a problem
if (selfCrypted == nil)
return NO;
if ([selfCrypted isEqualToString: pass] == YES)
return YES;
return NO;
passwordData = [self dataUsingEncoding: NSUTF8StringEncoding];
return [decodedData verifyUsingScheme: scheme
withPassword: passwordData
keyPath: theKeyPath];
}
/**
* Calls asCryptedPassUsingScheme:withSalt:andEncoding: with an empty salt and uses
* the default encoding.
*
* @param passwordScheme
* @param passwordScheme: The password scheme to hash the cleartext password.
* @return If successful, the encrypted and encoded NSString of the format {scheme}pass, or nil if the scheme did not exists or an error occured
*/
- (NSString *) asCryptedPassUsingScheme: (NSString *) passwordScheme
keyPath: (NSString *) theKeyPath
{
return [self asCryptedPassUsingScheme: passwordScheme
withSalt: [NSData data]
andEncoding: encDefault];
andEncoding: encDefault
keyPath: theKeyPath];
}
/**
@@ -198,6 +187,7 @@
- (NSString *) asCryptedPassUsingScheme: (NSString *) passwordScheme
withSalt: (NSData *) theSalt
andEncoding: (keyEncoding) userEncoding
keyPath: (NSString *) theKeyPath
{
keyEncoding dataEncoding;
NSData* cryptedData;
@@ -219,7 +209,10 @@
// convert NSString to NSData and apply encryption scheme
cryptedData = [self dataUsingEncoding: NSUTF8StringEncoding];
cryptedData = [cryptedData asCryptedPassUsingScheme: passwordScheme withSalt: theSalt];
cryptedData = [cryptedData asCryptedPassUsingScheme: passwordScheme
withSalt: theSalt
keyPath: theKeyPath];
// abort on unsupported scheme or error
if (cryptedData == nil)
return nil;
@@ -229,7 +222,7 @@
// hex encoding
return [NSData encodeDataAsHexString: cryptedData];
}
else if(dataEncoding == encBase64)
else if (dataEncoding == encBase64)
{
// base64 encoding
NSString *s = [[NSString alloc] initWithData: [cryptedData dataByEncodingBase64WithLineLength: 1024]
@@ -352,7 +345,7 @@
// See http://en.wikipedia.org/wiki/LM_hash#Algorithm
d = [[self uppercaseString] dataUsingEncoding: NSWindowsCP1252StringEncoding];
return [[NSData encodeDataAsHexString: [d asLM]] uppercaseString];
}
+1 -1
View File
@@ -739,7 +739,7 @@ static int cssEscapingCount;
substrLen = [substring length];
matchRange = NSMakeRange (0, selfLen);
while (!done)
while (!done && matchRange.length > 0)
{
substrRange = [self rangeOfString: substring options: 0 range: matchRange];
if (substrRange.location == NSNotFound)
-8
View File
@@ -31,7 +31,6 @@
@class NGImap4Connection;
@class SOGoGroup;
@class SOGoObject;
@class SOGoUser;
@class SOGoUserDefaults;
@@ -67,13 +66,6 @@
withName: (NSString *) userName;
- (id) userNamed: (NSString *) name;
- (void) registerGroup: (SOGoGroup *) group
withName: (NSString *) groupName
inDomain: (NSString *) domainName;
- (id) groupNamed: (NSString *) groupName
inDomain: (NSString *) domainName;
- (void) registerIMAP4Connection: (NGImap4Connection *) connection
forKey: (NSString *) key;
- (NGImap4Connection *) imap4ConnectionForKey: (NSString *) key;
+1 -17
View File
@@ -1,6 +1,6 @@
/* SOGoCache.m - this file is part of SOGo
*
* Copyright (C) 2008-2014 Inverse inc.
* Copyright (C) 2008-2020 Inverse inc.
*
* This file is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -260,22 +260,6 @@ static memcached_st *handle = NULL;
return [users objectForKey: name];
}
- (void) registerGroup: (SOGoGroup *) group
withName: (NSString *) groupName
inDomain: (NSString *) domainName
{
if (group)
[groups setObject: group forKey: [NSString stringWithFormat: @"%@+%@", groupName, domainName]];
}
- (id) groupNamed: (NSString *) groupName
inDomain: (NSString *) domainName
{
return [groups objectForKey: [NSString stringWithFormat: @"%@+%@", groupName, domainName]];
}
- (void) registerIMAP4Connection: (NGImap4Connection *) connection
forKey: (NSString *) key
{
+1 -2
View File
@@ -30,7 +30,7 @@
SOGoLDAPContactInfoAttribute = "description";
SOGoMailingMechanism = "sendmail";
SOGoSMTPServer = "localhost";
SOGoSMTPServer = "smtp://localhost";
SOGoMailSpoolPath = "/var/spool/sogo";
SOGoWebAccessEnabled = YES;
@@ -103,7 +103,6 @@
SOGoMailReplyPlacement = "below";
SOGoMailSignaturePlacement = "below";
SOGoRefreshViewIntervals = ( 1, 2, 5, 10, 20, 30, 60 );
SOGoMailComposeMessageType = "text";
SOGoMailListViewColumnsOrder = ( "Thread", "Flagged", "Attachment", "Subject",
"From", "Unread", "Date", "Priority",
"Size" );
+3
View File
@@ -48,10 +48,13 @@
- (BOOL) forceExternalLoginWithEmail;
- (BOOL) externalAvatarsEnabled;
- (BOOL) sieveScriptsEnabled;
- (NSString *) sieveScriptHeaderTemplateFile;
- (NSString *) sieveScriptFooterTemplateFile;
- (BOOL) forwardEnabled;
- (int) forwardConstraints;
- (NSArray *) forwardConstraintsDomains;
- (BOOL) vacationEnabled;
- (BOOL) vacationPeriodEnabled;
- (NSString *) vacationDefaultSubject;
- (NSString *) vacationHeaderTemplateFile;
- (NSString *) vacationFooterTemplateFile;
+31 -1
View File
@@ -196,6 +196,16 @@
return [self boolForKey: @"SOGoSieveScriptsEnabled"];
}
- (NSString *) sieveScriptHeaderTemplateFile
{
return [self stringForKey: @"SOGoSieveScriptHeaderTemplateFile"];
}
- (NSString *) sieveScriptFooterTemplateFile
{
return [self stringForKey: @"SOGoSieveScriptFooterTemplateFile"];
}
- (BOOL) forwardEnabled
{
return [self boolForKey: @"SOGoForwardEnabled"];
@@ -220,6 +230,18 @@
return [self boolForKey: @"SOGoVacationEnabled"];
}
- (BOOL) vacationPeriodEnabled
{
id o;
o = [self stringForKey: @"SOGoVacationPeriodEnabled"];
if (o)
return [o boolValue];
return YES;
}
- (NSString *) vacationDefaultSubject
{
return [self stringForKey: @"SOGoVacationDefaultSubject"];
@@ -259,7 +281,15 @@
- (NSString *) smtpServer
{
return [self stringForKey: @"SOGoSMTPServer"];
NSString *server;
server = [self stringForKey: @"SOGoSMTPServer"];
// backwards compatibility
if (![server hasPrefix: @"smtp://"] &&
![server hasPrefix: @"smtps://"])
{
return [NSString stringWithFormat: @"smtp://%@", server];
}
return server;
}
- (NSString *) smtpAuthenticationType
+102 -57
View File
@@ -49,7 +49,7 @@
#import "SOGoCache.h"
#import "SOGoContentObject.h"
#import "SOGoDomainDefaults.h"
#import "SOGoGroup.h"
#import "SOGoSource.h"
#import "SOGoParentFolder.h"
#import "SOGoPermissions.h"
#import "SOGoUser.h"
@@ -477,11 +477,18 @@ static NSArray *childRecordFields = nil;
if ([newName length])
{
[self renameTo: newName];
error = nil;
NS_DURING
{
[self renameTo: newName];
error = nil;
}
NS_HANDLER
error = [NSException exceptionWithHTTPStatus: 409
reason: @"Existing name"];
NS_ENDHANDLER;
}
else
error = [NSException exceptionWithHTTPStatus: 400
error = [NSException exceptionWithHTTPStatus: 403
reason: @"Empty string"];
return error;
@@ -645,6 +652,12 @@ static NSArray *childRecordFields = nil;
- (void) renameTo: (NSString *) newName
{
if (!displayName)
[self displayName];
if ([displayName isEqualToString: newName])
return;
#warning SOGoFolder should have the corresponding method
[displayName release];
displayName = nil;
@@ -910,34 +923,47 @@ static NSArray *childRecordFields = nil;
{
NSMutableDictionary *moduleSettings, *folderShowAlarms;
NSMutableArray *folderSubscription;
NSString *subscriptionPointer;
NSString *subscriptionPointer, *domain;
NSMutableArray *allUsers;
SOGoUserSettings *us;
NSDictionary *dict;
SOGoUser *sogoUser;
BOOL rc;
int i;
dict = [[SOGoUserManager sharedUserManager] contactInfosForUserWithUIDorEmail: theIdentifier];
domain = [[context activeUser] domain];
dict = [[SOGoUserManager sharedUserManager] contactInfosForUserWithUIDorEmail: theIdentifier
inDomain: domain];
if ([[dict objectForKey: @"isGroup"] boolValue])
if (dict && [[dict objectForKey: @"isGroup"] boolValue])
{
SOGoGroup *aGroup;
id <SOGoSource> source;
aGroup = [SOGoGroup groupWithIdentifier: theIdentifier
inDomain: [[context activeUser] domain]];
allUsers = [NSMutableArray arrayWithArray: [aGroup members]];
source = [[SOGoUserManager sharedUserManager] sourceWithID: [dict objectForKey: @"SOGoSource"]];
if ([source conformsToProtocol:@protocol(SOGoMembershipSource)])
{
NSArray *members;
// We remove the active user from the group (if present) in order to
// not subscribe him to their own resource!
[allUsers removeObject: [context activeUser]];
members = [(id<SOGoMembershipSource>)(source) membersForGroupWithUID: [dict objectForKey: @"c_uid"]];
allUsers = [NSMutableArray array];
for (i = 0; i < [members count]; i++)
{
[allUsers addObject: [[members objectAtIndex: i] objectForKey: @"c_uid"]];
}
// We remove the active user from the group (if present) in order to
// not subscribe him to their own resource!
[allUsers removeObject: [[context activeUser] login]];
}
else
{
[self errorWithFormat: @"Inconsistency (subscribeUserOrGroup:reallyDo:response:) error - got group identifier (%@) from a source (%@) that does not support groups (%@).", theIdentifier, [dict objectForKey: @"SOGoSource"], NSStringFromClass([source class])];
return NO;
}
}
else
{
sogoUser = [SOGoUser userWithLogin: theIdentifier roles: nil];
if (sogoUser)
allUsers = [NSArray arrayWithObject: sogoUser];
if (dict)
allUsers = [NSArray arrayWithObject: [dict objectForKey: @"c_uid"]];
else
allUsers = [NSArray array];
}
@@ -950,8 +976,7 @@ static NSArray *childRecordFields = nil;
for (i = 0; i < [allUsers count]; i++)
{
sogoUser = [allUsers objectAtIndex: i];
us = [sogoUser userSettings];
us = [SOGoUserSettings settingsForUser: [allUsers objectAtIndex: i]];
moduleSettings = [us objectForKey: [container nameInContainer]];
if (!(moduleSettings
&& [moduleSettings isKindOfClass: [NSMutableDictionary class]]))
@@ -1608,14 +1633,11 @@ static NSArray *childRecordFields = nil;
matchingUID: (NSString *) uid
{
int count, max;
NSDictionary *record;
NSDictionary *record, *dict;
NSString *currentUID, *domain;
SOGoGroup *group;
NSMutableArray *acls;
acls = [NSMutableArray array];
#warning should it be the domain of the ownerUser instead?
domain = [[context activeUser] domain];
max = [records count];
for (count = 0; count < max; count++)
@@ -1624,17 +1646,27 @@ static NSArray *childRecordFields = nil;
currentUID = [record valueForKey: @"c_uid"];
if ([currentUID hasPrefix: @"@"])
{
group = [[SOGoCache sharedCache] groupNamed: currentUID inDomain: domain];
domain = [[context activeUser] domain];
dict = [[SOGoUserManager sharedUserManager] contactInfosForUserWithUIDorEmail: currentUID
inDomain: domain];
if (dict)
{
id <SOGoSource> source;
if (!group)
{
group = [SOGoGroup groupWithIdentifier: currentUID
inDomain: domain];
[[SOGoCache sharedCache] registerGroup: group withName: currentUID inDomain: domain];
}
if (group && [group hasMemberWithUID: uid])
[acls addObject: [record valueForKey: @"c_role"]];
source = [[SOGoUserManager sharedUserManager] sourceWithID: [dict objectForKey: @"SOGoSource"]];
if ([source conformsToProtocol:@protocol(SOGoMembershipSource)])
{
if ([(id<SOGoMembershipSource>)(source) groupWithUIDHasMemberWithUID: currentUID memberUid: uid])
[acls addObject: [record valueForKey: @"c_role"]];
else
[self errorWithFormat: @"Group %@ has no member with UID %@", currentUID, uid];
}
else
{
[self errorWithFormat: @"Inconsistency (_aclsFromGroupRoles:matchingUID:) error - got group identifier (%@) from a source (%@) that does not support groups (%@).", currentUID, [dict objectForKey: @"SOGoSource"], NSStringFromClass([source class])];
return [NSArray array];
}
}
}
}
@@ -1744,36 +1776,49 @@ static NSArray *childRecordFields = nil;
NSString *uid, *uids, *qs, *objectPath, *domain;
NSMutableArray *usersAndGroups, *groupsMembers;
NSMutableDictionary *aclsForObject;
SOGoGroup *group;
unsigned int i;
if ([users count] > 0)
{
domain = [[context activeUser] domain];
usersAndGroups = [NSMutableArray arrayWithArray: users];
groupsMembers = [NSMutableArray array];
for (i = 0; i < [usersAndGroups count]; i++)
{
NSDictionary *dict;
uid = [usersAndGroups objectAtIndex: i];
group = [SOGoGroup groupWithIdentifier: uid inDomain: domain];
if (group)
domain = [[context activeUser] domain];
dict = [[SOGoUserManager sharedUserManager] contactInfosForUserWithUIDorEmail: uid
inDomain: domain];
if (dict && [[dict objectForKey: @"isGroup"] boolValue])
{
NSArray *members;
SOGoUser *user;
unsigned int j;
// Fetch members to remove them from the cache along the group
members = [group members];
for (j = 0; j < [members count]; j++)
id <SOGoSource> source;
source = [[SOGoUserManager sharedUserManager] sourceWithID: [dict objectForKey: @"SOGoSource"]];
if ([source conformsToProtocol:@protocol(SOGoMembershipSource)])
{
user = [members objectAtIndex: j];
[groupsMembers addObject: [user login]];
}
NSDictionary *user;
NSArray *members;
unsigned int j;
if (![uid hasPrefix: @"@"])
// Prefix the UID with the character "@" when dealing with a group
[usersAndGroups replaceObjectAtIndex: i
withObject: [NSString stringWithFormat: @"@%@", uid]];
// Fetch members to remove them from the cache along the group
members = [(id<SOGoMembershipSource>)(source) membersForGroupWithUID: uid];
for (j = 0; j < [members count]; j++)
{
user = [members objectAtIndex: j];
[groupsMembers addObject: [user objectForKey: @"c_uid"]];
}
if (![uid hasPrefix: @"@"])
// Prefix the UID with the character "@" when dealing with a group
[usersAndGroups replaceObjectAtIndex: i
withObject: [NSString stringWithFormat: @"@%@", uid]];
}
else
{
[self errorWithFormat: @"Inconsistency error (removeAclsForUsers:forObjectAtPath:) - got group identifier (%@) from a source (%@) that does not support groups (%@).", uid, [dict objectForKey: @"SOGoSource"], NSStringFromClass([source class])];
return;
}
}
}
objectPath = [objectPathArray componentsJoinedByString: @"/"];
@@ -1835,7 +1880,6 @@ static NSArray *childRecordFields = nil;
{
NSString *objectPath, *aUID, *domain;
NSMutableArray *newRoles;
SOGoGroup *group;
objectPath = [objectPathArray componentsJoinedByString: @"/"];
@@ -1845,10 +1889,11 @@ static NSArray *childRecordFields = nil;
aUID = uid;
if (![uid hasPrefix: @"@"])
{
// Prefix the UID with the character "@" when dealing with a group
NSDictionary *dict;
domain = [[context activeUser] domain];
group = [SOGoGroup groupWithIdentifier: uid inDomain: domain];
if (group)
dict = [[SOGoUserManager sharedUserManager] contactInfosForUserWithUIDorEmail: uid
inDomain: domain];
if ([[dict objectForKey: @"isGroup"] boolValue])
{
aUID = [NSString stringWithFormat: @"@%@", uid];
// Remove all roles when defining ACLs for a group
-60
View File
@@ -1,60 +0,0 @@
/* SOGoGroup.h - this file is part of SOGo
*
* Copyright (C) 2009-2012 Inverse inc.
*
* Author: Ludovic Marcotte <lmarcotte@inverse.ca>
*
* This file is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2, or (at your option)
* any later version.
*
* This file is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; see the file COPYING. If not, write to
* the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
* Boston, MA 02111-1307, USA.
*/
#ifndef __SOGOGROUP_H__
#define __SOGOGROUP_H__
#import <Foundation/NSObject.h>
@class LDAPSource;
@class NSArray;
@class NSMutableArray;
@class NSString;
@class NGLdapEntry;
@protocol SOGoSource;
@interface SOGoGroup : NSObject
{
@private
NSString *_identifier;
NSString *_domain;
NGLdapEntry *_entry;
NSObject <SOGoSource> *_source;
NSMutableArray *_members;
}
+ (id) groupWithIdentifier: (NSString *) theID
inDomain: (NSString *) domain;
+ (id) groupWithEmail: (NSString *) theEmail
inDomain: (NSString *) domain;
+ (id) groupWithValue: (NSString *) theValue
andSourceSelector: (SEL) theSelector
inDomain: (NSString *) domain;
- (NSArray *) members;
- (BOOL) hasMemberWithUID: (NSString *) memberUID;
@end
#endif // __SOGOGROUP_H__
-370
View File
@@ -1,370 +0,0 @@
/* SOGoGroup.m - this file is part of SOGo
*
* Copyright (C) 2009-2014 Inverse inc.
*
* This file is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 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.
*/
/*
Here are some group samples:
[ POSIX group ]
dn: cn=it-staff,ou=Group,dc=zzz,dc=xxx,dc=yyy
objectClass: posixGroup
objectClass: top
cn: it-staff
userPassword: {crypt}x
gidNumber: 8000
memberUid: lsa
memberUid: mrm
memberUid: ij
memberUid: no
memberUid: ld
memberUid: db
memberUid: rgl
memberUid: ja
memberUid: hbt
memberUid: hossein
dn: cn=inverse,ou=groups,dc=inverse,dc=ca
objectClass: groupOfUniqueNames
objectClass: top
objectClass: extensibleObject
uniqueMember: uid=flachapelle,ou=users,dc=inverse,dc=ca
uniqueMember: uid=lmarcotte,ou=users,dc=inverse,dc=ca
uniqueMember: uid=wsourdeau,ou=users,dc=inverse,dc=ca
cn: inverse
mail: inverse@inverse.ca
*/
#include "SOGoGroup.h"
#include "SOGoCache.h"
#include "SOGoSource.h"
#include "SOGoSystemDefaults.h"
#include "SOGoUserManager.h"
#include "SOGoUser.h"
#import <NGLdap/NGLdapEntry.h>
#define CHECK_CLASS(o) ({ \
if ([o isKindOfClass: [NSString class]]) \
o = [NSArray arrayWithObject: o]; \
})
@implementation SOGoGroup
- (id) initWithIdentifier: (NSString *) theID
domain: (NSString *) theDomain
source: (NSObject <SOGoSource> *) theSource
entry: (NGLdapEntry *) theEntry
{
self = [super init];
if (self)
{
ASSIGN(_identifier, theID);
ASSIGN(_domain, theDomain);
ASSIGN(_source, theSource);
ASSIGN(_entry, theEntry);
_members = nil;
}
return self;
}
- (void) dealloc
{
RELEASE(_identifier);
RELEASE(_domain);
RELEASE(_source);
RELEASE(_entry);
RELEASE(_members);
[super dealloc];
}
+ (id) groupWithIdentifier: (NSString *) theID
inDomain: (NSString *) domain
{
NSRange r;
NSString *uid, *inDomain;
SOGoSystemDefaults *sd;
uid = [theID hasPrefix: @"@"] ? [theID substringFromIndex: 1] : theID;
inDomain = domain;
sd = [SOGoSystemDefaults sharedSystemDefaults];
if ([sd enableDomainBasedUID])
{
/* Split domain from uid */
r = [uid rangeOfString: @"@" options: NSBackwardsSearch];
if (r.location != NSNotFound)
{
if (!domain)
inDomain = [uid substringFromIndex: r.location + 1];
uid = [uid substringToIndex: r.location];
}
}
return [SOGoGroup groupWithValue: uid
andSourceSelector: @selector (lookupGroupEntryByUID:inDomain:)
inDomain: inDomain];
}
+ (id) groupWithEmail: (NSString *) theEmail
inDomain: (NSString *) domain
{
return [SOGoGroup groupWithValue: theEmail
andSourceSelector: @selector (lookupGroupEntryByEmail:inDomain:)
inDomain: domain];
}
//
// Returns nil if theValue doesn't match to a group
// (so its objectClass isn't a group)
//
+ (id) groupWithValue: (NSString *) theValue
andSourceSelector: (SEL) theSelector
inDomain: (NSString *) domain
{
NSArray *allSources;
NGLdapEntry *entry;
NSObject <SOGoSource, SOGoDNSource> *source;
id o;
NSEnumerator *gclasses;
NSString *gclass;
int i;
// Don't bother looking in all sources if the
// supplied value is nil.
if (!theValue)
return nil;
allSources = [[SOGoUserManager sharedUserManager]
sourceIDsInDomain: domain];
entry = nil;
o = nil;
for (i = 0; i < [allSources count]; i++)
{
source = (NSObject <SOGoSource, SOGoDNSource> *) [[SOGoUserManager sharedUserManager] sourceWithID: [allSources objectAtIndex: i]];
// Our different sources might not all implements groups support
if ([source respondsToSelector: theSelector])
entry = [source performSelector: theSelector
withObject: theValue
withObject: domain];
if (entry)
break;
entry = nil;
}
if (entry)
{
NSArray *classes;
// We check to see if it's a group
classes = [[entry asDictionary] objectForKey: @"objectclass"];
if (classes)
{
/* LDAP records returned as dictionaries may contain NSString or
NSArray values, depending on whether the amount of values
assigned to a key is 1 or more. Since this can occur with
"objectclass" too, we need to check whether "classes" is actually
an NSString instance... */
if ([classes isKindOfClass: [NSString class]])
classes = [NSArray arrayWithObject:
[(NSString *) classes lowercaseString]];
else
{
int i, c;
classes = [NSMutableArray arrayWithArray: classes];
c = [classes count];
for (i = 0; i < c; i++)
[(id)classes replaceObjectAtIndex: i
withObject: [[classes objectAtIndex: i] lowercaseString]];
}
}
gclasses = [[source groupObjectClasses] objectEnumerator];
while ((gclass = [gclasses nextObject]))
if ([classes containsObject: gclass])
{
// Found a group, let's return it.
o = [[self alloc] initWithIdentifier: theValue
domain: domain
source: source
entry: entry];
AUTORELEASE(o);
}
}
return o;
}
//
// This method actually try to obtain all members
// from either dynamic of static groups.
//
- (NSArray *) members
{
NSMutableArray *dns, *uids, *logins;
NSString *dn, *login;
SOGoUserManager *um;
NSDictionary *d;
SOGoUser *user;
NSArray *o;
NSAutoreleasePool *pool;
int i, c;
if (!_members)
{
_members = [NSMutableArray new];
uids = [NSMutableArray array];
dns = [NSMutableArray array];
logins = [NSMutableArray array];
// We check if it's a static group
// Fetch "members" - we get DNs
d = [_entry asDictionary];
o = [d objectForKey: @"member"];
CHECK_CLASS(o);
if (o) [dns addObjectsFromArray: o];
// Fetch "uniqueMembers" - we get DNs
o = [d objectForKey: @"uniquemember"];
CHECK_CLASS(o);
if (o) [dns addObjectsFromArray: o];
// Fetch "memberUid" - we get UID (like login names)
o = [d objectForKey: @"memberuid"];
CHECK_CLASS(o);
if (o) [uids addObjectsFromArray: o];
c = [dns count] + [uids count];
// We deal with a static group, let's add the members
if (c)
{
um = [SOGoUserManager sharedUserManager];
// We add members for whom we have their associated DN
for (i = 0; i < [dns count]; i++)
{
pool = [NSAutoreleasePool new];
dn = [dns objectAtIndex: i];
login = [um getLoginForDN: [dn lowercaseString]];
user = [SOGoUser userWithLogin: login roles: nil];
if (user)
{
[logins addObject: login];
[_members addObject: user];
}
[pool release];
}
// We add members for whom we have their associated login name
for (i = 0; i < [uids count]; i++)
{
pool = [NSAutoreleasePool new];
login = [uids objectAtIndex: i];
user = [SOGoUser userWithLogin: login roles: nil];
if (user)
{
[logins addObject: [user loginInDomain]];
[_members addObject: user];
}
[pool release];
}
// We are done fetching members, let's cache the members of the group
// (ie., their UIDs) in memcached to speed up -hasMemberWithUID.
[[SOGoCache sharedCache] setValue: [logins componentsJoinedByString: @","]
forKey: [NSString stringWithFormat: @"%@+%@", _identifier, _domain]];
}
else
{
// We deal with a dynamic group, let's search all users for whom
// memberOf is equal to our group's DN.
// We also need to look for labelelURI?
}
}
return _members;
}
//
//
//
- (BOOL) hasMemberWithUID: (NSString *) memberUID
{
BOOL rc;
rc = NO;
// If _members is initialized, we use it as it's very accurate.
// Otherwise, we fallback on memcached in order to avoid
// decomposing the group all the time just to see if a user
// is a member of it.
if (_members)
{
NSString *currentUID;
int count, max;
max = [_members count];
for (count = 0; !rc && count < max; count++)
{
currentUID = [[_members objectAtIndex: count] login];
rc = [memberUID isEqualToString: currentUID];
}
}
else
{
NSString *key, *value;;
NSArray *a;
key = [NSString stringWithFormat: @"%@+%@", _identifier, _domain];
value = [[SOGoCache sharedCache] valueForKey: key];
// If the value isn't in memcached, that probably means -members was never called.
// We call it only once here.
if (!value)
{
[self members];
value = [[SOGoCache sharedCache] valueForKey: key];
}
a = [value componentsSeparatedByString: @","];
rc = [a containsObject: memberUID];
}
return rc;
}
@end
+11 -23
View File
@@ -22,6 +22,7 @@
#import <NGObjWeb/NSException+HTTP.h>
#import <NGExtensions/NSObject+Logs.h>
#import <NGExtensions/NSURL+misc.h>
#import <NGMail/NGSendMail.h>
#import <NGMail/NGSmtpClient.h>
#import <NGMime/NGMimePartGenerator.h>
@@ -216,40 +217,27 @@
}
- (NSException *) _smtpSendData: (NSData *) mailData
toRecipients: (NSArray *) recipients
sender: (NSString *) sender
toRecipients: (NSArray *) recipients
sender: (NSString *) sender
withAuthenticator: (id <SOGoAuthenticator>) authenticator
inContext: (WOContext *) woContext
{
NSString *currentTo, *host, *login, *password;
NGInternetSocketAddress *addr;
NSString *currentTo, *login, *password;
NSMutableArray *toErrors;
NSEnumerator *addresses;
NSEnumerator *addresses;
NGSmtpClient *client;
NSException *result;
NSRange r;
unsigned int port;
NSURL * smtpUrl;
client = [NGSmtpClient smtpClient];
host = smtpServer;
result = nil;
port = 25;
// We check if there is a port specified in the smtpServer ivar value
r = [smtpServer rangeOfString: @":"];
if (r.length)
{
port = [[smtpServer substringFromIndex: r.location+1] intValue];
host = [smtpServer substringToIndex: r.location];
}
smtpUrl = [[[NSURL alloc] initWithString: smtpServer] autorelease];
addr = [NGInternetSocketAddress addressWithPort: port
onHost: host];
client = [NGSmtpClient clientWithURL: smtpUrl];
NS_DURING
{
[client connectToAddress: addr];
[client connect];
if ([authenticationType isEqualToString: @"plain"])
{
/* XXX Allow static credentials by peeking at the classname */
@@ -297,7 +285,7 @@
@" (smtp) all recipients discarded"];
else if ([toErrors count] > 0)
result = [NSException exceptionWithHTTPStatus: 500
reason: [NSString stringWithFormat:
reason: [NSString stringWithFormat:
@"cannot send message (smtp) - recipients discarded:\n%@",
[toErrors componentsJoinedByString: @", "]]];
else
@@ -313,7 +301,7 @@
}
NS_HANDLER
{
[self errorWithFormat: @"Could not connect to the SMTP server %@ on port %d", host, port];
[self errorWithFormat: @"Could not connect to the SMTP server %@", smtpServer];
result = [NSException exceptionWithHTTPStatus: 500
reason: @"cannot send message:"
@" (smtp) error when connecting"];
+10 -5
View File
@@ -1,6 +1,6 @@
/* SOGoSieveManager.h - this file is part of SOGo
*
* Copyright (C) 2010-2014 Inverse inc.
* Copyright (C) 2010-2019 Inverse inc.
*
* Author: Inverse <info@inverse.ca>
*
@@ -25,6 +25,7 @@
@class NSDictionary;
@class NSException;
@class NSMutableArray;
@class NSString;
@class NGSieveClient;
@@ -41,7 +42,8 @@
+ (id) sieveManagerForUser: (SOGoUser *) user;
- (id) initForUser: (SOGoUser *) newUser;
- (NSString *) sieveScriptWithRequirements: (NSMutableArray *) newRequirements;
- (NSString *) sieveScriptWithRequirements: (NSMutableArray *) newRequirements
delimiter: (NSString *) delimiter;
- (NSString *) lastScriptError;
- (NGSieveClient *) clientForAccount: (SOGoMailAccount *) theAccount;
@@ -49,10 +51,13 @@
withUsername: (NSString *) theUsername
andPassword: (NSString *) thePassword;
- (BOOL) updateFiltersForAccount: (SOGoMailAccount *) theAccount;
- (BOOL) updateFiltersForAccount: (SOGoMailAccount *) theAccount
- (BOOL) hasActiveExternalSieveScripts: (NGSieveClient *) client;
- (NSException *) updateFiltersForAccount: (SOGoMailAccount *) theAccount;
- (NSException *) updateFiltersForAccount: (SOGoMailAccount *) theAccount
withUsername: (NSString *) theUsername
andPassword: (NSString *) thePassword;
andPassword: (NSString *) thePassword
forceActivation: (BOOL) forceActivation;
@end
+179 -43
View File
@@ -21,6 +21,7 @@
*/
#import <Foundation/NSCalendarDate.h>
#import <Foundation/NSCharacterSet.h>
#import <Foundation/NSURL.h>
#import <Foundation/NSValue.h>
@@ -32,7 +33,11 @@
#import <SOGo/SOGoTextTemplateFile.h>
#import <NGExtensions/NSObject+Logs.h>
#import <NGExtensions/NSString+Ext.h>
#import <NGImap4/NGImap4Connection.h>
#import <NGImap4/NGImap4Client.h>
#import <NGImap4/NGSieveClient.h>
#import <NGObjWeb/NSException+HTTP.h>
#import "../Mailer/SOGoMailAccount.h"
@@ -251,6 +256,44 @@ static NSString *sieveScriptName = @"sogo";
return YES;
}
- (NSString *) _extractRequirementsFromContent: (NSString *) theContent
intoArray: (NSMutableArray *) theRequirements
{
NSString *line, *v;
NSArray *lines;
id o;
int i, count;
lines = [theContent componentsSeparatedByCharactersInSet: [NSCharacterSet newlineCharacterSet]];
count = [lines count];
for (i = 0; i < count; i++)
{
line = [[lines objectAtIndex: i] stringByTrimmingSpaces];
if ([line hasPrefix: @"require "])
{
line = [line substringFromIndex: 8];
// Handle lines like: require "imapflags";
if ([line characterAtIndex: 0] == '"')
{
v = [line substringToIndex: [line length]-2];
[theRequirements addObject: v];
}
// Else handle lines like: require ["imapflags","vacation"];
else if ([line characterAtIndex: 0] == '[')
{
o = [[line substringToIndex: [line length]-1] objectFromJSONString];
[theRequirements addObjectsFromArray: o];
}
}
else
break;
}
return [[lines subarrayWithRange: NSMakeRange(i, count-i)] componentsJoinedByString: @"\n"];
}
- (BOOL) _extractRuleField: (NSString **) field
fromRule: (NSDictionary *) rule
andType: (UIxFilterFieldType *) type
@@ -450,7 +493,6 @@ static NSString *sieveScriptName = @"sogo";
{
NSString *sieveAction, *method, *requirement, *argument, *flag, *mailbox;
NSDictionary *mailLabels;
SOGoDomainDefaults *dd;
sieveAction = nil;
@@ -486,7 +528,6 @@ static NSString *sieveScriptName = @"sogo";
}
else if ([method isEqualToString: @"fileinto"])
{
dd = [user domainDefaults];
mailbox
= [[argument componentsSeparatedByString: @"/"]
componentsJoinedByString: delimiter];
@@ -592,7 +633,7 @@ static NSString *sieveScriptName = @"sogo";
sieveScript = [NSMutableString string];
ASSIGN (requirements, newRequirements);
ASSIGN(requirements, newRequirements);
[scriptError release];
scriptError = nil;
@@ -613,8 +654,7 @@ static NSString *sieveScriptName = @"sogo";
}
[scriptError retain];
[requirements release];
requirements = nil;
DESTROY(requirements);
if (scriptError)
sieveScript = nil;
@@ -765,44 +805,75 @@ static NSString *sieveScriptName = @"sogo";
return [client autorelease];
}
//
//
//
- (BOOL) updateFiltersForAccount: (SOGoMailAccount *) theAccount
- (BOOL) hasActiveExternalSieveScripts: (NGSieveClient *) client
{
return [self updateFiltersForAccount: theAccount
withUsername: nil
andPassword: nil];
NSDictionary *scripts;
NSEnumerator *keys;
NSString *key;
scripts = [client listScripts];
keys = [scripts keyEnumerator];
while ((key = [keys nextObject]))
{
if ([key caseInsensitiveCompare: @"sogo"] != NSOrderedSame &&
[[scripts objectForKey: key] intValue] > 0)
return YES;
}
return NO;
}
//
//
//
- (BOOL) updateFiltersForAccount: (SOGoMailAccount *) theAccount
- (NSException *) updateFiltersForAccount: (SOGoMailAccount *) theAccount
{
return [self updateFiltersForAccount: theAccount
withUsername: nil
andPassword: nil
forceActivation: NO];
}
//
//
//
- (NSException *) updateFiltersForAccount: (SOGoMailAccount *) theAccount
withUsername: (NSString *) theUsername
andPassword: (NSString *) thePassword
forceActivation: (BOOL) forceActivation
{
NSString *filterScript, *v, *delimiter, *content, *message;
NSMutableArray *req;
NSMutableString *script, *header;
NSDictionary *result, *values;
NSException *error;
SOGoUserDefaults *ud;
SOGoDomainDefaults *dd;
NGSieveClient *client;
NSString *filterScript, *v;
BOOL b, dateCapability;
NGImap4Client *imapClient;
BOOL b, activate, dateCapability;
unsigned int now;
error = nil;
dd = [user domainDefaults];
if (!([dd sieveScriptsEnabled] || [dd vacationEnabled] || [dd forwardEnabled]))
return YES;
return error;
req = [NSMutableArray arrayWithCapacity: 15];
ud = [user userDefaults];
client = [self clientForAccount: theAccount withUsername: theUsername andPassword: thePassword];
if (!client)
return NO;
{
error = [NSException exceptionWithHTTPStatus: 500 /* Server Error */
reason: @"Error while connecting to Sieve server."];
return error;
}
// Activate script Sieve when forced or when no external script is enabled
activate = forceActivation || ![self hasActiveExternalSieveScripts: client];
// We adjust the "methodRequirements" based on the server's
// capabilities. Cyrus exposes "imapflags" while Dovecot (and
@@ -821,9 +892,26 @@ static NSString *sieveScriptName = @"sogo";
//
script = [NSMutableString string];
// We grab the IMAP4 delimiter using the supplied username/password
if (thePassword)
{
imapClient = [NGImap4Client clientWithURL: [theAccount imap4URL]];
[imapClient login: theUsername password: thePassword];
}
else
imapClient = [[theAccount imap4Connection] client];
delimiter = [imapClient delimiter];
if (!delimiter)
[imapClient list: @"INBOX" pattern: @""];
if (!delimiter)
delimiter = [dd stringForKey: @"NGImap4ConnectionStringSeparator"];
// We first handle filters
filterScript = [self sieveScriptWithRequirements: req
delimiter: [theAccount imap4Separator]];
delimiter: delimiter];
if (filterScript)
{
if ([filterScript length])
@@ -834,9 +922,12 @@ static NSString *sieveScriptName = @"sogo";
}
else
{
[self errorWithFormat: @"Sieve generation failure: %@", [self lastScriptError]];
message = [NSString stringWithFormat: @"Sieve generation failure: %@", [self lastScriptError]];
[self errorWithFormat: message];
[client closeConnection];
return NO;
error = [NSException exceptionWithHTTPStatus: 500 /* Server Error */
reason: message];
return error;
}
//
@@ -914,7 +1005,9 @@ static NSString *sieveScriptName = @"sogo";
}
// Start date of auto-reply
if ([[values objectForKey: @"startDateEnabled"] boolValue] && dateCapability)
if ([dd vacationPeriodEnabled] &&
[[values objectForKey: @"startDateEnabled"] boolValue] &&
dateCapability)
{
[req addObjectUniquely: @"date"];
[req addObjectUniquely: @"relational"];
@@ -925,7 +1018,9 @@ static NSString *sieveScriptName = @"sogo";
}
// End date of auto-reply
if ([[values objectForKey: @"endDateEnabled"] boolValue] && dateCapability)
if ([dd vacationPeriodEnabled] &&
[[values objectForKey: @"endDateEnabled"] boolValue] &&
dateCapability)
{
[req addObjectUniquely: @"date"];
[req addObjectUniquely: @"relational"];
@@ -1012,46 +1107,87 @@ static NSString *sieveScriptName = @"sogo";
[script appendString: @"keep;\r\n"];
}
// We handle header/footer Sieve scripts
if ((v = [dd sieveScriptHeaderTemplateFile]))
{
content = [NSString stringWithContentsOfFile: v
encoding: NSUTF8StringEncoding
error: NULL];
if (content)
{
v = [self _extractRequirementsFromContent: content
intoArray: req];
[script insertString: v atIndex: 0];
b = YES;
}
}
if ((v = [dd sieveScriptFooterTemplateFile]))
{
content = [NSString stringWithContentsOfFile: v
encoding: NSUTF8StringEncoding
error: NULL];
if (content)
{
v = [self _extractRequirementsFromContent: content
intoArray: req];
[script appendString: @"\n"];
[script appendString: v];
b = YES;
}
}
if ([req count])
{
header = [NSString stringWithFormat: @"require [\"%@\"];\r\n",
[req componentsJoinedByString: @"\",\""]];
[[req uniqueObjects] componentsJoinedByString: @"\",\""]];
[script insertString: header atIndex: 0];
}
/* We ensure to deactive the current active script since it could prevent
its deletion from the server. */
result = [client setActiveScript: @""];
if (activate)
result = [client setActiveScript: @""];
// We delete the existing Sieve script
result = [client deleteScript: sieveScriptName];
if (![[result valueForKey:@"result"] boolValue]) {
[self logWithFormat: @"WARNING: Could not delete Sieve script - continuing...: %@", result];
}
if (![[result valueForKey:@"result"] boolValue])
[self warnWithFormat: @"Could not delete Sieve script: %@", [[result objectForKey: @"RawResponse"] objectForKey: @"reason"]];
// We put and activate the script only if we actually have a script
// that does something...
/* We put and activate the script only if we actually have a script
that does something... */
if (b && [script length])
{
result = [client putScript: sieveScriptName script: script];
if (![[result valueForKey:@"result"] boolValue]) {
[self logWithFormat: @"Could not upload Sieve script: %@", result];
[client closeConnection];
return NO;
}
if (![[result valueForKey:@"result"] boolValue])
{
message = [NSString stringWithFormat: @"Could not upload Sieve script: %@", [[result objectForKey: @"RawResponse"] objectForKey: @"reason"]];
[self errorWithFormat: message];
[client closeConnection];
error = [NSException exceptionWithHTTPStatus: 500 /* Server Error */
reason: message];
return error;
}
result = [client setActiveScript: sieveScriptName];
if (![[result valueForKey:@"result"] boolValue]) {
[self logWithFormat: @"Could not enable Sieve script: %@", result];
[client closeConnection];
return NO;
}
}
if (activate)
{
result = [client setActiveScript: sieveScriptName];
if (![[result valueForKey:@"result"] boolValue])
{
message = [NSString stringWithFormat: @"Could not enable Sieve script: %@", [[result objectForKey: @"RawResponse"] objectForKey: @"reason"]];
[self errorWithFormat: message];
[client closeConnection];
error = [NSException exceptionWithHTTPStatus: 500 /* Server Error */
reason: message];
return error;
}
}
}
[client closeConnection];
return YES;
return error;
}
@end
+6
View File
@@ -118,4 +118,10 @@
- (void) updateBaseDNFromLogin: (NSString *) theLogin;
@end
@protocol SOGoMembershipSource <SOGoSource>
- (NSArray *) membersForGroupWithUID: (NSString *) uid;
- (BOOL) groupWithUIDHasMemberWithUID: (NSString *) uid
memberUid: (NSString *) memberUid;
@end
#endif /* SOGOSOURCE_H */
+2 -1
View File
@@ -1,5 +1,5 @@
/*
Copyright (C) 2006-2016 Inverse inc.
Copyright (C) 2006-2020 Inverse inc.
This file is part of SOGo.
@@ -120,6 +120,7 @@
- (BOOL) isSuperUser;
- (BOOL) canAuthenticate;
- (NSString *) googleAuthenticatorKey;
/* resource */
- (BOOL) isResource;
+134 -91
View File
@@ -1,5 +1,5 @@
/*
Copyright (C) 2006-2016 Inverse inc.
Copyright (C) 2006-2020 Inverse inc.
This file is part of SOGo.
@@ -39,6 +39,10 @@
#import "SOGoUserSettings.h"
#import "WOResourceManager+SOGo.h"
#if defined(MFA_CONFIG)
#include <liboath/oath.h>
#endif
#import "SOGoUser.h"
@implementation SoUser (SOGoExtension)
@@ -338,18 +342,34 @@
- (NSMutableDictionary *) defaultIdentity
{
NSMutableDictionary *currentIdentity, *defaultIdentity;
NSEnumerator *identities;
NSDictionary *defaultAccount, *currentIdentity;
NSMutableDictionary *defaultIdentity;
NSArray *identities;
NSString *defaultEmail;
unsigned int count, max;
defaultEmail = [NSString stringWithFormat: @"%@@%@", [self loginInDomain], [self domain]];
defaultAccount = [[self mailAccounts] objectAtIndex: 0];
defaultIdentity = nil;
identities = [[self allIdentities] objectEnumerator];
while (!defaultIdentity
&& (currentIdentity = [identities nextObject]))
if ([[currentIdentity objectForKey: @"isDefault"] boolValue])
defaultIdentity = currentIdentity;
identities = [defaultAccount objectForKey: @"identities"];
max = [identities count];
return defaultIdentity;
for (count = 0; count < max; count++)
{
currentIdentity = [identities objectAtIndex: count];
if ([[currentIdentity objectForKey: @"isDefault"] boolValue])
{
defaultIdentity = [NSMutableDictionary dictionaryWithDictionary: currentIdentity];
break;
}
else if ([[currentIdentity objectForKey: @"email"] caseInsensitiveCompare: defaultEmail] == NSOrderedSame)
{
defaultIdentity = [NSMutableDictionary dictionaryWithDictionary: currentIdentity];
}
}
return defaultIdentity; // can be nil
}
- (SOGoDateFormatter *) dateFormatterInContext: (WOContext *) context
@@ -622,19 +642,20 @@
- (void) _appendSystemMailAccountWithDelegatedIdentities: (BOOL) appendDeletegatedIdentities
{
NSString *fullName, *replyTo, *imapLogin, *imapServer, *cImapServer, *signature,
*encryption, *scheme, *action, *query, *customEmail, *defaultEmail, *sieveServer;
NSString *fullName, *imapLogin, *imapServer, *cImapServer,
*encryption, *scheme, *action, *query, *customEmail, *sieveServer;
NSMutableDictionary *mailAccount, *identity, *mailboxes, *receipts, *security, *mailSettings;
NSNumber *port;
NSMutableArray *identities, *mails;
NSArray *delegators, *delegates;
NSURL *url, *cUrl;
unsigned int count, max, default_identity;
unsigned int count, max; //, default_identity;
NSInteger defaultPort;
NSUInteger index;
BOOL hasDefaultIdentity;
[self userDefaults];
[self userSettings];
[self userDefaults]; // set _defaults
[self userSettings]; // set _settings
mailSettings = [_settings objectForKey: @"Mail"];
mailAccount = [NSMutableDictionary new];
@@ -711,89 +732,75 @@
}
// 5. Identities
defaultEmail = [NSString stringWithFormat: @"%@@%@", [self loginInDomain], [self domain]];
default_identity = 0;
identities = [NSMutableArray new];
[identities addObjectsFromArray: [_defaults mailIdentities]];
mails = [NSMutableArray arrayWithArray: [self allEmails]];
[mailAccount setObject: [mails objectAtIndex: 0] forKey: @"name"];
max = [identities count];
hasDefaultIdentity = NO;
fullName = [self cn];
if ([fullName length] == 0)
fullName = login;
replyTo = [_defaults mailReplyTo];
max = [mails count];
/* custom from */
if ([[self domainDefaults] mailCustomFromEnabled])
{
[self userDefaults];
customEmail = [_defaults mailCustomEmail];
fullName = [_defaults mailCustomFullName];
if ([customEmail length] > 0 || [fullName length] > 0)
{
if ([customEmail length] == 0)
customEmail = [mails objectAtIndex: 0];
else if ([fullName length] == 0)
{
// Custom email but default fullname; if the custom email is
// one of the user's emails, remove the duplicated entry
index = [mails indexOfObject: customEmail];
if (index != NSNotFound)
{
[mails removeObjectAtIndex: index];
max--;
}
}
if ([fullName length] == 0)
{
fullName = [self cn];
if ([fullName length] == 0)
fullName = login;
}
identity = [NSMutableDictionary new];
[identity setObject: customEmail forKey: @"email"];
[identity setObject: fullName forKey: @"fullName"];
if ([replyTo length] > 0)
[identity setObject: replyTo forKey: @"replyTo"];
signature = [_defaults mailSignature];
if (signature)
[identity setObject: signature forKey: @"signature"];
[identities addObject: identity];
if ([[identity objectForKey: @"email"] caseInsensitiveCompare: defaultEmail] == NSOrderedSame)
default_identity = [identities count]-1;
[identity release];
}
}
// Sanitize identities
for (count = 0; count < max; count++)
{
identity = [NSMutableDictionary new];
fullName = [self cn];
if (![fullName length])
fullName = login;
[identity setObject: fullName forKey: @"fullName"];
[identity setObject: [[mails objectAtIndex: count] stringByTrimmingSpaces]
forKey: @"email"];
identity = [NSMutableDictionary dictionaryWithDictionary: [identities objectAtIndex: count]];
customEmail = [identity objectForKey: @"email"];
if ([customEmail length])
{
if (![[self domainDefaults] mailCustomFromEnabled])
{
// No custom from -- enforce a valid email
index = [mails indexOfObject: customEmail];
if (index == NSNotFound)
{
[identity setObject: [self systemEmail] forKey: @"email"];
}
}
}
else
{
// Email must be defined
[identity setObject: [self systemEmail] forKey: @"email"];
}
if (![[self domainDefaults] mailCustomFromEnabled])
{
// No custom from -- enforce a valid fullname and remove reply-to
[identity setObject: fullName forKey: @"fullName"];
[identity removeObjectForKey: @"replyTo"];
}
if (!appendDeletegatedIdentities)
{
[identity setObject: [NSNumber numberWithBool: YES] forKey: @"isReadOnly"];
}
if ([[identity objectForKey: @"isDefault"] boolValue])
{
if (hasDefaultIdentity || !appendDeletegatedIdentities)
[identity removeObjectForKey: @"isDefault"]; // only one possible default identity
else
hasDefaultIdentity = YES;
}
[identities replaceObjectAtIndex: count withObject: identity];
}
if ([replyTo length] > 0)
[identity setObject: replyTo forKey: @"replyTo"];
signature = [_defaults mailSignature];
if (signature)
[identity setObject: signature forKey: @"signature"];
if (![identities count])
{
// Create a default identity
identity = [NSMutableDictionary dictionaryWithObjectsAndKeys:
fullName, @"fullName",
[self systemEmail], @"email", nil];
if (appendDeletegatedIdentities)
{
[identity setObject: [NSNumber numberWithBool: YES] forKey: @"isDefault"];
hasDefaultIdentity = YES;
}
else
{
[identity setObject: [NSNumber numberWithBool: YES] forKey: @"isReadOnly"];
}
[identities addObject: identity];
if ([[identity objectForKey: @"email"] caseInsensitiveCompare: defaultEmail] == NSOrderedSame)
default_identity = [identities count]-1;
[identity release];
}
[[identities objectAtIndex: default_identity] setObject: [NSNumber numberWithBool: YES]
forKey: @"isDefault"];
/* identities from delegators */
if (appendDeletegatedIdentities)
@@ -976,11 +983,19 @@
- (NSDictionary *) primaryIdentity
{
NSDictionary *defaultAccount;
NSArray *identities;
NSDictionary *defaultIdentity, *defaultAccount;
defaultAccount = [[self mailAccounts] objectAtIndex: 0];
defaultIdentity = [self defaultIdentity];
return [[defaultAccount objectForKey: @"identities"] objectAtIndex: 0];
if (!defaultIdentity && [[self mailAccounts] count])
{
defaultAccount = [[self mailAccounts] objectAtIndex: 0];
identities = [defaultAccount objectForKey: @"identities"];
defaultIdentity = [identities objectAtIndex: 0];
}
return defaultIdentity;
}
/* folders */
@@ -1079,6 +1094,34 @@
return [authValue boolValue];
}
- (NSString *) googleAuthenticatorKey
{
#if defined(MFA_CONFIG)
NSString *key, *result;
const char *s;
char *secret;
size_t s_len, secret_len;
key = [[[self userSettings] userSalt] substringToIndex: 12];
s = [key UTF8String];
s_len = strlen(s);
oath_init();
oath_base32_encode(s,s_len, &secret, &secret_len);
oath_done();
result = [[NSString alloc] initWithBytesNoCopy: secret
length: secret_len
encoding: NSASCIIStringEncoding
freeWhenDone: YES];
return [result autorelease];
#else
return nil;
#endif
}
/* resource */
- (BOOL) isResource
{
+7 -13
View File
@@ -1,6 +1,6 @@
/* SOGoUserDefaults.h - this file is part of SOGo
*
* Copyright (C) 2011-2017 Inverse inc.
* Copyright (C) 2011-2020 Inverse inc.
*
* This file is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -133,6 +133,9 @@ extern NSString *SOGoWeekStartFirstFullWeek;
- (void) setAnimationMode: (NSString *) newValue;
- (NSString *) animationMode;
- (BOOL) googleAuthenticatorEnabled;
- (void) setGoogleAuthenticatorEnabled: (BOOL) newValue;
- (void) setMailComposeWindow: (NSString *) newValue;
- (NSString *) mailComposeWindow;
@@ -154,21 +157,9 @@ extern NSString *SOGoWeekStartFirstFullWeek;
- (void) setMailReplyPlacement: (NSString *) newValue;
- (NSString *) mailReplyPlacement;
- (void) setMailSignature: (NSString *) newValue;
- (NSString *) mailSignature;
- (void) setMailSignaturePlacement: (NSString *) newValue;
- (NSString *) mailSignaturePlacement;
- (void) setMailCustomFullName: (NSString *) newValue;
- (NSString *) mailCustomFullName;
- (void) setMailCustomEmail: (NSString *) newValue;
- (NSString *) mailCustomEmail;
- (void) setMailReplyTo: (NSString *) newValue;
- (NSString *) mailReplyTo;
- (void) setAllowUserReceipt: (BOOL) allow;
- (BOOL) allowUserReceipt;
- (void) setUserReceiptNonRecipientAction: (NSString *) action;
@@ -191,6 +182,9 @@ extern NSString *SOGoWeekStartFirstFullWeek;
- (void) setMailCertificateAlwaysEncrypt: (BOOL) newValue;
- (BOOL) mailCertificateAlwaysEncrypt;
- (void) setMailIdentities: (NSArray *) newIdentites;
- (NSArray *) mailIdentities;
- (void) setAuxiliaryMailAccounts: (NSArray *) newAccounts;
- (NSArray *) auxiliaryMailAccounts;
+67 -74
View File
@@ -1,6 +1,6 @@
/* SOGoUserDefaults.m - this file is part of SOGo
*
* Copyright (C) 2009-2017 Inverse inc.
* Copyright (C) 2009-2020 Inverse inc.
*
* This file is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -20,6 +20,7 @@
#import <Foundation/NSDictionary.h>
#import <Foundation/NSTimeZone.h>
#import <Foundation/NSValue.h>
#import <NGImap4/NSString+Imap4.h>
#import <NGObjWeb/WOApplication.h>
@@ -114,33 +115,55 @@ NSString *SOGoWeekStartFirstFullWeek = @"FirstFullWeek";
return rc;
}
- (BOOL) _migrateSignature
- (BOOL) _migrateMailIdentities
{
BOOL rc;
NSString *signature;
NSArray *mailAccounts, *identities;
NSDictionary *identity;
NSArray *mailIdentities;
NSMutableDictionary *identity;
NSString *fullName, *email, *replyTo, *signature;
mailAccounts = [self arrayForKey: @"MailAccounts"];
if (mailAccounts)
mailIdentities = [self mailIdentities];
if (mailIdentities)
{
rc = YES;
if ([mailAccounts count] > 0)
{
identities = [[mailAccounts objectAtIndex: 0]
objectForKey: @"identifies"];
if ([identities count] > 0)
{
identity = [identities objectAtIndex: 0];
signature = [identity objectForKey: @"signature"];
if ([signature length])
[self setObject: signature forKey: @"MailSignature"];
}
}
[self removeObjectForKey: @"MailAccounts"];
rc = NO;
}
else
rc = NO;
{
identity = [NSMutableDictionary dictionaryWithCapacity: 4];
fullName = [self stringForKey: @"SOGoMailCustomFullName"];
email = [self stringForKey: @"SOGoMailCustomEmail"];
replyTo = [self stringForKey: @"SOGoMailReplyTo"];
signature = [self stringForKey: @"SOGoMailSignature"];
if ([fullName length])
[identity setObject: fullName forKey: @"fullName"];
if ([email length])
[identity setObject: email forKey: @"email"];
if ([replyTo length])
[identity setObject: replyTo forKey: @"replyTo"];
if ([signature length])
[identity setObject: signature forKey: @"signature"];
if ([identity count])
{
[identity setObject: [NSNumber numberWithBool: YES] forKey: @"isDefault"];
[self setMailIdentities: [NSArray arrayWithObject: identity]];
}
/**
* Keep old attributes for now because v2 doesn't handle identities
*
if (fullName)
[self removeObjectForKey: @"SOGoMailCustomFullName"];
if (email)
[self removeObjectForKey: @"SOGoMailCustomEmail"];
if (replyTo)
[self removeObjectForKey: @"SOGoMailReplyTo"];
if (signature)
[self removeObjectForKey: @"SOGoMailSignature"];
*/
rc = YES;
}
return rc;
}
@@ -211,7 +234,8 @@ NSString *SOGoWeekStartFirstFullWeek = @"FirstFullWeek";
/* we must not use a boolean operation, otherwise subsequent migrations will
not be invoked in the case where rc = YES. */
return ([self _migrateLastModule]
| [self _migrateSignature]
// | [self _migrateSignature]
| [self _migrateMailIdentities]
| [self _migrateCalendarCategories]
| [self migrateOldDefaultsWithDictionary: migratedKeys]
| [super migrate]);
@@ -542,6 +566,16 @@ NSString *SOGoWeekStartFirstFullWeek = @"FirstFullWeek";
return [self stringForKey: @"SOGoAnimationMode"];
}
- (BOOL) googleAuthenticatorEnabled
{
return [self boolForKey: @"SOGoGoogleAuthenticatorEnabled"];
}
- (void) setGoogleAuthenticatorEnabled: (BOOL) newValue
{
[self setBool: newValue forKey: @"SOGoGoogleAuthenticatorEnabled"];
}
- (void) setMailComposeWindow: (NSString *) newValue
{
[self setObject: newValue forKey: @"SOGoMailComposeWindow"];
@@ -619,18 +653,6 @@ NSString *SOGoWeekStartFirstFullWeek = @"FirstFullWeek";
return [self stringForKey: @"SOGoMailReplyPlacement"];
}
- (void) setMailSignature: (NSString *) newValue
{
if ([newValue length] == 0)
newValue = nil;
[self setObject: newValue forKey: @"SOGoMailSignature"];
}
- (NSString *) mailSignature
{
return [self stringForKey: @"SOGoMailSignature"];
}
- (void) setMailSignaturePlacement: (NSString *) newValue
{
[self setObject: newValue forKey: @"SOGoMailSignaturePlacement"];
@@ -650,45 +672,6 @@ NSString *SOGoWeekStartFirstFullWeek = @"FirstFullWeek";
return signaturePlacement;
}
- (void) setMailCustomFullName: (NSString *) newValue
{
if ([newValue length] == 0)
newValue = nil;
[self setObject: [newValue stringByTrimmingSpaces]
forKey: @"SOGoMailCustomFullName"];
}
- (NSString *) mailCustomFullName
{
return [self stringForKey: @"SOGoMailCustomFullName"];
}
- (void) setMailCustomEmail: (NSString *) newValue
{
if ([newValue length] == 0)
newValue = nil;
[self setObject: [newValue stringByTrimmingSpaces]
forKey: @"SOGoMailCustomEmail"];
}
- (NSString *) mailCustomEmail
{
return [self stringForKey: @"SOGoMailCustomEmail"];
}
- (void) setMailReplyTo: (NSString *) newValue
{
if ([newValue length] == 0)
newValue = nil;
[self setObject: [newValue stringByTrimmingSpaces]
forKey: @"SOGoMailReplyTo"];
}
- (NSString *) mailReplyTo
{
return [self stringForKey: @"SOGoMailReplyTo"];
}
- (void) setAllowUserReceipt: (BOOL) allow
{
[self setBool: allow forKey: @"SOGoMailReceiptAllow"];
@@ -774,6 +757,16 @@ NSString *SOGoWeekStartFirstFullWeek = @"FirstFullWeek";
return [self boolForKey: @"SOGoMailCertificateAlwaysEncrypt"];
}
- (void) setMailIdentities: (NSArray *) newIdentites
{
[self setObject: newIdentites forKey: @"SOGoMailIdentities"];
}
- (NSArray *) mailIdentities
{
return [self arrayForKey: @"SOGoMailIdentities"];
}
- (void) setAuxiliaryMailAccounts: (NSArray *) newAccounts
{
[self setObject: newAccounts forKey: @"AuxiliaryMailAccounts"];
+10 -10
View File
@@ -674,18 +674,18 @@
return [NSString stringWithFormat: @"urn:uuid:%@", nameInContainer];
}
- (NSException *) setDavSignature: (NSString *) newSignature
{
SOGoUserDefaults *ud;
SOGoUser *user;
// - (NSException *) setDavSignature: (NSString *) newSignature
// {
// SOGoUserDefaults *ud;
// SOGoUser *user;
user = [SOGoUser userWithLogin: [self ownerInContext: nil]];
ud = [user userDefaults];
[ud setMailSignature: newSignature];
[ud synchronize];
// user = [SOGoUser userWithLogin: [self ownerInContext: nil]];
// ud = [user userDefaults];
// [ud setMailSignature: newSignature];
// [ud synchronize];
return nil;
}
// return nil;
// }
#warning unused stub
- (BOOL) collectionDavKey: (NSString *) key
+38
View File
@@ -0,0 +1,38 @@
/* SOGoZipArchiver.h - this file is part of SOGo
*
* Copyright (C) 2020 Inverse inc.
*
* This file is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 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 SOGOZIPARCHIVER_H
#define SOGOZIPARCHIVER_H
#include <zip.h>
@interface SOGoZipArchiver : NSObject
{
zip_t *zip;
}
- (id) initFromFile: (NSString *) file;
+ (id) archiverAtPath: (NSString *) file;
- (BOOL) putFileWithName: (NSString *) filename andData: (NSData *) data;
- (BOOL) close;
@end
#endif /* SOGOZIPARCHIVER_H */
+107
View File
@@ -0,0 +1,107 @@
/* SOGoZipArchiver.m - this file is part of SOGo
*
* Copyright (C) 2020 Inverse inc.
*
* This file is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 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/NSCharacterSet.h>
#import <Foundation/NSData.h>
#import "SOGoZipArchiver.h"
@implementation SOGoZipArchiver
+ (id)archiverAtPath:(NSString *)file
{
id newArchiver = [[self alloc] initFromFile: file];
[newArchiver autorelease];
return newArchiver;
}
- (id)init
{
if ((self = [super init])) {
zip = NULL;
}
return self;
}
- (void)dealloc
{
[self close];
[super dealloc];
}
- (id)initFromFile:(NSString *)file
{
id ret;
ret = nil;
if (file) {
if ((self = [self init])) {
int errorp;
self->zip = zip_open([file cString], ZIP_CREATE | ZIP_EXCL, &errorp);
if (self->zip == NULL) {
zip_error_t ziperror;
zip_error_init_with_code(&ziperror, errorp);
NSLog(@"Failed to open zip output file %@: %@", file,
[NSString stringWithCString: zip_error_strerror(&ziperror)]);
} else {
ret = self;
}
}
}
return ret;
}
- (BOOL)putFileWithName:(NSString *)filename andData:(NSData *)data
{
if (self->zip == NULL) {
NSLog(@"Failed to add file, archive is not open");
return NO;
}
zip_source_t *source = zip_source_buffer(self->zip, [data bytes], [data length], 0);
if (source == NULL) {
NSLog(@"Failed to create zip source from buffer: %@", [NSString stringWithCString: zip_strerror(self->zip)]);
return NO;
}
if (zip_file_add(self->zip, [filename UTF8String], source, ZIP_FL_ENC_UTF_8) < 0) {
NSLog(@"Failed to add file %@: %@", filename, [NSString stringWithCString: zip_strerror(self->zip)]);
zip_source_free(source);
}
return YES;
}
- (BOOL)close
{
BOOL success = YES;
if (self->zip != NULL) {
if (zip_close(zip) != 0) {
NSLog(@"Failed to close zip archive: %@", [NSString stringWithCString: zip_strerror(self->zip)]);
zip_discard(self->zip);
success = NO;
}
self->zip = NULL;
}
return success;
}
@end
+2 -1
View File
@@ -1,6 +1,6 @@
/* SQLSource.h - this file is part of SOGo
*
* Copyright (C) 2009-2019 Inverse inc.
* Copyright (C) 2009-2020 Inverse inc.
*
* This file is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -42,6 +42,7 @@
NSString *_imapHostField;
NSString *_sieveHostField;
NSString *_userPasswordAlgorithm;
NSString *_keyPath;
NSURL *_viewURL;
BOOL _prependPasswordScheme;
+82 -77
View File
@@ -1,6 +1,6 @@
/* SQLSource.h - this file is part of SOGo
*
* Copyright (C) 2009-2017 Inverse inc.
* Copyright (C) 2009-2020 Inverse inc.
*
* This file is part of SOGo.
*
@@ -94,6 +94,7 @@
_searchFields = [NSArray arrayWithObjects: @"c_cn", @"mail", nil];
[_searchFields retain];
_userPasswordAlgorithm = nil;
_keyPath = nil;
_viewURL = nil;
_kindField = nil;
_multipleBookingsField = nil;
@@ -114,6 +115,7 @@
[_mailFields release];
[_searchFields release];
[_userPasswordAlgorithm release];
[_keyPath release];
[_viewURL release];
[_kindField release];
[_multipleBookingsField release];
@@ -137,6 +139,7 @@
ASSIGN(_loginFields, [udSource objectForKey: @"LoginFieldNames"]);
ASSIGN(_mailFields, [udSource objectForKey: @"MailFieldNames"]);
ASSIGN(_userPasswordAlgorithm, [udSource objectForKey: @"userPasswordAlgorithm"]);
ASSIGN(_keyPath, [udSource objectForKey: @"keyPath"]);
ASSIGN(_imapLoginField, [udSource objectForKey: @"IMAPLoginFieldName"]);
ASSIGN(_imapHostField, [udSource objectForKey: @"IMAPHostFieldName"]);
ASSIGN(_sieveHostField, [udSource objectForKey: @"SieveHostFieldName"]);
@@ -191,7 +194,8 @@
return NO;
return [plainPassword isEqualToCrypted: encryptedPassword
withDefaultScheme: _userPasswordAlgorithm];
withDefaultScheme: _userPasswordAlgorithm
keyPath: _keyPath];
}
/**
@@ -205,7 +209,8 @@
NSString *pass;
NSString* result;
pass = [plainPassword asCryptedPassUsingScheme: _userPasswordAlgorithm];
pass = [plainPassword asCryptedPassUsingScheme: _userPasswordAlgorithm
keyPath: _keyPath];
if (pass == nil)
{
@@ -228,10 +233,10 @@
// kind of logic in this module.
//
- (BOOL) checkLogin: (NSString *) _login
password: (NSString *) _pwd
perr: (SOGoPasswordPolicyError *) _perr
expire: (int *) _expire
grace: (int *) _grace
password: (NSString *) _pwd
perr: (SOGoPasswordPolicyError *) _perr
expire: (int *) _expire
grace: (int *) _grace
{
EOAdaptorChannel *channel;
EOQualifier *qualifier;
@@ -259,8 +264,8 @@
{
field = [_loginFields objectAtIndex: i];
loginQualifier = [[EOKeyValueQualifier alloc] initWithKey: field
operatorSelector: EOQualifierOperatorEqual
value: _login];
operatorSelector: EOQualifierOperatorEqual
value: _login];
[loginQualifier autorelease];
[qualifiers addObject: loginQualifier];
}
@@ -299,7 +304,7 @@
value = [row objectForKey: @"c_password"];
rc = [self _isPassword: _pwd equalTo: value];
[channel cancelFetch];
[channel cancelFetch];
}
else
[self errorWithFormat: @"could not run SQL '%@': %@", qualifier, ex];
@@ -322,9 +327,9 @@
* @return YES if the password was successfully changed.
*/
- (BOOL) changePasswordForLogin: (NSString *) login
oldPassword: (NSString *) oldPassword
newPassword: (NSString *) newPassword
perr: (SOGoPasswordPolicyError *) perr
oldPassword: (NSString *) oldPassword
newPassword: (NSString *) newPassword
perr: (SOGoPasswordPolicyError *) perr
{
EOAdaptorChannel *channel;
GCSChannelManager *cm;
@@ -351,23 +356,23 @@
cm = [GCSChannelManager defaultChannelManager];
channel = [cm acquireOpenChannelForURL: _viewURL];
if (channel)
{
sqlstr = [NSString stringWithFormat: (@"UPDATE %@"
@" SET c_password = '%@'"
@" WHERE c_uid = '%@'"),
[_viewURL gcsTableName], encryptedPassword, login];
{
sqlstr = [NSString stringWithFormat: (@"UPDATE %@"
@" SET c_password = '%@'"
@" WHERE c_uid = '%@'"),
[_viewURL gcsTableName], encryptedPassword, login];
ex = [channel evaluateExpressionX: sqlstr];
if (!ex)
{
didChange = YES;
}
else
{
[self errorWithFormat: @"could not run SQL '%@': %@", sqlstr, ex];
}
[cm releaseChannel: channel];
}
ex = [channel evaluateExpressionX: sqlstr];
if (!ex)
{
didChange = YES;
}
else
{
[self errorWithFormat: @"could not run SQL '%@': %@", sqlstr, ex];
}
[cm releaseChannel: channel];
}
}
return didChange;
@@ -489,8 +494,8 @@
[loginQualifier autorelease];
[qualifiers addObject: loginQualifier];
if (_mailFields)
{
if (_mailFields)
{
for (i = 0; i < [_mailFields count]; i++)
{
field = [_mailFields objectAtIndex: i];
@@ -505,7 +510,7 @@
}
}
}
}
}
sql = [NSMutableString stringWithFormat: @"SELECT *"
@" FROM %@"
@@ -519,12 +524,12 @@
ex = [channel evaluateExpressionX: sql];
if (!ex)
{
NSMutableArray *emails;
NSMutableArray *emails;
response = [[channel fetchAttributes: [channel describeResults: NO]
withZone: NULL] mutableCopy];
[response autorelease];
[channel cancelFetch];
[channel cancelFetch];
/* Convert all c_ fields to obtain their ldif equivalent */
fieldNames = [response allKeys];
@@ -540,35 +545,35 @@
[self _fillConstraintsForModule: @"Mail" intoRecord: response];
[self _fillConstraintsForModule: @"ActiveSync" intoRecord: response];
// We set the domain, if any
// We set the domain, if any
value = nil;
if (_domain)
value = _domain;
else if (_domainField)
if (_domain)
value = _domain;
else if (_domainField)
value = [response objectForKey: _domainField];
if (![value isNotNull])
value = @"";
[response setObject: value forKey: @"c_domain"];
value = @"";
[response setObject: value forKey: @"c_domain"];
// We populate all mail fields
emails = [NSMutableArray array];
// We populate all mail fields
emails = [NSMutableArray array];
if ([response objectForKey: @"mail"])
[emails addObject: [response objectForKey: @"mail"]];
if ([response objectForKey: @"mail"])
[emails addObject: [response objectForKey: @"mail"]];
if (_mailFields && [_mailFields count] > 0)
{
NSString *s;
int i;
if (_mailFields && [_mailFields count] > 0)
{
NSString *s;
int i;
for (i = 0; i < [_mailFields count]; i++)
if ((s = [response objectForKey: [_mailFields objectAtIndex: i]]) &&
for (i = 0; i < [_mailFields count]; i++)
if ((s = [response objectForKey: [_mailFields objectAtIndex: i]]) &&
[s isNotNull] &&
[[s stringByTrimmingSpaces] length] > 0)
[emails addObjectsFromArray: [s componentsSeparatedByString: @" "]];
}
[[s stringByTrimmingSpaces] length] > 0)
[emails addObjectsFromArray: [s componentsSeparatedByString: @" "]];
}
[response setObject: [emails uniqueObjects] forKey: @"c_emails"];
[response setObject: [emails uniqueObjects] forKey: @"c_emails"];
if (_imapHostField)
{
value = [response objectForKey: _imapHostField];
@@ -626,29 +631,29 @@
[response setObject: [response objectForKey: _imapLoginField] forKey: @"c_imaplogin"];
}
// We check if it's a resource of not
if (_kindField)
{
if ((value = [response objectForKey: _kindField]) && [value isNotNull])
{
if ([value caseInsensitiveCompare: @"location"] == NSOrderedSame ||
[value caseInsensitiveCompare: @"thing"] == NSOrderedSame ||
[value caseInsensitiveCompare: @"group"] == NSOrderedSame)
{
[response setObject: [NSNumber numberWithInt: 1]
forKey: @"isResource"];
}
}
}
// We check if it's a resource of not
if (_kindField)
{
if ((value = [response objectForKey: _kindField]) && [value isNotNull])
{
if ([value caseInsensitiveCompare: @"location"] == NSOrderedSame ||
[value caseInsensitiveCompare: @"thing"] == NSOrderedSame ||
[value caseInsensitiveCompare: @"group"] == NSOrderedSame)
{
[response setObject: [NSNumber numberWithInt: 1]
forKey: @"isResource"];
}
}
}
if (_multipleBookingsField)
{
if ((value = [response objectForKey: _multipleBookingsField]))
{
[response setObject: [NSNumber numberWithInt: [value intValue]]
forKey: @"numberOfSimultaneousBookings"];
}
}
if (_multipleBookingsField)
{
if ((value = [response objectForKey: _multipleBookingsField]))
{
[response setObject: [NSNumber numberWithInt: [value intValue]]
forKey: @"numberOfSimultaneousBookings"];
}
}
[response setObject: self forKey: @"source"];
}
@@ -742,7 +747,7 @@
if (channel)
{
sql = [NSMutableString stringWithFormat: @"SELECT c_uid FROM %@",
[_viewURL gcsTableName]];
[_viewURL gcsTableName]];
if (_domainField)
{
+659
View File
@@ -0,0 +1,659 @@
/*
This is an implementation of the AES128 algorithm, specifically ECB and CBC mode.
The implementation is verified against the test vectors in:
National Institute of Standards and Technology Special Publication 800-38A 2001 ED
ECB-AES128
----------
plain-text:
6bc1bee22e409f96e93d7e117393172a
ae2d8a571e03ac9c9eb76fac45af8e51
30c81c46a35ce411e5fbc1191a0a52ef
f69f2445df4f9b17ad2b417be66c3710
key:
2b7e151628aed2a6abf7158809cf4f3c
resulting cipher
3ad77bb40d7a3660a89ecaf32466ef97
f5d3d58503b9699de785895a96fdbaaf
43b1cd7f598ece23881b00e3ed030688
7b0c785e27e8ad3f8223207104725dd4
NOTE: String length must be evenly divisible by 16byte (str_len % 16 == 0)
You should pad the end of the string with zeros if this is not the case.
*/
/*****************************************************************************/
/* Includes: */
/*****************************************************************************/
#include <stdint.h>
#include <string.h> // CBC mode, for memset
#include "aes.h"
/*****************************************************************************/
/* Defines: */
/*****************************************************************************/
// The number of columns comprising a state in AES. This is a constant in AES. Value=4
#define Nb 4
// The number of 32 bit words in a key.
#define Nk 4
// Key length in bytes [128 bit]
#define KEYLEN 16
// The number of rounds in AES Cipher.
#define Nr 10
// jcallan@github points out that declaring Multiply as a function
// reduces code size considerably with the Keil ARM compiler.
// See this link for more information: https://github.com/kokke/tiny-AES128-C/pull/3
#ifndef MULTIPLY_AS_A_FUNCTION
#define MULTIPLY_AS_A_FUNCTION 0
#endif
/*****************************************************************************/
/* Private variables: */
/*****************************************************************************/
// state - array holding the intermediate results during decryption.
typedef uint8_t state_t[4][4];
static state_t* state;
// The array that stores the round keys.
static uint8_t RoundKey[176];
// The Key input to the AES Program
static const uint8_t* Key;
#if defined(CBC) && CBC
// Initial Vector used only for CBC mode
static uint8_t* Iv;
#endif
// The lookup-tables are marked const so they can be placed in read-only storage instead of RAM
// The numbers below can be computed dynamically trading ROM for RAM -
// This can be useful in (embedded) bootloader applications, where ROM is often limited.
static const uint8_t sbox[256] = {
//0 1 2 3 4 5 6 7 8 9 A B C D E F
0x63, 0x7c, 0x77, 0x7b, 0xf2, 0x6b, 0x6f, 0xc5, 0x30, 0x01, 0x67, 0x2b, 0xfe, 0xd7, 0xab, 0x76,
0xca, 0x82, 0xc9, 0x7d, 0xfa, 0x59, 0x47, 0xf0, 0xad, 0xd4, 0xa2, 0xaf, 0x9c, 0xa4, 0x72, 0xc0,
0xb7, 0xfd, 0x93, 0x26, 0x36, 0x3f, 0xf7, 0xcc, 0x34, 0xa5, 0xe5, 0xf1, 0x71, 0xd8, 0x31, 0x15,
0x04, 0xc7, 0x23, 0xc3, 0x18, 0x96, 0x05, 0x9a, 0x07, 0x12, 0x80, 0xe2, 0xeb, 0x27, 0xb2, 0x75,
0x09, 0x83, 0x2c, 0x1a, 0x1b, 0x6e, 0x5a, 0xa0, 0x52, 0x3b, 0xd6, 0xb3, 0x29, 0xe3, 0x2f, 0x84,
0x53, 0xd1, 0x00, 0xed, 0x20, 0xfc, 0xb1, 0x5b, 0x6a, 0xcb, 0xbe, 0x39, 0x4a, 0x4c, 0x58, 0xcf,
0xd0, 0xef, 0xaa, 0xfb, 0x43, 0x4d, 0x33, 0x85, 0x45, 0xf9, 0x02, 0x7f, 0x50, 0x3c, 0x9f, 0xa8,
0x51, 0xa3, 0x40, 0x8f, 0x92, 0x9d, 0x38, 0xf5, 0xbc, 0xb6, 0xda, 0x21, 0x10, 0xff, 0xf3, 0xd2,
0xcd, 0x0c, 0x13, 0xec, 0x5f, 0x97, 0x44, 0x17, 0xc4, 0xa7, 0x7e, 0x3d, 0x64, 0x5d, 0x19, 0x73,
0x60, 0x81, 0x4f, 0xdc, 0x22, 0x2a, 0x90, 0x88, 0x46, 0xee, 0xb8, 0x14, 0xde, 0x5e, 0x0b, 0xdb,
0xe0, 0x32, 0x3a, 0x0a, 0x49, 0x06, 0x24, 0x5c, 0xc2, 0xd3, 0xac, 0x62, 0x91, 0x95, 0xe4, 0x79,
0xe7, 0xc8, 0x37, 0x6d, 0x8d, 0xd5, 0x4e, 0xa9, 0x6c, 0x56, 0xf4, 0xea, 0x65, 0x7a, 0xae, 0x08,
0xba, 0x78, 0x25, 0x2e, 0x1c, 0xa6, 0xb4, 0xc6, 0xe8, 0xdd, 0x74, 0x1f, 0x4b, 0xbd, 0x8b, 0x8a,
0x70, 0x3e, 0xb5, 0x66, 0x48, 0x03, 0xf6, 0x0e, 0x61, 0x35, 0x57, 0xb9, 0x86, 0xc1, 0x1d, 0x9e,
0xe1, 0xf8, 0x98, 0x11, 0x69, 0xd9, 0x8e, 0x94, 0x9b, 0x1e, 0x87, 0xe9, 0xce, 0x55, 0x28, 0xdf,
0x8c, 0xa1, 0x89, 0x0d, 0xbf, 0xe6, 0x42, 0x68, 0x41, 0x99, 0x2d, 0x0f, 0xb0, 0x54, 0xbb, 0x16 };
static const uint8_t rsbox[256] =
{ 0x52, 0x09, 0x6a, 0xd5, 0x30, 0x36, 0xa5, 0x38, 0xbf, 0x40, 0xa3, 0x9e, 0x81, 0xf3, 0xd7, 0xfb,
0x7c, 0xe3, 0x39, 0x82, 0x9b, 0x2f, 0xff, 0x87, 0x34, 0x8e, 0x43, 0x44, 0xc4, 0xde, 0xe9, 0xcb,
0x54, 0x7b, 0x94, 0x32, 0xa6, 0xc2, 0x23, 0x3d, 0xee, 0x4c, 0x95, 0x0b, 0x42, 0xfa, 0xc3, 0x4e,
0x08, 0x2e, 0xa1, 0x66, 0x28, 0xd9, 0x24, 0xb2, 0x76, 0x5b, 0xa2, 0x49, 0x6d, 0x8b, 0xd1, 0x25,
0x72, 0xf8, 0xf6, 0x64, 0x86, 0x68, 0x98, 0x16, 0xd4, 0xa4, 0x5c, 0xcc, 0x5d, 0x65, 0xb6, 0x92,
0x6c, 0x70, 0x48, 0x50, 0xfd, 0xed, 0xb9, 0xda, 0x5e, 0x15, 0x46, 0x57, 0xa7, 0x8d, 0x9d, 0x84,
0x90, 0xd8, 0xab, 0x00, 0x8c, 0xbc, 0xd3, 0x0a, 0xf7, 0xe4, 0x58, 0x05, 0xb8, 0xb3, 0x45, 0x06,
0xd0, 0x2c, 0x1e, 0x8f, 0xca, 0x3f, 0x0f, 0x02, 0xc1, 0xaf, 0xbd, 0x03, 0x01, 0x13, 0x8a, 0x6b,
0x3a, 0x91, 0x11, 0x41, 0x4f, 0x67, 0xdc, 0xea, 0x97, 0xf2, 0xcf, 0xce, 0xf0, 0xb4, 0xe6, 0x73,
0x96, 0xac, 0x74, 0x22, 0xe7, 0xad, 0x35, 0x85, 0xe2, 0xf9, 0x37, 0xe8, 0x1c, 0x75, 0xdf, 0x6e,
0x47, 0xf1, 0x1a, 0x71, 0x1d, 0x29, 0xc5, 0x89, 0x6f, 0xb7, 0x62, 0x0e, 0xaa, 0x18, 0xbe, 0x1b,
0xfc, 0x56, 0x3e, 0x4b, 0xc6, 0xd2, 0x79, 0x20, 0x9a, 0xdb, 0xc0, 0xfe, 0x78, 0xcd, 0x5a, 0xf4,
0x1f, 0xdd, 0xa8, 0x33, 0x88, 0x07, 0xc7, 0x31, 0xb1, 0x12, 0x10, 0x59, 0x27, 0x80, 0xec, 0x5f,
0x60, 0x51, 0x7f, 0xa9, 0x19, 0xb5, 0x4a, 0x0d, 0x2d, 0xe5, 0x7a, 0x9f, 0x93, 0xc9, 0x9c, 0xef,
0xa0, 0xe0, 0x3b, 0x4d, 0xae, 0x2a, 0xf5, 0xb0, 0xc8, 0xeb, 0xbb, 0x3c, 0x83, 0x53, 0x99, 0x61,
0x17, 0x2b, 0x04, 0x7e, 0xba, 0x77, 0xd6, 0x26, 0xe1, 0x69, 0x14, 0x63, 0x55, 0x21, 0x0c, 0x7d };
// The round constant word array, Rcon[i], contains the values given by
// x to th e power (i-1) being powers of x (x is denoted as {02}) in the field GF(2^8)
// Note that i starts at 1, not 0).
static const uint8_t Rcon[255] = {
0x8d, 0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80, 0x1b, 0x36, 0x6c, 0xd8, 0xab, 0x4d, 0x9a,
0x2f, 0x5e, 0xbc, 0x63, 0xc6, 0x97, 0x35, 0x6a, 0xd4, 0xb3, 0x7d, 0xfa, 0xef, 0xc5, 0x91, 0x39,
0x72, 0xe4, 0xd3, 0xbd, 0x61, 0xc2, 0x9f, 0x25, 0x4a, 0x94, 0x33, 0x66, 0xcc, 0x83, 0x1d, 0x3a,
0x74, 0xe8, 0xcb, 0x8d, 0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80, 0x1b, 0x36, 0x6c, 0xd8,
0xab, 0x4d, 0x9a, 0x2f, 0x5e, 0xbc, 0x63, 0xc6, 0x97, 0x35, 0x6a, 0xd4, 0xb3, 0x7d, 0xfa, 0xef,
0xc5, 0x91, 0x39, 0x72, 0xe4, 0xd3, 0xbd, 0x61, 0xc2, 0x9f, 0x25, 0x4a, 0x94, 0x33, 0x66, 0xcc,
0x83, 0x1d, 0x3a, 0x74, 0xe8, 0xcb, 0x8d, 0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80, 0x1b,
0x36, 0x6c, 0xd8, 0xab, 0x4d, 0x9a, 0x2f, 0x5e, 0xbc, 0x63, 0xc6, 0x97, 0x35, 0x6a, 0xd4, 0xb3,
0x7d, 0xfa, 0xef, 0xc5, 0x91, 0x39, 0x72, 0xe4, 0xd3, 0xbd, 0x61, 0xc2, 0x9f, 0x25, 0x4a, 0x94,
0x33, 0x66, 0xcc, 0x83, 0x1d, 0x3a, 0x74, 0xe8, 0xcb, 0x8d, 0x01, 0x02, 0x04, 0x08, 0x10, 0x20,
0x40, 0x80, 0x1b, 0x36, 0x6c, 0xd8, 0xab, 0x4d, 0x9a, 0x2f, 0x5e, 0xbc, 0x63, 0xc6, 0x97, 0x35,
0x6a, 0xd4, 0xb3, 0x7d, 0xfa, 0xef, 0xc5, 0x91, 0x39, 0x72, 0xe4, 0xd3, 0xbd, 0x61, 0xc2, 0x9f,
0x25, 0x4a, 0x94, 0x33, 0x66, 0xcc, 0x83, 0x1d, 0x3a, 0x74, 0xe8, 0xcb, 0x8d, 0x01, 0x02, 0x04,
0x08, 0x10, 0x20, 0x40, 0x80, 0x1b, 0x36, 0x6c, 0xd8, 0xab, 0x4d, 0x9a, 0x2f, 0x5e, 0xbc, 0x63,
0xc6, 0x97, 0x35, 0x6a, 0xd4, 0xb3, 0x7d, 0xfa, 0xef, 0xc5, 0x91, 0x39, 0x72, 0xe4, 0xd3, 0xbd,
0x61, 0xc2, 0x9f, 0x25, 0x4a, 0x94, 0x33, 0x66, 0xcc, 0x83, 0x1d, 0x3a, 0x74, 0xe8, 0xcb };
/*****************************************************************************/
/* Private functions: */
/*****************************************************************************/
static uint8_t getSBoxValue(uint8_t num)
{
return sbox[num];
}
static uint8_t getSBoxInvert(uint8_t num)
{
return rsbox[num];
}
// This function produces Nb(Nr+1) round keys. The round keys are used in each round to decrypt the states.
static void KeyExpansion(void)
{
uint32_t i, j, k;
uint8_t tempa[4]; // Used for the column/row operations
// The first round key is the key itself.
for(i = 0; i < Nk; ++i)
{
RoundKey[(i * 4) + 0] = Key[(i * 4) + 0];
RoundKey[(i * 4) + 1] = Key[(i * 4) + 1];
RoundKey[(i * 4) + 2] = Key[(i * 4) + 2];
RoundKey[(i * 4) + 3] = Key[(i * 4) + 3];
}
// All other round keys are found from the previous round keys.
for(; (i < (Nb * (Nr + 1))); ++i)
{
for(j = 0; j < 4; ++j)
{
tempa[j]=RoundKey[(i-1) * 4 + j];
}
if (i % Nk == 0)
{
// This function rotates the 4 bytes in a word to the left once.
// [a0,a1,a2,a3] becomes [a1,a2,a3,a0]
// Function RotWord()
{
k = tempa[0];
tempa[0] = tempa[1];
tempa[1] = tempa[2];
tempa[2] = tempa[3];
tempa[3] = k;
}
// SubWord() is a function that takes a four-byte input word and
// applies the S-box to each of the four bytes to produce an output word.
// Function Subword()
{
tempa[0] = getSBoxValue(tempa[0]);
tempa[1] = getSBoxValue(tempa[1]);
tempa[2] = getSBoxValue(tempa[2]);
tempa[3] = getSBoxValue(tempa[3]);
}
tempa[0] = tempa[0] ^ Rcon[i/Nk];
}
else if (Nk > 6 && i % Nk == 4)
{
// Function Subword()
{
tempa[0] = getSBoxValue(tempa[0]);
tempa[1] = getSBoxValue(tempa[1]);
tempa[2] = getSBoxValue(tempa[2]);
tempa[3] = getSBoxValue(tempa[3]);
}
}
RoundKey[i * 4 + 0] = RoundKey[(i - Nk) * 4 + 0] ^ tempa[0];
RoundKey[i * 4 + 1] = RoundKey[(i - Nk) * 4 + 1] ^ tempa[1];
RoundKey[i * 4 + 2] = RoundKey[(i - Nk) * 4 + 2] ^ tempa[2];
RoundKey[i * 4 + 3] = RoundKey[(i - Nk) * 4 + 3] ^ tempa[3];
}
}
// This function adds the round key to state.
// The round key is added to the state by an XOR function.
static void AddRoundKey(uint8_t round)
{
uint8_t i,j;
for(i=0;i<4;++i)
{
for(j = 0; j < 4; ++j)
{
(*state)[i][j] ^= RoundKey[round * Nb * 4 + i * Nb + j];
}
}
}
// The SubBytes Function Substitutes the values in the
// state matrix with values in an S-box.
static void SubBytes(void)
{
uint8_t i, j;
for(i = 0; i < 4; ++i)
{
for(j = 0; j < 4; ++j)
{
(*state)[j][i] = getSBoxValue((*state)[j][i]);
}
}
}
// The ShiftRows() function shifts the rows in the state to the left.
// Each row is shifted with different offset.
// Offset = Row number. So the first row is not shifted.
static void ShiftRows(void)
{
uint8_t temp;
// Rotate first row 1 columns to left
temp = (*state)[0][1];
(*state)[0][1] = (*state)[1][1];
(*state)[1][1] = (*state)[2][1];
(*state)[2][1] = (*state)[3][1];
(*state)[3][1] = temp;
// Rotate second row 2 columns to left
temp = (*state)[0][2];
(*state)[0][2] = (*state)[2][2];
(*state)[2][2] = temp;
temp = (*state)[1][2];
(*state)[1][2] = (*state)[3][2];
(*state)[3][2] = temp;
// Rotate third row 3 columns to left
temp = (*state)[0][3];
(*state)[0][3] = (*state)[3][3];
(*state)[3][3] = (*state)[2][3];
(*state)[2][3] = (*state)[1][3];
(*state)[1][3] = temp;
}
static uint8_t xtime(uint8_t x)
{
return ((x<<1) ^ (((x>>7) & 1) * 0x1b));
}
// MixColumns function mixes the columns of the state matrix
static void MixColumns(void)
{
uint8_t i;
uint8_t Tmp,Tm,t;
for(i = 0; i < 4; ++i)
{
t = (*state)[i][0];
Tmp = (*state)[i][0] ^ (*state)[i][1] ^ (*state)[i][2] ^ (*state)[i][3] ;
Tm = (*state)[i][0] ^ (*state)[i][1] ; Tm = xtime(Tm); (*state)[i][0] ^= Tm ^ Tmp ;
Tm = (*state)[i][1] ^ (*state)[i][2] ; Tm = xtime(Tm); (*state)[i][1] ^= Tm ^ Tmp ;
Tm = (*state)[i][2] ^ (*state)[i][3] ; Tm = xtime(Tm); (*state)[i][2] ^= Tm ^ Tmp ;
Tm = (*state)[i][3] ^ t ; Tm = xtime(Tm); (*state)[i][3] ^= Tm ^ Tmp ;
}
}
// Multiply is used to multiply numbers in the field GF(2^8)
#if MULTIPLY_AS_A_FUNCTION
static uint8_t Multiply(uint8_t x, uint8_t y)
{
return (((y & 1) * x) ^
((y>>1 & 1) * xtime(x)) ^
((y>>2 & 1) * xtime(xtime(x))) ^
((y>>3 & 1) * xtime(xtime(xtime(x)))) ^
((y>>4 & 1) * xtime(xtime(xtime(xtime(x))))));
}
#else
#define Multiply(x, y) \
( ((y & 1) * x) ^ \
((y>>1 & 1) * xtime(x)) ^ \
((y>>2 & 1) * xtime(xtime(x))) ^ \
((y>>3 & 1) * xtime(xtime(xtime(x)))) ^ \
((y>>4 & 1) * xtime(xtime(xtime(xtime(x)))))) \
#endif
// MixColumns function mixes the columns of the state matrix.
// The method used to multiply may be difficult to understand for the inexperienced.
// Please use the references to gain more information.
static void InvMixColumns(void)
{
int i;
uint8_t a,b,c,d;
for(i=0;i<4;++i)
{
a = (*state)[i][0];
b = (*state)[i][1];
c = (*state)[i][2];
d = (*state)[i][3];
(*state)[i][0] = Multiply(a, 0x0e) ^ Multiply(b, 0x0b) ^ Multiply(c, 0x0d) ^ Multiply(d, 0x09);
(*state)[i][1] = Multiply(a, 0x09) ^ Multiply(b, 0x0e) ^ Multiply(c, 0x0b) ^ Multiply(d, 0x0d);
(*state)[i][2] = Multiply(a, 0x0d) ^ Multiply(b, 0x09) ^ Multiply(c, 0x0e) ^ Multiply(d, 0x0b);
(*state)[i][3] = Multiply(a, 0x0b) ^ Multiply(b, 0x0d) ^ Multiply(c, 0x09) ^ Multiply(d, 0x0e);
}
}
// The SubBytes Function Substitutes the values in the
// state matrix with values in an S-box.
static void InvSubBytes(void)
{
uint8_t i,j;
for(i=0;i<4;++i)
{
for(j=0;j<4;++j)
{
(*state)[j][i] = getSBoxInvert((*state)[j][i]);
}
}
}
static void InvShiftRows(void)
{
uint8_t temp;
// Rotate first row 1 columns to right
temp=(*state)[3][1];
(*state)[3][1]=(*state)[2][1];
(*state)[2][1]=(*state)[1][1];
(*state)[1][1]=(*state)[0][1];
(*state)[0][1]=temp;
// Rotate second row 2 columns to right
temp=(*state)[0][2];
(*state)[0][2]=(*state)[2][2];
(*state)[2][2]=temp;
temp=(*state)[1][2];
(*state)[1][2]=(*state)[3][2];
(*state)[3][2]=temp;
// Rotate third row 3 columns to right
temp=(*state)[0][3];
(*state)[0][3]=(*state)[1][3];
(*state)[1][3]=(*state)[2][3];
(*state)[2][3]=(*state)[3][3];
(*state)[3][3]=temp;
}
// Cipher is the main function that encrypts the PlainText.
static void Cipher(void)
{
uint8_t round = 0;
// Add the First round key to the state before starting the rounds.
AddRoundKey(0);
// There will be Nr rounds.
// The first Nr-1 rounds are identical.
// These Nr-1 rounds are executed in the loop below.
for(round = 1; round < Nr; ++round)
{
SubBytes();
ShiftRows();
MixColumns();
AddRoundKey(round);
}
// The last round is given below.
// The MixColumns function is not here in the last round.
SubBytes();
ShiftRows();
AddRoundKey(Nr);
}
static void InvCipher(void)
{
uint8_t round=0;
// Add the First round key to the state before starting the rounds.
AddRoundKey(Nr);
// There will be Nr rounds.
// The first Nr-1 rounds are identical.
// These Nr-1 rounds are executed in the loop below.
for(round=Nr-1;round>0;round--)
{
InvShiftRows();
InvSubBytes();
AddRoundKey(round);
InvMixColumns();
}
// The last round is given below.
// The MixColumns function is not here in the last round.
InvShiftRows();
InvSubBytes();
AddRoundKey(0);
}
static void BlockCopy(uint8_t* output, uint8_t* input)
{
uint8_t i;
for (i=0;i<KEYLEN;++i)
{
output[i] = input[i];
}
}
/*****************************************************************************/
/* Public functions: */
/*****************************************************************************/
#if defined(ECB) && ECB
void AES128_ECB_encrypt(uint8_t* input, const uint8_t* key, uint8_t* output)
{
// Copy input to output, and work in-memory on output
BlockCopy(output, input);
state = (state_t*)output;
Key = key;
KeyExpansion();
// The next function call encrypts the PlainText with the Key using AES algorithm.
Cipher();
}
void AES128_ECB_decrypt(uint8_t* input, const uint8_t* key, uint8_t *output)
{
// Copy input to output, and work in-memory on output
BlockCopy(output, input);
state = (state_t*)output;
// The KeyExpansion routine must be called before encryption.
Key = key;
KeyExpansion();
InvCipher();
}
#endif // #if defined(ECB) && ECB
#if defined(CBC) && CBC
static void XorWithIv(uint8_t* buf)
{
uint8_t i;
for(i = 0; i < KEYLEN; ++i)
{
buf[i] ^= Iv[i];
}
}
void AES128_CBC_encrypt_buffer(uint8_t* output, uint8_t* input, uint32_t length, const uint8_t* key, const uint8_t* iv)
{
size_t i;
uint8_t remainders = length % KEYLEN; /* Remaining bytes in the last non-full block */
BlockCopy(output, input);
state = (state_t*)output;
// Skip the key expansion if key is passed as 0
if(0 != key)
{
Key = key;
KeyExpansion();
}
if(iv != 0)
{
Iv = (uint8_t*)iv;
}
for(i = 0; i < length; i += KEYLEN)
{
XorWithIv(input);
BlockCopy(output, input);
state = (state_t*)output;
Cipher();
Iv = output;
input += KEYLEN;
output += KEYLEN;
}
if(remainders)
{
BlockCopy(output, input);
memset(output + remainders, 0, KEYLEN - remainders); /* add 0-padding */
state = (state_t*)output;
Cipher();
}
}
void AES128_CBC_decrypt_buffer(uint8_t* output, uint8_t* input, uint32_t length, const uint8_t* key, const uint8_t* iv)
{
size_t i;
uint8_t remainders = length % KEYLEN; /* Remaining bytes in the last non-full block */
BlockCopy(output, input);
state = (state_t*)output;
// Skip the key expansion if key is passed as 0
if(0 != key)
{
Key = key;
KeyExpansion();
}
// If iv is passed as 0, we continue to encrypt without re-setting the Iv
if(iv != 0)
{
Iv = (uint8_t*)iv;
}
for(i = 0; i < length; i += KEYLEN)
{
BlockCopy(output, input);
state = (state_t*)output;
InvCipher();
XorWithIv(output);
Iv = input;
input += KEYLEN;
output += KEYLEN;
}
if(remainders)
{
BlockCopy(output, input);
memset(output+remainders, 0, KEYLEN - remainders); /* add 0-padding */
state = (state_t*)output;
InvCipher();
}
}
uint8_t AES128_CBC_encrypt_inplace( uint8_t* data, size_t length, const uint8_t* key, const uint8_t* iv){
size_t i;
state = NULL;
/* Check for valid length. Must be > 0 and a multiple of KEYLEN */
if( length % KEYLEN != 0 || length == 0){
return 1;
}
// Skip the key expansion if key is passed as 0
if(0 != key)
{
Key = key;
KeyExpansion();
}
if(iv != 0)
{
Iv = (uint8_t*)iv;
}
for(i = 0; i < length; i += KEYLEN)
{
XorWithIv(data);
state = (state_t*)data;
Cipher();
Iv = data;
data += KEYLEN;
}
return 0;
}
/* We must have a writable iv pointer in this case, as we need the storage for holding the next decryption IV */
uint8_t AES128_CBC_decrypt_inplace( uint8_t* data, size_t length, const uint8_t* key, uint8_t* iv){
size_t i;
state = NULL;
uint8_t next_iv[KEYLEN];
/* Check for valid length. Must be > 0 and a multiple of KEYLEN */
if( length % KEYLEN != 0 || length == 0){
return 1;
}
if( 0 == iv )
{
return 2;
}
Iv = (uint8_t*)iv;
// Skip the key expansion if key is passed as 0
if(0 != key)
{
Key = key;
KeyExpansion();
}
BlockCopy(next_iv,data);
for(i = 0; i < length; i += KEYLEN)
{
state = (state_t*)data;
InvCipher();
XorWithIv(data);
data += KEYLEN;
/* use the last buffered IV */
BlockCopy(iv,next_iv);
/* and buffer the next */
BlockCopy(next_iv,data);
}
return 0;
}
#endif // #if defined(CBC) && CBC
+49
View File
@@ -0,0 +1,49 @@
#ifndef _AES_H_
#define _AES_H_
#include <stdint.h>
// #define the macros below to 1/0 to enable/disable the mode of operation.
//
// CBC enables AES128 encryption in CBC-mode of operation and handles 0-padding.
// ECB enables the basic ECB 16-byte block algorithm. Both can be enabled simultaneously.
// The #ifndef-guard allows it to be configured before #include'ing or at compile time.
#ifndef CBC
#define CBC 1
#endif
#ifndef ECB
#define ECB 1
#endif
#if defined(ECB) && ECB
void AES128_ECB_encrypt(uint8_t* input, const uint8_t* key, uint8_t *output);
void AES128_ECB_decrypt(uint8_t* input, const uint8_t* key, uint8_t *output);
#endif // #if defined(ECB) && ECB
#if defined(CBC) && CBC
void AES128_CBC_encrypt_buffer(uint8_t* output, uint8_t* input, uint32_t length, const uint8_t* key, const uint8_t* iv);
void AES128_CBC_decrypt_buffer(uint8_t* output, uint8_t* input, uint32_t length, const uint8_t* key, const uint8_t* iv);
/* These variants encrypt and decrypt the data block in-place.
* The data block length MUST be a multiple of the algorithm block size (16 bytes)
* The return value will be non-zero if the length is incorrect.
* For the decypt function, the iv data must be writable, and will be modified on return.
*/
uint8_t AES128_CBC_encrypt_inplace( uint8_t* data, size_t length, const uint8_t* key, const uint8_t* iv);
uint8_t AES128_CBC_decrypt_inplace( uint8_t* data, size_t length, const uint8_t* key, uint8_t* iv);
#endif // #if defined(CBC) && CBC
#endif //_AES_H_
+907
View File
@@ -0,0 +1,907 @@
/*
* The crypt_blowfish homepage is:
*
* http://www.openwall.com/crypt/
*
* This code comes from John the Ripper password cracker, with reentrant
* and crypt(3) interfaces added, but optimizations specific to password
* cracking removed.
*
* Written by Solar Designer <solar at openwall.com> in 1998-2014.
* No copyright is claimed, and the software is hereby placed in the public
* domain. In case this attempt to disclaim copyright and place the software
* in the public domain is deemed null and void, then the software is
* Copyright (c) 1998-2014 Solar Designer and it is hereby released to the
* general public under the following terms:
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted.
*
* There's ABSOLUTELY NO WARRANTY, express or implied.
*
* It is my intent that you should be able to use this on your system,
* as part of a software package, or anywhere else to improve security,
* ensure compatibility, or for any other purpose. I would appreciate
* it if you give credit where it is due and keep your modifications in
* the public domain as well, but I don't require that in order to let
* you place this code and any modifications you make under a license
* of your choice.
*
* This implementation is fully compatible with OpenBSD's bcrypt.c for prefix
* "$2b$", originally by Niels Provos <provos at citi.umich.edu>, and it uses
* some of his ideas. The password hashing algorithm was designed by David
* Mazieres <dm at lcs.mit.edu>. For information on the level of
* compatibility for bcrypt hash prefixes other than "$2b$", please refer to
* the comments in BF_set_key() below and to the included crypt(3) man page.
*
* There's a paper on the algorithm that explains its design decisions:
*
* http://www.usenix.org/events/usenix99/provos.html
*
* Some of the tricks in BF_ROUND might be inspired by Eric Young's
* Blowfish library (I can't be sure if I would think of something if I
* hadn't seen his code).
*/
#include <string.h>
#include <errno.h>
#ifndef __set_errno
#define __set_errno(val) errno = (val)
#endif
/* Just to make sure the prototypes match the actual definitions */
#include "crypt_blowfish.h"
#ifdef __i386__
#define BF_ASM 0
#define BF_SCALE 1
#elif defined(__x86_64__) || defined(__alpha__) || defined(__hppa__)
#define BF_ASM 0
#define BF_SCALE 1
#else
#define BF_ASM 0
#define BF_SCALE 0
#endif
typedef unsigned int BF_word;
typedef signed int BF_word_signed;
/* Number of Blowfish rounds, this is also hardcoded into a few places */
#define BF_N 16
typedef BF_word BF_key[BF_N + 2];
typedef struct {
BF_word S[4][0x100];
BF_key P;
} BF_ctx;
/*
* Magic IV for 64 Blowfish encryptions that we do at the end.
* The string is "OrpheanBeholderScryDoubt" on big-endian.
*/
static BF_word BF_magic_w[6] = {
0x4F727068, 0x65616E42, 0x65686F6C,
0x64657253, 0x63727944, 0x6F756274
};
/*
* P-box and S-box tables initialized with digits of Pi.
*/
static BF_ctx BF_init_state = {
{
{
0xd1310ba6, 0x98dfb5ac, 0x2ffd72db, 0xd01adfb7,
0xb8e1afed, 0x6a267e96, 0xba7c9045, 0xf12c7f99,
0x24a19947, 0xb3916cf7, 0x0801f2e2, 0x858efc16,
0x636920d8, 0x71574e69, 0xa458fea3, 0xf4933d7e,
0x0d95748f, 0x728eb658, 0x718bcd58, 0x82154aee,
0x7b54a41d, 0xc25a59b5, 0x9c30d539, 0x2af26013,
0xc5d1b023, 0x286085f0, 0xca417918, 0xb8db38ef,
0x8e79dcb0, 0x603a180e, 0x6c9e0e8b, 0xb01e8a3e,
0xd71577c1, 0xbd314b27, 0x78af2fda, 0x55605c60,
0xe65525f3, 0xaa55ab94, 0x57489862, 0x63e81440,
0x55ca396a, 0x2aab10b6, 0xb4cc5c34, 0x1141e8ce,
0xa15486af, 0x7c72e993, 0xb3ee1411, 0x636fbc2a,
0x2ba9c55d, 0x741831f6, 0xce5c3e16, 0x9b87931e,
0xafd6ba33, 0x6c24cf5c, 0x7a325381, 0x28958677,
0x3b8f4898, 0x6b4bb9af, 0xc4bfe81b, 0x66282193,
0x61d809cc, 0xfb21a991, 0x487cac60, 0x5dec8032,
0xef845d5d, 0xe98575b1, 0xdc262302, 0xeb651b88,
0x23893e81, 0xd396acc5, 0x0f6d6ff3, 0x83f44239,
0x2e0b4482, 0xa4842004, 0x69c8f04a, 0x9e1f9b5e,
0x21c66842, 0xf6e96c9a, 0x670c9c61, 0xabd388f0,
0x6a51a0d2, 0xd8542f68, 0x960fa728, 0xab5133a3,
0x6eef0b6c, 0x137a3be4, 0xba3bf050, 0x7efb2a98,
0xa1f1651d, 0x39af0176, 0x66ca593e, 0x82430e88,
0x8cee8619, 0x456f9fb4, 0x7d84a5c3, 0x3b8b5ebe,
0xe06f75d8, 0x85c12073, 0x401a449f, 0x56c16aa6,
0x4ed3aa62, 0x363f7706, 0x1bfedf72, 0x429b023d,
0x37d0d724, 0xd00a1248, 0xdb0fead3, 0x49f1c09b,
0x075372c9, 0x80991b7b, 0x25d479d8, 0xf6e8def7,
0xe3fe501a, 0xb6794c3b, 0x976ce0bd, 0x04c006ba,
0xc1a94fb6, 0x409f60c4, 0x5e5c9ec2, 0x196a2463,
0x68fb6faf, 0x3e6c53b5, 0x1339b2eb, 0x3b52ec6f,
0x6dfc511f, 0x9b30952c, 0xcc814544, 0xaf5ebd09,
0xbee3d004, 0xde334afd, 0x660f2807, 0x192e4bb3,
0xc0cba857, 0x45c8740f, 0xd20b5f39, 0xb9d3fbdb,
0x5579c0bd, 0x1a60320a, 0xd6a100c6, 0x402c7279,
0x679f25fe, 0xfb1fa3cc, 0x8ea5e9f8, 0xdb3222f8,
0x3c7516df, 0xfd616b15, 0x2f501ec8, 0xad0552ab,
0x323db5fa, 0xfd238760, 0x53317b48, 0x3e00df82,
0x9e5c57bb, 0xca6f8ca0, 0x1a87562e, 0xdf1769db,
0xd542a8f6, 0x287effc3, 0xac6732c6, 0x8c4f5573,
0x695b27b0, 0xbbca58c8, 0xe1ffa35d, 0xb8f011a0,
0x10fa3d98, 0xfd2183b8, 0x4afcb56c, 0x2dd1d35b,
0x9a53e479, 0xb6f84565, 0xd28e49bc, 0x4bfb9790,
0xe1ddf2da, 0xa4cb7e33, 0x62fb1341, 0xcee4c6e8,
0xef20cada, 0x36774c01, 0xd07e9efe, 0x2bf11fb4,
0x95dbda4d, 0xae909198, 0xeaad8e71, 0x6b93d5a0,
0xd08ed1d0, 0xafc725e0, 0x8e3c5b2f, 0x8e7594b7,
0x8ff6e2fb, 0xf2122b64, 0x8888b812, 0x900df01c,
0x4fad5ea0, 0x688fc31c, 0xd1cff191, 0xb3a8c1ad,
0x2f2f2218, 0xbe0e1777, 0xea752dfe, 0x8b021fa1,
0xe5a0cc0f, 0xb56f74e8, 0x18acf3d6, 0xce89e299,
0xb4a84fe0, 0xfd13e0b7, 0x7cc43b81, 0xd2ada8d9,
0x165fa266, 0x80957705, 0x93cc7314, 0x211a1477,
0xe6ad2065, 0x77b5fa86, 0xc75442f5, 0xfb9d35cf,
0xebcdaf0c, 0x7b3e89a0, 0xd6411bd3, 0xae1e7e49,
0x00250e2d, 0x2071b35e, 0x226800bb, 0x57b8e0af,
0x2464369b, 0xf009b91e, 0x5563911d, 0x59dfa6aa,
0x78c14389, 0xd95a537f, 0x207d5ba2, 0x02e5b9c5,
0x83260376, 0x6295cfa9, 0x11c81968, 0x4e734a41,
0xb3472dca, 0x7b14a94a, 0x1b510052, 0x9a532915,
0xd60f573f, 0xbc9bc6e4, 0x2b60a476, 0x81e67400,
0x08ba6fb5, 0x571be91f, 0xf296ec6b, 0x2a0dd915,
0xb6636521, 0xe7b9f9b6, 0xff34052e, 0xc5855664,
0x53b02d5d, 0xa99f8fa1, 0x08ba4799, 0x6e85076a
}, {
0x4b7a70e9, 0xb5b32944, 0xdb75092e, 0xc4192623,
0xad6ea6b0, 0x49a7df7d, 0x9cee60b8, 0x8fedb266,
0xecaa8c71, 0x699a17ff, 0x5664526c, 0xc2b19ee1,
0x193602a5, 0x75094c29, 0xa0591340, 0xe4183a3e,
0x3f54989a, 0x5b429d65, 0x6b8fe4d6, 0x99f73fd6,
0xa1d29c07, 0xefe830f5, 0x4d2d38e6, 0xf0255dc1,
0x4cdd2086, 0x8470eb26, 0x6382e9c6, 0x021ecc5e,
0x09686b3f, 0x3ebaefc9, 0x3c971814, 0x6b6a70a1,
0x687f3584, 0x52a0e286, 0xb79c5305, 0xaa500737,
0x3e07841c, 0x7fdeae5c, 0x8e7d44ec, 0x5716f2b8,
0xb03ada37, 0xf0500c0d, 0xf01c1f04, 0x0200b3ff,
0xae0cf51a, 0x3cb574b2, 0x25837a58, 0xdc0921bd,
0xd19113f9, 0x7ca92ff6, 0x94324773, 0x22f54701,
0x3ae5e581, 0x37c2dadc, 0xc8b57634, 0x9af3dda7,
0xa9446146, 0x0fd0030e, 0xecc8c73e, 0xa4751e41,
0xe238cd99, 0x3bea0e2f, 0x3280bba1, 0x183eb331,
0x4e548b38, 0x4f6db908, 0x6f420d03, 0xf60a04bf,
0x2cb81290, 0x24977c79, 0x5679b072, 0xbcaf89af,
0xde9a771f, 0xd9930810, 0xb38bae12, 0xdccf3f2e,
0x5512721f, 0x2e6b7124, 0x501adde6, 0x9f84cd87,
0x7a584718, 0x7408da17, 0xbc9f9abc, 0xe94b7d8c,
0xec7aec3a, 0xdb851dfa, 0x63094366, 0xc464c3d2,
0xef1c1847, 0x3215d908, 0xdd433b37, 0x24c2ba16,
0x12a14d43, 0x2a65c451, 0x50940002, 0x133ae4dd,
0x71dff89e, 0x10314e55, 0x81ac77d6, 0x5f11199b,
0x043556f1, 0xd7a3c76b, 0x3c11183b, 0x5924a509,
0xf28fe6ed, 0x97f1fbfa, 0x9ebabf2c, 0x1e153c6e,
0x86e34570, 0xeae96fb1, 0x860e5e0a, 0x5a3e2ab3,
0x771fe71c, 0x4e3d06fa, 0x2965dcb9, 0x99e71d0f,
0x803e89d6, 0x5266c825, 0x2e4cc978, 0x9c10b36a,
0xc6150eba, 0x94e2ea78, 0xa5fc3c53, 0x1e0a2df4,
0xf2f74ea7, 0x361d2b3d, 0x1939260f, 0x19c27960,
0x5223a708, 0xf71312b6, 0xebadfe6e, 0xeac31f66,
0xe3bc4595, 0xa67bc883, 0xb17f37d1, 0x018cff28,
0xc332ddef, 0xbe6c5aa5, 0x65582185, 0x68ab9802,
0xeecea50f, 0xdb2f953b, 0x2aef7dad, 0x5b6e2f84,
0x1521b628, 0x29076170, 0xecdd4775, 0x619f1510,
0x13cca830, 0xeb61bd96, 0x0334fe1e, 0xaa0363cf,
0xb5735c90, 0x4c70a239, 0xd59e9e0b, 0xcbaade14,
0xeecc86bc, 0x60622ca7, 0x9cab5cab, 0xb2f3846e,
0x648b1eaf, 0x19bdf0ca, 0xa02369b9, 0x655abb50,
0x40685a32, 0x3c2ab4b3, 0x319ee9d5, 0xc021b8f7,
0x9b540b19, 0x875fa099, 0x95f7997e, 0x623d7da8,
0xf837889a, 0x97e32d77, 0x11ed935f, 0x16681281,
0x0e358829, 0xc7e61fd6, 0x96dedfa1, 0x7858ba99,
0x57f584a5, 0x1b227263, 0x9b83c3ff, 0x1ac24696,
0xcdb30aeb, 0x532e3054, 0x8fd948e4, 0x6dbc3128,
0x58ebf2ef, 0x34c6ffea, 0xfe28ed61, 0xee7c3c73,
0x5d4a14d9, 0xe864b7e3, 0x42105d14, 0x203e13e0,
0x45eee2b6, 0xa3aaabea, 0xdb6c4f15, 0xfacb4fd0,
0xc742f442, 0xef6abbb5, 0x654f3b1d, 0x41cd2105,
0xd81e799e, 0x86854dc7, 0xe44b476a, 0x3d816250,
0xcf62a1f2, 0x5b8d2646, 0xfc8883a0, 0xc1c7b6a3,
0x7f1524c3, 0x69cb7492, 0x47848a0b, 0x5692b285,
0x095bbf00, 0xad19489d, 0x1462b174, 0x23820e00,
0x58428d2a, 0x0c55f5ea, 0x1dadf43e, 0x233f7061,
0x3372f092, 0x8d937e41, 0xd65fecf1, 0x6c223bdb,
0x7cde3759, 0xcbee7460, 0x4085f2a7, 0xce77326e,
0xa6078084, 0x19f8509e, 0xe8efd855, 0x61d99735,
0xa969a7aa, 0xc50c06c2, 0x5a04abfc, 0x800bcadc,
0x9e447a2e, 0xc3453484, 0xfdd56705, 0x0e1e9ec9,
0xdb73dbd3, 0x105588cd, 0x675fda79, 0xe3674340,
0xc5c43465, 0x713e38d8, 0x3d28f89e, 0xf16dff20,
0x153e21e7, 0x8fb03d4a, 0xe6e39f2b, 0xdb83adf7
}, {
0xe93d5a68, 0x948140f7, 0xf64c261c, 0x94692934,
0x411520f7, 0x7602d4f7, 0xbcf46b2e, 0xd4a20068,
0xd4082471, 0x3320f46a, 0x43b7d4b7, 0x500061af,
0x1e39f62e, 0x97244546, 0x14214f74, 0xbf8b8840,
0x4d95fc1d, 0x96b591af, 0x70f4ddd3, 0x66a02f45,
0xbfbc09ec, 0x03bd9785, 0x7fac6dd0, 0x31cb8504,
0x96eb27b3, 0x55fd3941, 0xda2547e6, 0xabca0a9a,
0x28507825, 0x530429f4, 0x0a2c86da, 0xe9b66dfb,
0x68dc1462, 0xd7486900, 0x680ec0a4, 0x27a18dee,
0x4f3ffea2, 0xe887ad8c, 0xb58ce006, 0x7af4d6b6,
0xaace1e7c, 0xd3375fec, 0xce78a399, 0x406b2a42,
0x20fe9e35, 0xd9f385b9, 0xee39d7ab, 0x3b124e8b,
0x1dc9faf7, 0x4b6d1856, 0x26a36631, 0xeae397b2,
0x3a6efa74, 0xdd5b4332, 0x6841e7f7, 0xca7820fb,
0xfb0af54e, 0xd8feb397, 0x454056ac, 0xba489527,
0x55533a3a, 0x20838d87, 0xfe6ba9b7, 0xd096954b,
0x55a867bc, 0xa1159a58, 0xcca92963, 0x99e1db33,
0xa62a4a56, 0x3f3125f9, 0x5ef47e1c, 0x9029317c,
0xfdf8e802, 0x04272f70, 0x80bb155c, 0x05282ce3,
0x95c11548, 0xe4c66d22, 0x48c1133f, 0xc70f86dc,
0x07f9c9ee, 0x41041f0f, 0x404779a4, 0x5d886e17,
0x325f51eb, 0xd59bc0d1, 0xf2bcc18f, 0x41113564,
0x257b7834, 0x602a9c60, 0xdff8e8a3, 0x1f636c1b,
0x0e12b4c2, 0x02e1329e, 0xaf664fd1, 0xcad18115,
0x6b2395e0, 0x333e92e1, 0x3b240b62, 0xeebeb922,
0x85b2a20e, 0xe6ba0d99, 0xde720c8c, 0x2da2f728,
0xd0127845, 0x95b794fd, 0x647d0862, 0xe7ccf5f0,
0x5449a36f, 0x877d48fa, 0xc39dfd27, 0xf33e8d1e,
0x0a476341, 0x992eff74, 0x3a6f6eab, 0xf4f8fd37,
0xa812dc60, 0xa1ebddf8, 0x991be14c, 0xdb6e6b0d,
0xc67b5510, 0x6d672c37, 0x2765d43b, 0xdcd0e804,
0xf1290dc7, 0xcc00ffa3, 0xb5390f92, 0x690fed0b,
0x667b9ffb, 0xcedb7d9c, 0xa091cf0b, 0xd9155ea3,
0xbb132f88, 0x515bad24, 0x7b9479bf, 0x763bd6eb,
0x37392eb3, 0xcc115979, 0x8026e297, 0xf42e312d,
0x6842ada7, 0xc66a2b3b, 0x12754ccc, 0x782ef11c,
0x6a124237, 0xb79251e7, 0x06a1bbe6, 0x4bfb6350,
0x1a6b1018, 0x11caedfa, 0x3d25bdd8, 0xe2e1c3c9,
0x44421659, 0x0a121386, 0xd90cec6e, 0xd5abea2a,
0x64af674e, 0xda86a85f, 0xbebfe988, 0x64e4c3fe,
0x9dbc8057, 0xf0f7c086, 0x60787bf8, 0x6003604d,
0xd1fd8346, 0xf6381fb0, 0x7745ae04, 0xd736fccc,
0x83426b33, 0xf01eab71, 0xb0804187, 0x3c005e5f,
0x77a057be, 0xbde8ae24, 0x55464299, 0xbf582e61,
0x4e58f48f, 0xf2ddfda2, 0xf474ef38, 0x8789bdc2,
0x5366f9c3, 0xc8b38e74, 0xb475f255, 0x46fcd9b9,
0x7aeb2661, 0x8b1ddf84, 0x846a0e79, 0x915f95e2,
0x466e598e, 0x20b45770, 0x8cd55591, 0xc902de4c,
0xb90bace1, 0xbb8205d0, 0x11a86248, 0x7574a99e,
0xb77f19b6, 0xe0a9dc09, 0x662d09a1, 0xc4324633,
0xe85a1f02, 0x09f0be8c, 0x4a99a025, 0x1d6efe10,
0x1ab93d1d, 0x0ba5a4df, 0xa186f20f, 0x2868f169,
0xdcb7da83, 0x573906fe, 0xa1e2ce9b, 0x4fcd7f52,
0x50115e01, 0xa70683fa, 0xa002b5c4, 0x0de6d027,
0x9af88c27, 0x773f8641, 0xc3604c06, 0x61a806b5,
0xf0177a28, 0xc0f586e0, 0x006058aa, 0x30dc7d62,
0x11e69ed7, 0x2338ea63, 0x53c2dd94, 0xc2c21634,
0xbbcbee56, 0x90bcb6de, 0xebfc7da1, 0xce591d76,
0x6f05e409, 0x4b7c0188, 0x39720a3d, 0x7c927c24,
0x86e3725f, 0x724d9db9, 0x1ac15bb4, 0xd39eb8fc,
0xed545578, 0x08fca5b5, 0xd83d7cd3, 0x4dad0fc4,
0x1e50ef5e, 0xb161e6f8, 0xa28514d9, 0x6c51133c,
0x6fd5c7e7, 0x56e14ec4, 0x362abfce, 0xddc6c837,
0xd79a3234, 0x92638212, 0x670efa8e, 0x406000e0
}, {
0x3a39ce37, 0xd3faf5cf, 0xabc27737, 0x5ac52d1b,
0x5cb0679e, 0x4fa33742, 0xd3822740, 0x99bc9bbe,
0xd5118e9d, 0xbf0f7315, 0xd62d1c7e, 0xc700c47b,
0xb78c1b6b, 0x21a19045, 0xb26eb1be, 0x6a366eb4,
0x5748ab2f, 0xbc946e79, 0xc6a376d2, 0x6549c2c8,
0x530ff8ee, 0x468dde7d, 0xd5730a1d, 0x4cd04dc6,
0x2939bbdb, 0xa9ba4650, 0xac9526e8, 0xbe5ee304,
0xa1fad5f0, 0x6a2d519a, 0x63ef8ce2, 0x9a86ee22,
0xc089c2b8, 0x43242ef6, 0xa51e03aa, 0x9cf2d0a4,
0x83c061ba, 0x9be96a4d, 0x8fe51550, 0xba645bd6,
0x2826a2f9, 0xa73a3ae1, 0x4ba99586, 0xef5562e9,
0xc72fefd3, 0xf752f7da, 0x3f046f69, 0x77fa0a59,
0x80e4a915, 0x87b08601, 0x9b09e6ad, 0x3b3ee593,
0xe990fd5a, 0x9e34d797, 0x2cf0b7d9, 0x022b8b51,
0x96d5ac3a, 0x017da67d, 0xd1cf3ed6, 0x7c7d2d28,
0x1f9f25cf, 0xadf2b89b, 0x5ad6b472, 0x5a88f54c,
0xe029ac71, 0xe019a5e6, 0x47b0acfd, 0xed93fa9b,
0xe8d3c48d, 0x283b57cc, 0xf8d56629, 0x79132e28,
0x785f0191, 0xed756055, 0xf7960e44, 0xe3d35e8c,
0x15056dd4, 0x88f46dba, 0x03a16125, 0x0564f0bd,
0xc3eb9e15, 0x3c9057a2, 0x97271aec, 0xa93a072a,
0x1b3f6d9b, 0x1e6321f5, 0xf59c66fb, 0x26dcf319,
0x7533d928, 0xb155fdf5, 0x03563482, 0x8aba3cbb,
0x28517711, 0xc20ad9f8, 0xabcc5167, 0xccad925f,
0x4de81751, 0x3830dc8e, 0x379d5862, 0x9320f991,
0xea7a90c2, 0xfb3e7bce, 0x5121ce64, 0x774fbe32,
0xa8b6e37e, 0xc3293d46, 0x48de5369, 0x6413e680,
0xa2ae0810, 0xdd6db224, 0x69852dfd, 0x09072166,
0xb39a460a, 0x6445c0dd, 0x586cdecf, 0x1c20c8ae,
0x5bbef7dd, 0x1b588d40, 0xccd2017f, 0x6bb4e3bb,
0xdda26a7e, 0x3a59ff45, 0x3e350a44, 0xbcb4cdd5,
0x72eacea8, 0xfa6484bb, 0x8d6612ae, 0xbf3c6f47,
0xd29be463, 0x542f5d9e, 0xaec2771b, 0xf64e6370,
0x740e0d8d, 0xe75b1357, 0xf8721671, 0xaf537d5d,
0x4040cb08, 0x4eb4e2cc, 0x34d2466a, 0x0115af84,
0xe1b00428, 0x95983a1d, 0x06b89fb4, 0xce6ea048,
0x6f3f3b82, 0x3520ab82, 0x011a1d4b, 0x277227f8,
0x611560b1, 0xe7933fdc, 0xbb3a792b, 0x344525bd,
0xa08839e1, 0x51ce794b, 0x2f32c9b7, 0xa01fbac9,
0xe01cc87e, 0xbcc7d1f6, 0xcf0111c3, 0xa1e8aac7,
0x1a908749, 0xd44fbd9a, 0xd0dadecb, 0xd50ada38,
0x0339c32a, 0xc6913667, 0x8df9317c, 0xe0b12b4f,
0xf79e59b7, 0x43f5bb3a, 0xf2d519ff, 0x27d9459c,
0xbf97222c, 0x15e6fc2a, 0x0f91fc71, 0x9b941525,
0xfae59361, 0xceb69ceb, 0xc2a86459, 0x12baa8d1,
0xb6c1075e, 0xe3056a0c, 0x10d25065, 0xcb03a442,
0xe0ec6e0e, 0x1698db3b, 0x4c98a0be, 0x3278e964,
0x9f1f9532, 0xe0d392df, 0xd3a0342b, 0x8971f21e,
0x1b0a7441, 0x4ba3348c, 0xc5be7120, 0xc37632d8,
0xdf359f8d, 0x9b992f2e, 0xe60b6f47, 0x0fe3f11d,
0xe54cda54, 0x1edad891, 0xce6279cf, 0xcd3e7e6f,
0x1618b166, 0xfd2c1d05, 0x848fd2c5, 0xf6fb2299,
0xf523f357, 0xa6327623, 0x93a83531, 0x56cccd02,
0xacf08162, 0x5a75ebb5, 0x6e163697, 0x88d273cc,
0xde966292, 0x81b949d0, 0x4c50901b, 0x71c65614,
0xe6c6c7bd, 0x327a140a, 0x45e1d006, 0xc3f27b9a,
0xc9aa53fd, 0x62a80f00, 0xbb25bfe2, 0x35bdd2f6,
0x71126905, 0xb2040222, 0xb6cbcf7c, 0xcd769c2b,
0x53113ec0, 0x1640e3d3, 0x38abbd60, 0x2547adf0,
0xba38209c, 0xf746ce76, 0x77afa1c5, 0x20756060,
0x85cbfe4e, 0x8ae88dd8, 0x7aaaf9b0, 0x4cf9aa7e,
0x1948c25c, 0x02fb8a8c, 0x01c36ae4, 0xd6ebe1f9,
0x90d4f869, 0xa65cdea0, 0x3f09252d, 0xc208e69f,
0xb74e6132, 0xce77e25b, 0x578fdfe3, 0x3ac372e6
}
}, {
0x243f6a88, 0x85a308d3, 0x13198a2e, 0x03707344,
0xa4093822, 0x299f31d0, 0x082efa98, 0xec4e6c89,
0x452821e6, 0x38d01377, 0xbe5466cf, 0x34e90c6c,
0xc0ac29b7, 0xc97c50dd, 0x3f84d5b5, 0xb5470917,
0x9216d5d9, 0x8979fb1b
}
};
static unsigned char BF_itoa64[64 + 1] =
"./ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
static unsigned char BF_atoi64[0x60] = {
64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 0, 1,
54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 64, 64, 64, 64, 64,
64, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16,
17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 64, 64, 64, 64, 64,
64, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42,
43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 64, 64, 64, 64, 64
};
#define BF_safe_atoi64(dst, src) \
{ \
tmp = (unsigned char)(src); \
if ((unsigned int)(tmp -= 0x20) >= 0x60) return -1; \
tmp = BF_atoi64[tmp]; \
if (tmp > 63) return -1; \
(dst) = tmp; \
}
static int BF_decode(BF_word *dst, const char *src, int size)
{
unsigned char *dptr = (unsigned char *)dst;
unsigned char *end = dptr + size;
const unsigned char *sptr = (const unsigned char *)src;
unsigned int tmp, c1, c2, c3, c4;
do {
BF_safe_atoi64(c1, *sptr++);
BF_safe_atoi64(c2, *sptr++);
*dptr++ = (c1 << 2) | ((c2 & 0x30) >> 4);
if (dptr >= end) break;
BF_safe_atoi64(c3, *sptr++);
*dptr++ = ((c2 & 0x0F) << 4) | ((c3 & 0x3C) >> 2);
if (dptr >= end) break;
BF_safe_atoi64(c4, *sptr++);
*dptr++ = ((c3 & 0x03) << 6) | c4;
} while (dptr < end);
return 0;
}
static void BF_encode(char *dst, const BF_word *src, int size)
{
const unsigned char *sptr = (const unsigned char *)src;
const unsigned char *end = sptr + size;
unsigned char *dptr = (unsigned char *)dst;
unsigned int c1, c2;
do {
c1 = *sptr++;
*dptr++ = BF_itoa64[c1 >> 2];
c1 = (c1 & 0x03) << 4;
if (sptr >= end) {
*dptr++ = BF_itoa64[c1];
break;
}
c2 = *sptr++;
c1 |= c2 >> 4;
*dptr++ = BF_itoa64[c1];
c1 = (c2 & 0x0f) << 2;
if (sptr >= end) {
*dptr++ = BF_itoa64[c1];
break;
}
c2 = *sptr++;
c1 |= c2 >> 6;
*dptr++ = BF_itoa64[c1];
*dptr++ = BF_itoa64[c2 & 0x3f];
} while (sptr < end);
}
static void BF_swap(BF_word *x, int count)
{
static int endianness_check = 1;
char *is_little_endian = (char *)&endianness_check;
BF_word tmp;
if (*is_little_endian)
do {
tmp = *x;
tmp = (tmp << 16) | (tmp >> 16);
*x++ = ((tmp & 0x00FF00FF) << 8) | ((tmp >> 8) & 0x00FF00FF);
} while (--count);
}
#if BF_SCALE
/* Architectures which can shift addresses left by 2 bits with no extra cost */
#define BF_ROUND(L, R, N) \
tmp1 = L & 0xFF; \
tmp2 = L >> 8; \
tmp2 &= 0xFF; \
tmp3 = L >> 16; \
tmp3 &= 0xFF; \
tmp4 = L >> 24; \
tmp1 = data.ctx.S[3][tmp1]; \
tmp2 = data.ctx.S[2][tmp2]; \
tmp3 = data.ctx.S[1][tmp3]; \
tmp3 += data.ctx.S[0][tmp4]; \
tmp3 ^= tmp2; \
R ^= data.ctx.P[N + 1]; \
tmp3 += tmp1; \
R ^= tmp3;
#else
/* Architectures with no complicated addressing modes supported */
#define BF_INDEX(S, i) \
(*((BF_word *)(((unsigned char *)S) + (i))))
#define BF_ROUND(L, R, N) \
tmp1 = L & 0xFF; \
tmp1 <<= 2; \
tmp2 = L >> 6; \
tmp2 &= 0x3FC; \
tmp3 = L >> 14; \
tmp3 &= 0x3FC; \
tmp4 = L >> 22; \
tmp4 &= 0x3FC; \
tmp1 = BF_INDEX(data.ctx.S[3], tmp1); \
tmp2 = BF_INDEX(data.ctx.S[2], tmp2); \
tmp3 = BF_INDEX(data.ctx.S[1], tmp3); \
tmp3 += BF_INDEX(data.ctx.S[0], tmp4); \
tmp3 ^= tmp2; \
R ^= data.ctx.P[N + 1]; \
tmp3 += tmp1; \
R ^= tmp3;
#endif
/*
* Encrypt one block, BF_N is hardcoded here.
*/
#define BF_ENCRYPT \
L ^= data.ctx.P[0]; \
BF_ROUND(L, R, 0); \
BF_ROUND(R, L, 1); \
BF_ROUND(L, R, 2); \
BF_ROUND(R, L, 3); \
BF_ROUND(L, R, 4); \
BF_ROUND(R, L, 5); \
BF_ROUND(L, R, 6); \
BF_ROUND(R, L, 7); \
BF_ROUND(L, R, 8); \
BF_ROUND(R, L, 9); \
BF_ROUND(L, R, 10); \
BF_ROUND(R, L, 11); \
BF_ROUND(L, R, 12); \
BF_ROUND(R, L, 13); \
BF_ROUND(L, R, 14); \
BF_ROUND(R, L, 15); \
tmp4 = R; \
R = L; \
L = tmp4 ^ data.ctx.P[BF_N + 1];
#if BF_ASM
#define BF_body() \
_BF_body_r(&data.ctx);
#else
#define BF_body() \
L = R = 0; \
ptr = data.ctx.P; \
do { \
ptr += 2; \
BF_ENCRYPT; \
*(ptr - 2) = L; \
*(ptr - 1) = R; \
} while (ptr < &data.ctx.P[BF_N + 2]); \
\
ptr = data.ctx.S[0]; \
do { \
ptr += 2; \
BF_ENCRYPT; \
*(ptr - 2) = L; \
*(ptr - 1) = R; \
} while (ptr < &data.ctx.S[3][0xFF]);
#endif
static void BF_set_key(const char *key, BF_key expanded, BF_key initial,
unsigned char flags)
{
const char *ptr = key;
unsigned int bug, i, j;
BF_word safety, sign, diff, tmp[2];
/*
* There was a sign extension bug in older revisions of this function. While
* we would have liked to simply fix the bug and move on, we have to provide
* a backwards compatibility feature (essentially the bug) for some systems and
* a safety measure for some others. The latter is needed because for certain
* multiple inputs to the buggy algorithm there exist easily found inputs to
* the correct algorithm that produce the same hash. Thus, we optionally
* deviate from the correct algorithm just enough to avoid such collisions.
* While the bug itself affected the majority of passwords containing
* characters with the 8th bit set (although only a percentage of those in a
* collision-producing way), the anti-collision safety measure affects
* only a subset of passwords containing the '\xff' character (not even all of
* those passwords, just some of them). This character is not found in valid
* UTF-8 sequences and is rarely used in popular 8-bit character encodings.
* Thus, the safety measure is unlikely to cause much annoyance, and is a
* reasonable tradeoff to use when authenticating against existing hashes that
* are not reliably known to have been computed with the correct algorithm.
*
* We use an approach that tries to minimize side-channel leaks of password
* information - that is, we mostly use fixed-cost bitwise operations instead
* of branches or table lookups. (One conditional branch based on password
* length remains. It is not part of the bug aftermath, though, and is
* difficult and possibly unreasonable to avoid given the use of C strings by
* the caller, which results in similar timing leaks anyway.)
*
* For actual implementation, we set an array index in the variable "bug"
* (0 means no bug, 1 means sign extension bug emulation) and a flag in the
* variable "safety" (bit 16 is set when the safety measure is requested).
* Valid combinations of settings are:
*
* Prefix "$2a$": bug = 0, safety = 0x10000
* Prefix "$2b$": bug = 0, safety = 0
* Prefix "$2x$": bug = 1, safety = 0
* Prefix "$2y$": bug = 0, safety = 0
*/
bug = (unsigned int)flags & 1;
safety = ((BF_word)flags & 2) << 15;
sign = diff = 0;
for (i = 0; i < BF_N + 2; i++) {
tmp[0] = tmp[1] = 0;
for (j = 0; j < 4; j++) {
tmp[0] <<= 8;
tmp[0] |= (unsigned char)*ptr; /* correct */
tmp[1] <<= 8;
tmp[1] |= (BF_word_signed)(signed char)*ptr; /* bug */
/*
* Sign extension in the first char has no effect - nothing to overwrite yet,
* and those extra 24 bits will be fully shifted out of the 32-bit word. For
* chars 2, 3, 4 in each four-char block, we set bit 7 of "sign" if sign
* extension in tmp[1] occurs. Once this flag is set, it remains set.
*/
if (j)
sign |= tmp[1] & 0x80;
if (!*ptr)
ptr = key;
else
ptr++;
}
diff |= tmp[0] ^ tmp[1]; /* Non-zero on any differences */
expanded[i] = tmp[bug];
initial[i] = BF_init_state.P[i] ^ tmp[bug];
}
/*
* At this point, "diff" is zero iff the correct and buggy algorithms produced
* exactly the same result. If so and if "sign" is non-zero, which indicates
* that there was a non-benign sign extension, this means that we have a
* collision between the correctly computed hash for this password and a set of
* passwords that could be supplied to the buggy algorithm. Our safety measure
* is meant to protect from such many-buggy to one-correct collisions, by
* deviating from the correct algorithm in such cases. Let's check for this.
*/
diff |= diff >> 16; /* still zero iff exact match */
diff &= 0xffff; /* ditto */
diff += 0xffff; /* bit 16 set iff "diff" was non-zero (on non-match) */
sign <<= 9; /* move the non-benign sign extension flag to bit 16 */
sign &= ~diff & safety; /* action needed? */
/*
* If we have determined that we need to deviate from the correct algorithm,
* flip bit 16 in initial expanded key. (The choice of 16 is arbitrary, but
* let's stick to it now. It came out of the approach we used above, and it's
* not any worse than any other choice we could make.)
*
* It is crucial that we don't do the same to the expanded key used in the main
* Eksblowfish loop. By doing it to only one of these two, we deviate from a
* state that could be directly specified by a password to the buggy algorithm
* (and to the fully correct one as well, but that's a side-effect).
*/
initial[0] ^= sign;
}
static const unsigned char flags_by_subtype[26] =
{2, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 4, 0};
static char *BF_crypt(const char *key, const char *setting,
char *output, int size,
BF_word min)
{
#if BF_ASM
extern void _BF_body_r(BF_ctx *ctx);
#endif
struct {
BF_ctx ctx;
BF_key expanded_key;
union {
BF_word salt[4];
BF_word output[6];
} binary;
} data;
BF_word L, R;
BF_word tmp1, tmp2, tmp3, tmp4;
BF_word *ptr;
BF_word count;
int i;
if (size < 7 + 22 + 31 + 1) {
__set_errno(ERANGE);
return NULL;
}
if (setting[0] != '$' ||
setting[1] != '2' ||
setting[2] < 'a' || setting[2] > 'z' ||
!flags_by_subtype[(unsigned int)(unsigned char)setting[2] - 'a'] ||
setting[3] != '$' ||
setting[4] < '0' || setting[4] > '3' ||
setting[5] < '0' || setting[5] > '9' ||
(setting[4] == '3' && setting[5] > '1') ||
setting[6] != '$') {
__set_errno(EINVAL);
return NULL;
}
count = (BF_word)1 << ((setting[4] - '0') * 10 + (setting[5] - '0'));
if (count < min || BF_decode(data.binary.salt, &setting[7], 16)) {
__set_errno(EINVAL);
return NULL;
}
BF_swap(data.binary.salt, 4);
BF_set_key(key, data.expanded_key, data.ctx.P,
flags_by_subtype[(unsigned int)(unsigned char)setting[2] - 'a']);
memcpy(data.ctx.S, BF_init_state.S, sizeof(data.ctx.S));
L = R = 0;
for (i = 0; i < BF_N + 2; i += 2) {
L ^= data.binary.salt[i & 2];
R ^= data.binary.salt[(i & 2) + 1];
BF_ENCRYPT;
data.ctx.P[i] = L;
data.ctx.P[i + 1] = R;
}
ptr = data.ctx.S[0];
do {
ptr += 4;
L ^= data.binary.salt[(BF_N + 2) & 3];
R ^= data.binary.salt[(BF_N + 3) & 3];
BF_ENCRYPT;
*(ptr - 4) = L;
*(ptr - 3) = R;
L ^= data.binary.salt[(BF_N + 4) & 3];
R ^= data.binary.salt[(BF_N + 5) & 3];
BF_ENCRYPT;
*(ptr - 2) = L;
*(ptr - 1) = R;
} while (ptr < &data.ctx.S[3][0xFF]);
do {
int done;
for (i = 0; i < BF_N + 2; i += 2) {
data.ctx.P[i] ^= data.expanded_key[i];
data.ctx.P[i + 1] ^= data.expanded_key[i + 1];
}
done = 0;
do {
BF_body();
if (done)
break;
done = 1;
tmp1 = data.binary.salt[0];
tmp2 = data.binary.salt[1];
tmp3 = data.binary.salt[2];
tmp4 = data.binary.salt[3];
for (i = 0; i < BF_N; i += 4) {
data.ctx.P[i] ^= tmp1;
data.ctx.P[i + 1] ^= tmp2;
data.ctx.P[i + 2] ^= tmp3;
data.ctx.P[i + 3] ^= tmp4;
}
data.ctx.P[16] ^= tmp1;
data.ctx.P[17] ^= tmp2;
} while (1);
} while (--count);
for (i = 0; i < 6; i += 2) {
L = BF_magic_w[i];
R = BF_magic_w[i + 1];
count = 64;
do {
BF_ENCRYPT;
} while (--count);
data.binary.output[i] = L;
data.binary.output[i + 1] = R;
}
memcpy(output, setting, 7 + 22 - 1);
output[7 + 22 - 1] = BF_itoa64[(int)
BF_atoi64[(int)setting[7 + 22 - 1] - 0x20] & 0x30];
/* This has to be bug-compatible with the original implementation, so
* only encode 23 of the 24 bytes. :-) */
BF_swap(data.binary.output, 6);
BF_encode(&output[7 + 22], data.binary.output, 23);
output[7 + 22 + 31] = '\0';
return output;
}
int _crypt_output_magic(const char *setting, char *output, int size)
{
if (size < 3)
return -1;
output[0] = '*';
output[1] = '0';
output[2] = '\0';
if (setting[0] == '*' && setting[1] == '0')
output[1] = '1';
return 0;
}
/*
* Please preserve the runtime self-test. It serves two purposes at once:
*
* 1. We really can't afford the risk of producing incompatible hashes e.g.
* when there's something like gcc bug 26587 again, whereas an application or
* library integrating this code might not also integrate our external tests or
* it might not run them after every build. Even if it does, the miscompile
* might only occur on the production build, but not on a testing build (such
* as because of different optimization settings). It is painful to recover
* from incorrectly-computed hashes - merely fixing whatever broke is not
* enough. Thus, a proactive measure like this self-test is needed.
*
* 2. We don't want to leave sensitive data from our actual password hash
* computation on the stack or in registers. Previous revisions of the code
* would do explicit cleanups, but simply running the self-test after hash
* computation is more reliable.
*
* The performance cost of this quick self-test is around 0.6% at the "$2a$08"
* setting.
*/
char *_crypt_blowfish_rn(const char *key, const char *setting,
char *output, int size)
{
const char *test_key = "8b \xd0\xc1\xd2\xcf\xcc\xd8";
const char *test_setting = "$2a$00$abcdefghijklmnopqrstuu";
static const char * const test_hashes[2] =
{"i1D709vfamulimlGcq0qq3UvuUasvEa\0\x55", /* 'a', 'b', 'y' */
"VUrPmXD6q/nVSSp7pNDhCR9071IfIRe\0\x55"}; /* 'x' */
const char *test_hash = test_hashes[0];
char *retval;
const char *p;
int save_errno, ok;
struct {
char s[7 + 22 + 1];
char o[7 + 22 + 31 + 1 + 1 + 1];
} buf;
/* Hash the supplied password */
_crypt_output_magic(setting, output, size);
retval = BF_crypt(key, setting, output, size, 16);
save_errno = errno;
/*
* Do a quick self-test. It is important that we make both calls to BF_crypt()
* from the same scope such that they likely use the same stack locations,
* which makes the second call overwrite the first call's sensitive data on the
* stack and makes it more likely that any alignment related issues would be
* detected by the self-test.
*/
memcpy(buf.s, test_setting, sizeof(buf.s));
if (retval) {
unsigned int flags = flags_by_subtype[
(unsigned int)(unsigned char)setting[2] - 'a'];
test_hash = test_hashes[flags & 1];
buf.s[2] = setting[2];
}
memset(buf.o, 0x55, sizeof(buf.o));
buf.o[sizeof(buf.o) - 1] = 0;
p = BF_crypt(test_key, buf.s, buf.o, sizeof(buf.o) - (1 + 1), 1);
ok = (p == buf.o &&
!memcmp(p, buf.s, 7 + 22) &&
!memcmp(p + (7 + 22), test_hash, 31 + 1 + 1 + 1));
{
const char *k = "\xff\xa3" "34" "\xff\xff\xff\xa3" "345";
BF_key ae, ai, ye, yi;
BF_set_key(k, ae, ai, 2); /* $2a$ */
BF_set_key(k, ye, yi, 4); /* $2y$ */
ai[0] ^= 0x10000; /* undo the safety (for comparison) */
ok = ok && ai[0] == 0xdb9c59bc && ye[17] == 0x33343500 &&
!memcmp(ae, ye, sizeof(ae)) &&
!memcmp(ai, yi, sizeof(ai));
}
__set_errno(save_errno);
if (ok)
return retval;
/* Should not happen */
_crypt_output_magic(setting, output, size);
__set_errno(EINVAL); /* pretend we don't support this hash type */
return NULL;
}
char *_crypt_gensalt_blowfish_rn(const char *prefix, unsigned long count,
const char *input, int size, char *output, int output_size)
{
if (size < 16 || output_size < 7 + 22 + 1 ||
(count && (count < 4 || count > 31)) ||
prefix[0] != '$' || prefix[1] != '2' ||
(prefix[2] != 'a' && prefix[2] != 'b' && prefix[2] != 'y')) {
if (output_size > 0) output[0] = '\0';
__set_errno((output_size < 7 + 22 + 1) ? ERANGE : EINVAL);
return NULL;
}
if (!count) count = 5;
output[0] = '$';
output[1] = '2';
output[2] = prefix[2];
output[3] = '$';
output[4] = '0' + count / 10;
output[5] = '0' + count % 10;
output[6] = '$';
BF_encode(&output[7], (const BF_word *)input, 16);
output[7 + 22] = '\0';
return output;
}
+27
View File
@@ -0,0 +1,27 @@
/*
* Written by Solar Designer <solar at openwall.com> in 2000-2011.
* No copyright is claimed, and the software is hereby placed in the public
* domain. In case this attempt to disclaim copyright and place the software
* in the public domain is deemed null and void, then the software is
* Copyright (c) 2000-2011 Solar Designer and it is hereby released to the
* general public under the following terms:
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted.
*
* There's ABSOLUTELY NO WARRANTY, express or implied.
*
* See crypt_blowfish.c for more information.
*/
#ifndef _CRYPT_BLOWFISH_H
#define _CRYPT_BLOWFISH_H
extern int _crypt_output_magic(const char *setting, char *output, int size);
extern char *_crypt_blowfish_rn(const char *key, const char *setting,
char *output, int size);
extern char *_crypt_gensalt_blowfish_rn(const char *prefix,
unsigned long count,
const char *input, int size, char *output, int output_size);
#endif
+1 -1
View File
@@ -106,7 +106,7 @@ def FindHFile(args, filename):
found = full_filename
if found is None:
raise Exception, "'%s' not found in include dirs" % filename
raise Exception("'%s' not found in include dirs" % filename)
return found
+141
View File
@@ -0,0 +1,141 @@
/* $OpenBSD: pkcs5_pbkdf2.c,v 1.11 2019/11/21 16:07:24 tedu Exp $ */
/*-
* Copyright (c) 2008 Damien Bergamini <damien.bergamini@free.fr>
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
#include <sys/types.h>
#include <string.h>
#include <stdint.h>
#include <stdlib.h>
#ifdef HAVE_GNUTLS
#include <gnutls/gnutls.h>
#include <gnutls/crypto.h>
#define SHA_CTX gnutls_hash_hd_t
#define SHA1_Init(c) gnutls_hash_init(c, GNUTLS_DIG_SHA1)
#define SHA1_Update(c,b,l) gnutls_hash(*c, b, l)
#define SHA1_Final(b,c) gnutls_hash_deinit(*c, b);
#elif defined(HAVE_OPENSSL)
#include <openssl/sha.h>
#endif
#define MINIMUM(a,b) (((a) < (b)) ? (a) : (b))
#define bcopy(b1,b2,len) (memmove((b2), (b1), (len)), (void) 0)
#define SHA1_BLOCK_LENGTH 64
#define SHA1_DIGEST_LENGTH 20
#define SHA1_DIGEST_STRING_LENGTH (SHA1_DIGEST_LENGTH * 2 + 1)
/*
* HMAC-SHA-1 (from RFC 2202).
*/
static void
hmac_sha1(const u_int8_t *text, size_t text_len, const u_int8_t *key,
size_t key_len, u_int8_t digest[SHA1_DIGEST_LENGTH])
{
SHA_CTX ctx;
u_int8_t k_pad[SHA1_BLOCK_LENGTH];
u_int8_t tk[SHA1_DIGEST_LENGTH];
int i;
if (key_len > SHA1_BLOCK_LENGTH) {
SHA1_Init(&ctx);
SHA1_Update(&ctx, key, key_len);
SHA1_Final(tk, &ctx);
key = tk;
key_len = SHA1_DIGEST_LENGTH;
}
memset(k_pad, 0, sizeof k_pad);
bcopy(key, k_pad, key_len);
for (i = 0; i < SHA1_BLOCK_LENGTH; i++)
k_pad[i] ^= 0x36;
SHA1_Init(&ctx);
SHA1_Update(&ctx, k_pad, SHA1_BLOCK_LENGTH);
SHA1_Update(&ctx, text, text_len);
SHA1_Final(digest, &ctx);
memset(k_pad, 0, sizeof k_pad);
bcopy(key, k_pad, key_len);
for (i = 0; i < SHA1_BLOCK_LENGTH; i++)
k_pad[i] ^= 0x5c;
SHA1_Init(&ctx);
SHA1_Update(&ctx, k_pad, SHA1_BLOCK_LENGTH);
SHA1_Update(&ctx, digest, SHA1_DIGEST_LENGTH);
SHA1_Final(digest, &ctx);
}
/*
* Password-Based Key Derivation Function 2 (PKCS #5 v2.0).
* Code based on IEEE Std 802.11-2007, Annex H.4.2.
*/
int
pkcs5_pbkdf2(const char *pass, size_t pass_len, const uint8_t *salt,
size_t salt_len, uint8_t *key, size_t key_len, unsigned int rounds)
{
uint8_t *asalt, obuf[SHA1_DIGEST_LENGTH];
uint8_t d1[SHA1_DIGEST_LENGTH], d2[SHA1_DIGEST_LENGTH];
unsigned int i, j;
unsigned int count;
size_t r;
if (rounds < 1 || key_len == 0)
goto bad;
if (salt_len == 0 || salt_len > SIZE_MAX - 4)
goto bad;
if ((asalt = malloc(salt_len + 4)) == NULL)
goto bad;
memcpy(asalt, salt, salt_len);
for (count = 1; key_len > 0; count++) {
asalt[salt_len + 0] = (count >> 24) & 0xff;
asalt[salt_len + 1] = (count >> 16) & 0xff;
asalt[salt_len + 2] = (count >> 8) & 0xff;
asalt[salt_len + 3] = count & 0xff;
hmac_sha1(asalt, salt_len + 4, (const u_int8_t *)pass, pass_len, d1);
memcpy(obuf, d1, sizeof(obuf));
for (i = 1; i < rounds; i++) {
hmac_sha1(d1, sizeof(d1), (const u_int8_t *)pass, pass_len, d2);
memcpy(d1, d2, sizeof(d1));
for (j = 0; j < sizeof(obuf); j++)
obuf[j] ^= d1[j];
}
r = MINIMUM(key_len, SHA1_DIGEST_LENGTH);
memcpy(key, obuf, r);
key += r;
key_len -= r;
};
bzero(asalt, salt_len + 4);
free(asalt);
bzero(d1, sizeof(d1));
bzero(d2, sizeof(d2));
bzero(obuf, sizeof(obuf));
return 0;
bad:
/* overwrite with random in case caller doesn't check return code */
//arc4random_buf(key, key_len);
return -1;
}
+15
View File
@@ -0,0 +1,15 @@
#ifndef PKCS5_PBKDF2_H
#define PKCS5_PBKDF2_H
#include <stddef.h>
#include <stdint.h>
#define PBKDF2_KEY_SIZE_SHA1 (20)
#define PBKDF2_SALT_LEN (16)
#define PBKDF2_DEFAULT_ROUNDS (5000)
int
pkcs5_pbkdf2(const char *pass, size_t pass_len, const uint8_t *salt,
size_t salt_len, uint8_t *key, size_t key_len, unsigned int rounds);
#endif /* ! PKCS5_PBKDF2_H */
+53 -1
View File
@@ -1,6 +1,7 @@
/* TestNSString+MD5SHA1.m - this file is part of SOGo
/* TestNSString+Crypto.m - this file is part of SOGo
*
* Copyright (C) 2011, 2012 Jeroen Dekkers
* Copyright (C) 2020 Nicolas Höft
*
* Author: Jeroen Dekkers <jeroen@dekkers.ch>
*
@@ -60,4 +61,55 @@
}
}
- (void) test_blowfish
{
NSString *error;
// well-known comparison
NSString *blf_key = @"123456";
NSString *blf_hash = @"{BLF-CRYPT}$2a$05$tLVuFQTgdwrZmixu.QMxoedUAUEeIFIBv89Ur5mQ6F1vBL8Vw1mXO";
error = [NSString stringWithFormat:
@"string '%@' wrong BLF-CRYPT: '%@'",
blf_key, blf_hash];
testWithMessage([blf_key isEqualToCrypted:blf_hash withDefaultScheme: @"CRYPT" keyPath: nil], error);
// generate a new blowfish-crypt key
NSString *blf_prefix = @"$2y$05$";
NSString *blf_result = [blf_key asCryptedPassUsingScheme: @"blf-crypt" keyPath: nil];
error = [NSString stringWithFormat:
@"returned hash '%@' has incorrect BLF-CRYPT prefix: '%@'",
blf_result, blf_prefix];
testWithMessage([blf_result hasPrefix: blf_prefix], error);
test([blf_key isEqualToCrypted:blf_result withDefaultScheme: @"BLF-CRYPT" keyPath: nil]);
}
- (void) test_pbkdf2
{
NSString *error;
// well-known comparison
NSString *pbkdf2_key = @"123456";
NSString *pbkdf2_hash = @"{PBKDF2}$1$xbhnwhLxltdS9L5M$5001$f1699047a6132383490817d6e58a5284f13339f0";
NSString *pkbf2_prefix;
NSString *pkbf2_result;
error = [NSString stringWithFormat:
@"string '%@' wrong PBKDF2: '%@'",
pbkdf2_key, pbkdf2_hash];
testWithMessage([pbkdf2_key isEqualToCrypted:pbkdf2_hash withDefaultScheme: @"CRYPT" keyPath: nil], error);
// generate a new pbkdf2-crypt key
pkbf2_prefix = @"$1$";
pkbf2_result = [pbkdf2_key asCryptedPassUsingScheme: @"PBKDF2" keyPath: nil];
error = [NSString stringWithFormat:
@"returned hash '%@' has incorrect PBKDF2 prefix: '%@'",
pkbf2_result, pkbf2_prefix];
testWithMessage([pkbf2_result hasPrefix: pkbf2_prefix], error);
test([pbkdf2_key isEqualToCrypted:pkbf2_result withDefaultScheme: @"PBKDF2" keyPath: nil]);
}
@end
+45
View File
@@ -19,6 +19,7 @@
*/
#import <Foundation/NSAutoreleasePool.h>
#import <Foundation/NSCalendarDate.h>
#import <Foundation/NSDictionary.h>
#import <Foundation/NSEnumerator.h>
#import <Foundation/NSString.h>
@@ -36,6 +37,8 @@
#import <SOGo/SOGoSystemDefaults.h>
#import <NGCards/iCalCalendar.h>
#import <NGCards/iCalDateTime.h>
#import <NGCards/iCalEvent.h>
#import <NGCards/NGVCard.h>
#import "SOGoTool.h"
@@ -248,6 +251,48 @@
[gcsFolder deleteContentWithName: c_name];
rc = NO;
}
else
{
iCalEvent *event;
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;
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
+29 -3
View File
@@ -32,6 +32,7 @@
#import <GDLContentStore/NSURL+GCS.h>
#import <SOGo/SOGoCache.h>
#import <SOGo/SOGoSource.h>
#import <SOGo/SOGoUserManager.h>
#import <SOGo/NSArray+Utilities.h>
#import <SOGo/NSString+Utilities.h>
@@ -116,7 +117,7 @@ typedef enum
" folder the folder - Calendar/<ID> or Contacts/<ID>\n"
" user the user 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\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");
}
@@ -215,7 +216,30 @@ typedef enum
[allSQLUsers addObject: @"anonymous"];
}
else
[allSQLUsers addObject: user];
{
NSDictionary *dict;
dict = [[SOGoUserManager sharedUserManager] contactInfosForUserWithUIDorEmail: user];
if (dict && [[dict objectForKey: @"isGroup"] boolValue])
{
id <SOGoSource> source;
NSArray *members;
int i;
source = [[SOGoUserManager sharedUserManager] sourceWithID: [dict objectForKey: @"SOGoSource"]];
if ([source conformsToProtocol: @protocol(SOGoMembershipSource)])
{
members = [(id<SOGoMembershipSource>)(source) membersForGroupWithUID: [dict objectForKey: @"c_uid"]];
for (i = 0; i < [members count]; i++)
{
[allSQLUsers addObject: [[members objectAtIndex: i] objectForKey: @"c_uid"]];
}
}
}
else
[allSQLUsers addObject: user];
}
pool = [[NSAutoreleasePool alloc] init];
max = [allSQLUsers count];
@@ -413,7 +437,9 @@ typedef enum
fm = [GCSFolderManager defaultFolderManager];
f = [fm folderAtPath: [NSString stringWithFormat: @"/Users/%@/%@", owner, folder]];
if (!f)
// Skip the folder existence check so we can auto-create the personal folder
// for users that have never logged-in
if (!f && command != ManageACLSubscribe)
{
NSLog(@"No folder %@ found for user %@", folder, owner);
rc = NO;
+9 -2
View File
@@ -1,6 +1,6 @@
/* SOGoToolRestore.m - this file is part of SOGo
*
* Copyright (C) 2009-2015 Inverse inc.
* Copyright (C) 2009-2019 Inverse inc.
*
* This file is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -614,6 +614,7 @@
- (BOOL) _updateSieveScripsForLogin: (NSString *) theLogin
{
/* credentials file handling */
NSException *error;
NSString *credsFilename, *authname=nil, *authpwd=nil;
SOGoCredentialsFile *cf;
SOGoUser *user;
@@ -650,7 +651,13 @@
account = [folder lookupName: @"0" inContext: localContext acquire: NO];
[account setContext: localContext];
return [account updateFiltersWithUsername: authname andPassword: authpwd];
error = [account updateFiltersWithUsername: authname
andPassword: authpwd
forceActivation: NO];
if (error)
return NO;
return YES;
}
- (BOOL) restoreUserPreferencesFromUserRecord: (NSDictionary *) userRecord
+16 -3
View File
@@ -1,6 +1,6 @@
/* SOGoToolUpdateAutoReply.m - this file is part of SOGo
*
* Copyright (C) 2011-2016 Inverse inc.
* Copyright (C) 2011-2019 Inverse inc.
*
* This file is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -81,8 +81,10 @@
SOGoUserDefaults *userDefaults;
SOGoUser *user;
BOOL result;
NSException *error;
user = [SOGoUser userWithLogin: theLogin];
userDefaults = [user userDefaults];
vacationOptions = [[userDefaults vacationOptions] mutableCopy];
[vacationOptions autorelease];
@@ -93,6 +95,14 @@
}
else
{
// We do NOT enable the vacation message automatically if the domain
// preference is disabled by default.
if (![[user domainDefaults] vacationPeriodEnabled])
{
NSLog(@"SOGoVacationPeriodEnabled set to NO for the domain - ignoring.");
return NO;
}
[vacationOptions setObject: [NSNumber numberWithBool: NO] forKey: @"startDateEnabled"];
}
@@ -118,8 +128,10 @@
account = [folder lookupName: @"0" inContext: localContext acquire: NO];
[account setContext: localContext];
result = [account updateFiltersWithUsername: theUsername andPassword: thePassword];
if (!result)
error = [account updateFiltersWithUsername: theUsername
andPassword: thePassword
forceActivation: NO];
if (error)
{
// Can't update Sieve script -- Reactivate auto-reply
if (disabling)
@@ -128,6 +140,7 @@
[vacationOptions setObject: [NSNumber numberWithBool: YES] forKey: @"startDateEnabled"];
[userDefaults setVacationOptions: vacationOptions];
[userDefaults synchronize];
result = NO;
}
}
Loaded 100 of 352 files, more files were not shown because too many files have changed in this diff. Show more