diff --git a/ChangeLog b/ChangeLog index 1e8e5bad2..66b761c24 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,543 @@ +commit faf228022ac83e3c66b0f4f1e23654a9b273e601 +Author: Ludovic Marcotte +Date: Fri Jun 21 11:25:55 2013 -0400 + + Updated for the release + +M Documentation/SOGo Installation Guide.odt +M Documentation/SOGo Mobile Devices Configuration.odt +M Documentation/SOGo Mozilla Thunderbird Configuration.odt +M Documentation/SOGo Native Microsoft Outlook Configuration.odt +M NEWS +M Version + +commit 3e87ba92db5eb698f3d73bba4e3e2457ef2b4ea2 +Author: Ludovic Marcotte +Date: Fri Jun 21 10:45:03 2013 -0400 + + Updated french translation + +M UI/Scheduler/French.lproj/Localizable.strings + +commit 0a87ff97b6a7d4add9b8413074a0567e289386af +Author: Jean Raby +Date: Fri Jun 21 09:36:54 2013 -0400 + + cosmetic fix, no functional change + + added a POST payload to test this code + +M SoObjects/SOGo/SOGoCASSession.m +A Tests/README-cas-logoutRequest + +commit 031cd210f74bb130bea7fd73e41864e045a5aac9 +Author: Ludovic Marcotte +Date: Thu Jun 20 20:29:16 2013 -0400 + + Better description + +M NEWS + +commit 9d9a434fdc3deb403301a4b006a50967df3265d7 +Author: Ludovic Marcotte +Date: Thu Jun 20 15:44:35 2013 -0400 + + Avoid crashing when reading fcs crap. + +M NEWS +M OpenChange/RTFHandler.m + +commit 41ed498d6512b9cf2ef01654ea29be4c535d0c16 +Author: Jean Raby +Date: Thu Jun 20 14:32:14 2013 -0400 + + Add support for CAS LogoutRequests + + Fixes #2346 + This changes the serviceURL sent by SOGo to the CAS server: + /SOGo/so/ -> /SOGo/so/index + +M NEWS +M SoObjects/SOGo/GNUmakefile +M SoObjects/SOGo/SOGoCASSession.h +M SoObjects/SOGo/SOGoCASSession.m +M SoObjects/SOGo/SOGoCache.h +M SoObjects/SOGo/SOGoCache.m +M UI/MainUI/SOGoRootPage.m + +commit d28482557ff344e908ad5ad2056965ead061b1b2 +Author: Ludovic Marcotte +Date: Thu Jun 20 13:43:45 2013 -0400 + + Fix for bug 2285 + +M OpenChange/MAPIStoreGCSFolder.m + +commit 4c97d549394b7d7e701f281620d355bc283f7012 +Author: Ludovic Marcotte +Date: Thu Jun 20 09:10:09 2013 -0400 + + Fix for bug #2119 + +M NEWS +M OpenChange/MAPIStoreMailMessage.m + +commit c08263d1c3d6444616f51643c409a4e2d7c02593 +Author: Ludovic Marcotte +Date: Wed Jun 19 17:36:43 2013 -0400 + + Updated patch + +M SoObjects/SOGo/SOGoProxyAuthenticator.m + +commit dc96466462a0f3814da34e07bad0bdabc1db0169 +Author: Ludovic Marcotte +Date: Wed Jun 19 16:06:12 2013 -0400 + + Updated for the release + +M NEWS + +commit 1734b387a353cab21b1f768791af8983e9f9ec98 +Author: Ludovic Marcotte +Date: Wed Jun 19 15:45:40 2013 -0400 + + Fix for bug #2237 + +M SoObjects/SOGo/SOGoProxyAuthenticator.m +M SoObjects/SOGo/SOGoPublicBaseFolder.m + +commit 871115e349dd71ac2801650ff98fc98a888dabd6 +Author: Ludovic Marcotte +Date: Wed Jun 19 15:07:19 2013 -0400 + + Fixed bug #2294 + +M SoObjects/SOGo/SOGoContentObject.m +M UI/Common/UIxFolderActions.h +M UI/Common/UIxFolderActions.m +M UI/Contacts/UIxContactFolderActions.m +M UI/Contacts/UIxContactFoldersView.m +M UI/Contacts/product.plist +M UI/Scheduler/UIxCalFolderActions.h +M UI/Scheduler/UIxCalFolderActions.m +M UI/Scheduler/UIxCalMainActions.h +M UI/Scheduler/UIxCalMainActions.m +M UI/Scheduler/product.plist +M UI/WebServerResources/ContactsUI.js +M UI/WebServerResources/SchedulerUI.js + +commit a7642868792e58cfb4c6d1614e53787b26f6fa33 +Author: Ludovic Marcotte +Date: Wed Jun 19 14:09:20 2013 -0400 + + Fix for bug #2313 + +M SoObjects/Mailer/SOGoMailFolder.m +M UI/MailerUI/UIxMailUserRightsEditor.h +M UI/MailerUI/UIxMailUserRightsEditor.m + +commit 5304461e4cc5a4a366a33e84a74f7c785be8aa0d +Author: Ludovic Marcotte +Date: Wed Jun 19 10:23:47 2013 -0400 + + now possible to see who created a event/task in a delegated calendar + +M NEWS +M SoObjects/Appointments/iCalEntityObject+SOGo.h +M SoObjects/Appointments/iCalEntityObject+SOGo.m +M SoObjects/SOGo/SOGoUser.h +M SoObjects/SOGo/SOGoUserManager.m +M UI/Scheduler/English.lproj/Localizable.strings +M UI/Scheduler/UIxAppointmentEditor.m +M UI/Scheduler/UIxComponentEditor.h +M UI/Scheduler/UIxComponentEditor.m +M UI/Templates/SchedulerUI/UIxCalMainView.wox +M UI/Templates/SchedulerUI/UIxComponentEditor.wox +M UI/WebServerResources/SchedulerUI.js +M UI/WebServerResources/UIxAppointmentEditor.js + +commit 6b25f08943032852466189cd98b3c956772f276b +Author: Jean Raby +Date: Tue Jun 18 19:33:36 2013 -0400 + + Use domainDefaults to get imapCASServiceName + +M SoObjects/SOGo/SOGoWebAuthenticator.m + +commit a0022e06f70c381b2816c23f8ffc5453cc8c2fd1 +Author: Jean Raby +Date: Tue Jun 18 17:57:17 2013 -0400 + + New preference: SOGoIMAPCASServiceName + + Reworked CAS section a bit and add some information regarding casifying sieve. + +M Documentation/SOGo Installation Guide.odt + +commit 0fb006c19faa62e7b05f8ef7edd23581046b50d9 +Author: Jean Raby +Date: Tue Jun 18 16:36:11 2013 -0400 + + New domain preference: SOGoIMAPCASServiceName + + Set this to the service name expected by the CAS server if it differs + from SOGoIMAPServer. This is useful to request a CAS ticket for service + imap://imap.domain.com while connecting through imapproxy on imap://127.0.0.1:1143 + + SOGoDAVAuthenticator is not updated since it doesn't really use the imap code. + +M SoObjects/SOGo/SOGoDomainDefaults.h +M SoObjects/SOGo/SOGoDomainDefaults.m +M SoObjects/SOGo/SOGoWebAuthenticator.m + +commit 47a3f1a19d1089a105aeefba831f119b407ca196 +Author: Ludovic Marcotte +Date: Tue Jun 18 14:14:42 2013 -0400 + + toggle to send or not notifications + +M NEWS +M SoObjects/Appointments/SOGoCalendarComponent.m +M UI/Scheduler/English.lproj/Localizable.strings +M UI/Scheduler/UIxAppointmentEditor.h +M UI/Scheduler/UIxAppointmentEditor.m +M UI/Templates/SchedulerUI/UIxAppointmentEditor.wox + +commit f25344182debb14001c39db452374f138cd0ee8b +Author: Ludovic Marcotte +Date: Fri Jun 14 14:58:16 2013 -0400 + + Rewrote escapedForCards so it's 10 times faster. + +M SOPE/NGCards/NSString+NGCards.m + +commit f24e3a0a3146b2d5207af8555377c80d52c477b5 +Author: Jean Raby +Date: Wed Jun 12 02:36:50 2013 -0400 + + whitespace fixes. tabkill + +M SoObjects/SOGo/SOGoDAVAuthenticator.m +M SoObjects/SOGo/SOGoProxyAuthenticator.m +M SoObjects/SOGo/SOGoWebAuthenticator.m + +commit afb7bc309c03562acf5066faa8b3c9f1e43950a0 +Author: Ludovic Marcotte +Date: Tue Jun 11 11:21:58 2013 -0400 + + Added message submissions rate-limiting support + +M Documentation/SOGo Installation Guide.odt +M NEWS +M SoObjects/SOGo/SOGoCache.h +M SoObjects/SOGo/SOGoCache.m +M SoObjects/SOGo/SOGoSystemDefaults.h +M SoObjects/SOGo/SOGoSystemDefaults.m +M UI/MailerUI/UIxMailEditor.m + +commit cfee5aa3f4095576d00bc7d05f7606d3ca72571e +Author: Ludovic Marcotte +Date: Tue Jun 11 09:41:17 2013 -0400 + + Added new failed login rate-limiting options + +M Documentation/SOGo Installation Guide.odt +M NEWS +M SoObjects/SOGo/NSCalendarDate+SOGo.m +M SoObjects/SOGo/SOGoCache.h +M SoObjects/SOGo/SOGoCache.m +M SoObjects/SOGo/SOGoSystemDefaults.h +M SoObjects/SOGo/SOGoSystemDefaults.m +M SoObjects/SOGo/SOGoUserManager.m +M UI/MainUI/SOGoRootPage.m + +commit 2a51f6f385b71506e1d089fa9045e74b53954703 +Author: Francis Lachapelle +Date: Mon Jun 10 21:07:00 2013 -0400 + + Update CKEditor to version 4.1.2 + + Also added the blockquote plugin. + +M UI/WebServerResources/ckeditor/build-config.js +M UI/WebServerResources/ckeditor/ckeditor.js +M UI/WebServerResources/ckeditor/config.js +M UI/WebServerResources/ckeditor/lang/ar.js +M UI/WebServerResources/ckeditor/lang/ca.js +M UI/WebServerResources/ckeditor/lang/cs.js +M UI/WebServerResources/ckeditor/lang/cy.js +M UI/WebServerResources/ckeditor/lang/da.js +M UI/WebServerResources/ckeditor/lang/de.js +M UI/WebServerResources/ckeditor/lang/en.js +M UI/WebServerResources/ckeditor/lang/es.js +M UI/WebServerResources/ckeditor/lang/fi.js +M UI/WebServerResources/ckeditor/lang/fr.js +M UI/WebServerResources/ckeditor/lang/hu.js +M UI/WebServerResources/ckeditor/lang/is.js +M UI/WebServerResources/ckeditor/lang/it.js +M UI/WebServerResources/ckeditor/lang/nb.js +M UI/WebServerResources/ckeditor/lang/nl.js +M UI/WebServerResources/ckeditor/lang/no.js +M UI/WebServerResources/ckeditor/lang/pl.js +M UI/WebServerResources/ckeditor/lang/pt-br.js +M UI/WebServerResources/ckeditor/lang/ru.js +M UI/WebServerResources/ckeditor/lang/sk.js +M UI/WebServerResources/ckeditor/lang/sv.js +M UI/WebServerResources/ckeditor/lang/uk.js +M UI/WebServerResources/ckeditor/plugins/clipboard/dialogs/paste.js +M UI/WebServerResources/ckeditor/plugins/icons.png +M UI/WebServerResources/ckeditor/plugins/wsc/dialogs/ciframe.html +M UI/WebServerResources/ckeditor/plugins/wsc/dialogs/wsc.js +A UI/WebServerResources/ckeditor/plugins/wsc/dialogs/wsc_ie.js +M UI/WebServerResources/ckeditor/skins/moono/editor.css +M UI/WebServerResources/ckeditor/skins/moono/editor_gecko.css +M UI/WebServerResources/ckeditor/skins/moono/editor_ie.css +M UI/WebServerResources/ckeditor/skins/moono/editor_ie7.css +M UI/WebServerResources/ckeditor/skins/moono/editor_ie8.css +M UI/WebServerResources/ckeditor/skins/moono/editor_iequirks.css +M UI/WebServerResources/ckeditor/skins/moono/icons.png + +commit 4cffea43ebf88870a718f4e244ea224e0d952f42 +Author: Jean Raby +Date: Fri Jun 7 15:16:12 2013 -0400 + + French translation of Sync Issues + +M Scripts/openchange_user_cleanup + +commit cafce5753db7f35dafd5e37db4cf219754efd18e +Author: Jean Raby +Date: Thu Jun 6 16:04:47 2013 -0400 + + update description of MultipleBookingsFieldName + +M Documentation/SOGo Installation Guide.odt + +commit 5d6a5f5d5ed13be922226f2894ca8eed57f5061b +Author: Francis Lachapelle +Date: Thu Jun 6 13:21:26 2013 -0400 + + Update CKEditor to version 4.1.1 + +M NEWS +M UI/WebServerResources/ckeditor/build-config.js +M UI/WebServerResources/ckeditor/ckeditor.js +M UI/WebServerResources/ckeditor/config.js +M UI/WebServerResources/ckeditor/contents.css +A UI/WebServerResources/ckeditor/lang/ar.js +M UI/WebServerResources/ckeditor/lang/ca.js +M UI/WebServerResources/ckeditor/lang/cs.js +M UI/WebServerResources/ckeditor/lang/cy.js +M UI/WebServerResources/ckeditor/lang/da.js +M UI/WebServerResources/ckeditor/lang/de.js +M UI/WebServerResources/ckeditor/lang/en.js +M UI/WebServerResources/ckeditor/lang/es.js +M UI/WebServerResources/ckeditor/lang/fi.js +M UI/WebServerResources/ckeditor/lang/fr.js +M UI/WebServerResources/ckeditor/lang/hu.js +M UI/WebServerResources/ckeditor/lang/is.js +M UI/WebServerResources/ckeditor/lang/it.js +M UI/WebServerResources/ckeditor/lang/nb.js +M UI/WebServerResources/ckeditor/lang/nl.js +M UI/WebServerResources/ckeditor/lang/no.js +M UI/WebServerResources/ckeditor/lang/pl.js +A UI/WebServerResources/ckeditor/lang/pt-br.js +D UI/WebServerResources/ckeditor/lang/pt.js +M UI/WebServerResources/ckeditor/lang/ru.js +M UI/WebServerResources/ckeditor/lang/sk.js +M UI/WebServerResources/ckeditor/lang/sv.js +M UI/WebServerResources/ckeditor/lang/uk.js +A UI/WebServerResources/ckeditor/plugins/colordialog/dialogs/colordialog.js +M UI/WebServerResources/ckeditor/plugins/icons.png +M UI/WebServerResources/ckeditor/plugins/image/dialogs/image.js +M UI/WebServerResources/ckeditor/plugins/link/dialogs/link.js +M UI/WebServerResources/ckeditor/plugins/scayt/dialogs/options.js +M UI/WebServerResources/ckeditor/skins/moono/dialog.css +M UI/WebServerResources/ckeditor/skins/moono/dialog_ie.css +M UI/WebServerResources/ckeditor/skins/moono/dialog_ie7.css +M UI/WebServerResources/ckeditor/skins/moono/dialog_ie8.css +M UI/WebServerResources/ckeditor/skins/moono/dialog_iequirks.css +M UI/WebServerResources/ckeditor/skins/moono/dialog_opera.css +M UI/WebServerResources/ckeditor/skins/moono/editor.css +M UI/WebServerResources/ckeditor/skins/moono/editor_gecko.css +M UI/WebServerResources/ckeditor/skins/moono/editor_ie.css +M UI/WebServerResources/ckeditor/skins/moono/editor_ie7.css +M UI/WebServerResources/ckeditor/skins/moono/editor_ie8.css +M UI/WebServerResources/ckeditor/skins/moono/editor_iequirks.css +M UI/WebServerResources/ckeditor/skins/moono/icons.png +M UI/WebServerResources/ckeditor/styles.js + +commit 8e5a26dfb15f6105d014dc3298573a25ccf75844 +Author: Francis Lachapelle +Date: Wed Jun 5 15:53:53 2013 -0400 + + Remove unicode separators from JSON + + Fixes #2309 + +M NEWS +M SoObjects/SOGo/NSString+Utilities.m + +commit 4cb0facd72b06d09f0e1403f673bef25ed47cac5 +Author: Ludovic Marcotte +Date: Tue Jun 4 08:51:38 2013 -0400 + + Fixed bug #2206 + +M NEWS +M UI/MailerUI/UIxMailView.m + +commit 854bce28fbefe913aa6a823436a991da23164a60 +Author: Francis Lachapelle +Date: Mon Jun 3 12:07:18 2013 -0400 + + Fix condition in UIxMailPartHTMLViewer + +M UI/MailPartViewers/UIxMailPartHTMLViewer.m + +commit 49e964c5294f9b65295e55a46417a2fa73eadf44 +Author: Francis Lachapelle +Date: Mon Jun 3 11:47:48 2013 -0400 + + Fix decoding charset parameter + + To avoid a libxml bug/limitation, we were already stripping the charset + parameter, but we were not considering the single quote as valid + delimiter. + +M NEWS +M UI/MailPartViewers/UIxMailPartHTMLViewer.m + +commit 3281cea2a213e8d551a60227da0ad1409ec85266 +Author: Jean Raby +Date: Fri May 31 15:54:22 2013 -0400 + + Add bindFields to the sample configuration file + +M Scripts/sogo.conf + +commit 82253e91d2e8f0410625e11a03737883ea9f1d60 +Author: Jean Raby +Date: Fri May 24 15:00:05 2013 -0400 + + don't use -M when creating the sogo user on debian + + -M was not available in lenny. It is not actually needed since useradd + won't create the homedir when adding a system user (-r) + +M packaging/debian-multiarch/sogo.preinst +M packaging/debian/sogo.preinst + +commit 1d1ed9a031f17d607b804bcb177fa106c46f6f9b +Author: Jean Raby +Date: Wed May 15 14:51:15 2013 -0400 + + Update ldap filter description, example and syntax + +M Documentation/SOGo Installation Guide.odt + +commit 2bd8a2935f737f99544cb45659c61afbc16eaea2 +Author: Francis Lachapelle +Date: Thu May 9 08:12:27 2013 -0400 + + Change default value of SOGoMailSpoolPath + + Set the spool path to /var/spool/sogo and modified the installation + guide to point to the tmpwatch cronjob when changing SOGoMailSpoolPath. + +M Documentation/SOGo Installation Guide.odt +M Scripts/tmpwatch +M SoObjects/SOGo/SOGoDefaults.plist + +commit 8a25e3882746fa7e772a8f73b160f5fa2e05a875 +Author: Francis Lachapelle +Date: Thu May 2 09:07:17 2013 -0400 + + Add indent group to ckeditor toolbar + +M UI/WebServerResources/ckeditor/config.js + +commit 4ec19d46fa8fa8bde0c7f1b6a87d4828677de7c0 +Author: Ludovic Marcotte +Date: Thu Apr 25 14:46:00 2013 -0400 + + Removed left-over code that would cause the logic from bug #2035 to not work. + +M SoObjects/Appointments/SOGoAppointmentFolder.m + +commit d46a39e2523b3322bd8d9285b53638cd304912dd +Author: Jean Raby +Date: Mon Apr 22 13:22:16 2013 -0400 + + TOC links + +M Documentation/SOGo Installation Guide.odt + +commit 23e10d30be3aaef111ebb9ecd8ff87fdd3ec19d2 +Author: Jean Raby +Date: Mon Apr 22 12:31:18 2013 -0400 + + Added ldap attribute mapping table + +M Documentation/SOGo Installation Guide.odt + +commit a53c66e8a449d17f4f4484ef299b7500a8a5612b +Author: Jean Raby +Date: Thu Apr 18 14:35:56 2013 -0400 + + Install openchange_user_cleanup in usr/sbin/ + + Instead of usr/share/doc/sogo/ + +D Scripts/openchange_cleanup.py +M packaging/debian-multiarch/rules +M packaging/debian-multiarch/sogo.docs +M packaging/debian/rules +M packaging/debian/sogo.docs +M packaging/rhel/sogo.spec + +commit 4fb3492ea858458ea7e37acb73fa2ae35b41aed5 +Author: Jean Raby +Date: Thu Apr 18 14:28:55 2013 -0400 + + openchange_cleanup.py -> openchange_user_cleanup + +A Scripts/openchange_user_cleanup + +commit 353f40ac3e0552d95cd787b319eb6d60d1a42f81 +Author: Jean Raby +Date: Thu Apr 18 14:15:35 2013 -0400 + + try to cleanup under INBOX too (altnamespace = no) + +M Scripts/openchange_cleanup.py + +commit 96c831f72686fed7a1469b401ebb3bc732fd0432 +Author: Jean Raby +Date: Thu Apr 18 14:11:36 2013 -0400 + + Use separator sent by the imap server, not '/' + +M Scripts/openchange_cleanup.py + +commit d7fdfc6cb27665de2c9b5d860454b69fe9107d54 +Author: Jean Raby +Date: Tue Apr 16 15:16:54 2013 -0400 + + Update NEWS + bump version + +M NEWS +M Version + +commit d02f58b3cd6de503de7d2ea4d3963a5e4dcb6212 +Author: Jean Raby +Date: Tue Apr 16 15:12:29 2013 -0400 + + Update ChangeLog + +M ChangeLog + commit 62fee93d866b8eb35abbf67d61ba5274a3a27827 Author: Ludovic Marcotte Date: Tue Apr 16 14:33:57 2013 -0400 diff --git a/Documentation/SOGo Installation Guide.odt b/Documentation/SOGo Installation Guide.odt index cdbba3949..ba47f7351 100644 Binary files a/Documentation/SOGo Installation Guide.odt and b/Documentation/SOGo Installation Guide.odt differ diff --git a/Documentation/SOGo Mobile Devices Configuration.odt b/Documentation/SOGo Mobile Devices Configuration.odt index 8ae5ac6b9..652d8dc6c 100644 Binary files a/Documentation/SOGo Mobile Devices Configuration.odt and b/Documentation/SOGo Mobile Devices Configuration.odt differ diff --git a/Documentation/SOGo Mozilla Thunderbird Configuration.odt b/Documentation/SOGo Mozilla Thunderbird Configuration.odt index 5d4f5bdfa..47af9f2a3 100644 Binary files a/Documentation/SOGo Mozilla Thunderbird Configuration.odt and b/Documentation/SOGo Mozilla Thunderbird Configuration.odt differ diff --git a/Documentation/SOGo Native Microsoft Outlook Configuration.odt b/Documentation/SOGo Native Microsoft Outlook Configuration.odt index 4221333dc..78b6c6875 100644 Binary files a/Documentation/SOGo Native Microsoft Outlook Configuration.odt and b/Documentation/SOGo Native Microsoft Outlook Configuration.odt differ diff --git a/NEWS b/NEWS index a937922eb..0fb8c7610 100644 --- a/NEWS +++ b/NEWS @@ -1,4 +1,30 @@ +2.0.6 (2013-06-21) +------------------ + +Enhancements + - updated CKEditor to version 4.1.1 (#2333) + - new failed login attemps rate-limiting options. See the new + SOGoMaximumFailedLoginCount, SOGoMaximumFailedLoginInterval and + SOGoFailedLoginBlockInterval defaults + - new message submissions rate-limiting options. See the new + SOGoMaximumMessageSubmissionCount, SOGoMaximumRecipientCount, + SOGoMaximumSubmissionInterval and SOGoMessageSubmissionBlockInterval defaults + - now possible to send or not event notifications on a per-event basis + - now possible to see who created an event/task in a delegated calendar + - multi-domain support in OpenChange (implemented using a trick) + +Bug fixes + - fixed decoding of the charset parameter when using single quotes (#2306) + - fixed potential crash when sending MDN from Sent folder (#2209) + - fixed handling of unicode separators (#2309) + - fixed public access when SOGoTrustProxyAuthentication is used (#2237) + - fixed access right issues with import feature (#2294) + - fixed IMAP ACL issue when SOGoForceExternalLoginWithEmail is used (#2313) + - fixed handling of CAS logoutRequest (#2346) + - fixed many major OpenChange stability issues + 2.0.5a (2013-04-17) +------------------ Bug fixes - Fixed an issue when parsing user CN with leading or trailing spaces (#2287) diff --git a/OpenChange/MAPIStoreGCSFolder.m b/OpenChange/MAPIStoreGCSFolder.m index f64ca32f5..750a255a4 100644 --- a/OpenChange/MAPIStoreGCSFolder.m +++ b/OpenChange/MAPIStoreGCSFolder.m @@ -461,7 +461,7 @@ static Class NSNumberK; [mapping setObject: cLastModified forKey: changeNumber]; [messageEntry setObject: changeNumber forKey: @"version"]; - newChangeNum = [changeNumber unsignedLongValue]; + newChangeNum = [changeNumber unsignedLongLongValue]; changeKey = [self getReplicaKeyFromGlobCnt: newChangeNum >> 16]; [self _setChangeKey: changeKey forMessageEntry: messageEntry inChangeListOnly: NO]; diff --git a/OpenChange/MAPIStoreMailMessage.m b/OpenChange/MAPIStoreMailMessage.m index 476ddbac6..0515f0163 100644 --- a/OpenChange/MAPIStoreMailMessage.m +++ b/OpenChange/MAPIStoreMailMessage.m @@ -693,7 +693,15 @@ _compareBodyKeysByPriority (id entry1, id entry2, void *data) ngAddress = [[NGMailAddressParser mailAddressParserWithString: fullMail] parse]; if ([ngAddress isKindOfClass: [NGMailAddress class]]) - cn = [ngAddress displayName]; + { + cn = [ngAddress displayName]; + + // If we don't have a displayName, we use the email address instead. This + // avoid bug #2119 - where Outlook won't display anything in the "From" field, + // nor in the recipient field if we reply to the email. + if (![cn length]) + cn = [ngAddress address]; + } else cn = @""; diff --git a/OpenChange/RTFHandler.m b/OpenChange/RTFHandler.m index 99ddd48b1..27afe3c00 100644 --- a/OpenChange/RTFHandler.m +++ b/OpenChange/RTFHandler.m @@ -777,6 +777,10 @@ const unsigned short ansicpg874[256] = { [_html appendBytes: v length: strlen(v)]; free(v); } + else if ([s hasPrefix: @"fcs"]) + { + // ignore + } else if ([s hasPrefix: @"fs"]) { // ignore diff --git a/SOPE/NGCards/NSString+NGCards.m b/SOPE/NGCards/NSString+NGCards.m index 49c0a5798..8eef43074 100644 --- a/SOPE/NGCards/NSString+NGCards.m +++ b/SOPE/NGCards/NSString+NGCards.m @@ -155,20 +155,41 @@ - (NSString *) escapedForCards { - NSString *string; + NSMutableString *string; + unsigned int len, i; + unichar c; - string = [self stringByReplacingString: @"\\" - withString: @"\\\\"]; - string = [string stringByReplacingString: @"," - withString: @"\\,"]; - // string = [string stringByReplacingString: @":" - // withString: @"\\:"]; - string = [string stringByReplacingString: @";" - withString: @"\\;"]; - string = [string stringByReplacingString: @"\n" - withString: @"\\n"]; - string = [string stringByReplacingString: @"\r" - withString: @"\\r"]; + len = [self length]; + string = [NSMutableString stringWithCapacity: len * 1.5]; + + for (i = 0; i < len; i++) + { + c = [self characterAtIndex: i]; + + switch (c) + { + case '\\': + [string appendString: @"\\\\"]; + break; + case ',': + [string appendString: @"\\,"]; + break; + //case ':': + // [string appendString: @"\\:"]; + //break; + case ';': + [string appendString: @"\\;"]; + break; + case'\n': + [string appendString: @"\\n"]; + break; + case'\r': + [string appendString: @"\\r"]; + break; + default: + [string appendFormat: @"%C", c]; + } + } return string; } diff --git a/Scripts/openchange_cleanup.py b/Scripts/openchange_user_cleanup similarity index 92% rename from Scripts/openchange_cleanup.py rename to Scripts/openchange_user_cleanup index 9ceeca85a..8c32431fc 100755 --- a/Scripts/openchange_cleanup.py +++ b/Scripts/openchange_user_cleanup @@ -89,6 +89,16 @@ def main(): except Exception as e: print "Error during sqlCleanup, continuing: %s" % str(e) +def getsep(client): + seq = None + (code, data) = client.list("", "") + if code == "OK" and data is not None: + # yes this is ugly but it works on cyrus and dovecot. + # (\\Noinferiors \\HasNoChildren) "/" INBOX + m = re.search(".*\s+[\"\']?(.)[\"\']?\s+[\"\']?.*[\"\']?$", data[0]) + sep = m.group(1) + return sep + def extractmb(si): inparen = False inquote = False @@ -120,13 +130,13 @@ def extractmb(si): return parts[-1] -def cleanupmb(mb, client): - (code, data) = client.list("%s/" % mb, "%") +def cleanupmb(mb, sep, client): + (code, data) = client.list("%s%s" % (mb, sep), "%") if code == "OK": for si in data: if si is not None: submb = extractmb(si) - cleanupmb(submb, client) + cleanupmb(submb, sep, client) else: raise Exception, "Failure while cleaning up '%s'" % mb client.unsubscribe(mb) @@ -145,13 +155,20 @@ def imapCleanup(imaphost, imapport, username, userpass): print "Logged in as '%s'" % username - for foldername in ("Sync Issues", "Junk E-mail"): + sep = getsep(client) + if not sep: + client.logout() + return + + for foldername in ("Sync Issues", "Junk E-mail", + "INBOX%sSync Issues" % sep, "INBOX%sJunk E-mail" % sep, + "Probl&AOg-mes de synchronisation"): (code, data) = client.list(foldername, "%") if code == "OK": for si in data: if si is not None: mb = extractmb(si) - cleanupmb(mb, client) + cleanupmb(mb, sep, client) client.logout() def mapistoreCleanup(mapistorefolder, username): diff --git a/Scripts/sogo.conf b/Scripts/sogo.conf index caff050a6..8ae3f001f 100644 --- a/Scripts/sogo.conf +++ b/Scripts/sogo.conf @@ -44,8 +44,9 @@ // { // type = ldap; // CNFieldName = cn; - // IDFieldName = uid; // UIDFieldName = uid; + // IDFieldName = uid; // first field of the DN for direct binds + // bindFields = (uid, mail); // array of fields to use for indirect binds // baseDN = "ou=users,dc=acme,dc=com"; // bindDN = "uid=sogo,ou=users,dc=acme,dc=com"; // bindPassword = qwerty; diff --git a/Scripts/tmpwatch b/Scripts/tmpwatch index 00f4e0366..5aa1eb521 100644 --- a/Scripts/tmpwatch +++ b/Scripts/tmpwatch @@ -1,5 +1,6 @@ #!/bin/sh +# SOGOSPOOL must match the value of the configuration parameter SOGoMailSpoolPath SOGOSPOOL=/var/spool/sogo /usr/sbin/tmpwatch 24 "$SOGOSPOOL" diff --git a/SoObjects/Appointments/SOGoAppointmentFolder.m b/SoObjects/Appointments/SOGoAppointmentFolder.m index b54cb846c..395133ac8 100644 --- a/SoObjects/Appointments/SOGoAppointmentFolder.m +++ b/SoObjects/Appointments/SOGoAppointmentFolder.m @@ -2901,9 +2901,6 @@ firstInstanceCalendarDateRange: (NGCalendarDateRange *) fir NSMutableString *content; NSString *uid; - uid = [self globallyUniqueObjectId]; - [event setUid: uid]; - // We first look if there's an event with the same UID in our calendar. If not, // let's reuse what is in the event, otherwise generate a new GUID and use it. uid = [event uid]; diff --git a/SoObjects/Appointments/SOGoCalendarComponent.m b/SoObjects/Appointments/SOGoCalendarComponent.m index e71ec34e5..2169f8f6e 100644 --- a/SoObjects/Appointments/SOGoCalendarComponent.m +++ b/SoObjects/Appointments/SOGoCalendarComponent.m @@ -760,6 +760,10 @@ SOGoUser *ownerUser; SOGoDomainDefaults *dd; + // If defined, we return immediately. When not defined, we send the notifications correctly + if ([object firstChildWithTag: @"X-SOGo-Send-Appointment-Notifications"]) + return; + ownerUser = [SOGoUser userWithLogin: owner]; dd = [ownerUser domainDefaults]; if ([dd appointmentSendEMailNotifications] && [object isStillRelevant]) diff --git a/SoObjects/Appointments/iCalEntityObject+SOGo.h b/SoObjects/Appointments/iCalEntityObject+SOGo.h index 6d1545dcc..8963a1d69 100644 --- a/SoObjects/Appointments/iCalEntityObject+SOGo.h +++ b/SoObjects/Appointments/iCalEntityObject+SOGo.h @@ -1,8 +1,6 @@ /* iCalEntityObject+SOGo.h - this file is part of SOGo * - * Copyright (C) 2007 Inverse inc. - * - * Author: Wolfgang Sourdeau + * Copyright (C) 2007-2013 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 @@ -48,6 +46,8 @@ extern NSNumber *iCalDistantFutureNumber; - (NSMutableDictionary *) quickRecord; - (int) priorityNumber; +- (NSString *) createdBy; + - (NSNumber *) quickRecordDateAsNumber: (NSCalendarDate *) _date withOffset: (int) offset forAllDay: (BOOL) allDay; diff --git a/SoObjects/Appointments/iCalEntityObject+SOGo.m b/SoObjects/Appointments/iCalEntityObject+SOGo.m index 831ac3d3a..70aeffaf0 100644 --- a/SoObjects/Appointments/iCalEntityObject+SOGo.m +++ b/SoObjects/Appointments/iCalEntityObject+SOGo.m @@ -242,4 +242,19 @@ NSNumber *iCalDistantFutureNumber = nil; return priorityNumber; } +- (NSString *) createdBy +{ + NSString *created_by; + + created_by = [[self firstChildWithTag: @"X-SOGo-Component-Created-By"] flattenedValuesForKey: @""]; + + // We consider "SENT-BY" in case our custom header isn't found + if (![created_by length]) + { + created_by = [[self organizer] sentBy]; + } + + return created_by; +} + @end diff --git a/SoObjects/Mailer/SOGoMailFolder.m b/SoObjects/Mailer/SOGoMailFolder.m index 8d2290a4c..ad7c6bbe3 100644 --- a/SoObjects/Mailer/SOGoMailFolder.m +++ b/SoObjects/Mailer/SOGoMailFolder.m @@ -1,15 +1,15 @@ /* - Copyright (C) 2009-2011 Inverse inc. + Copyright (C) 2009-2013 Inverse inc. Copyright (C) 2004-2005 SKYRIX Software AG - This file is part of OpenGroupware.org. + This file is part of SOGo. - OGo is free software; you can redistribute it and/or modify it under + SOGo is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2, or (at your option) any later version. - OGo is distributed in the hope that it will be useful, but WITHOUT ANY + SOGo is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. @@ -1177,6 +1177,10 @@ static NSString *defaultUserID = @"anyone"; if ([uid hasPrefix: @"@"]) return [[[[context activeUser] domainDefaults] imapAclGroupIdPrefix] stringByAppendingString: [uid substringFromIndex: 1]]; + else if ([[[context activeUser] domainDefaults] forceExternalLoginWithEmail]) + { + return [[[context activeUser] primaryIdentity] objectForKey: @"email"]; + } else return uid; } diff --git a/SoObjects/SOGo/GNUmakefile b/SoObjects/SOGo/GNUmakefile index f38ce600a..43bce3e54 100644 --- a/SoObjects/SOGo/GNUmakefile +++ b/SoObjects/SOGo/GNUmakefile @@ -140,7 +140,8 @@ SOGo_OBJC_FILES = \ SOGo_RESOURCE_FILES = \ SOGoDefaults.plist \ - DAVReportMap.plist + DAVReportMap.plist \ + CASLogoutRequestMap.plist ifeq ($(saml2_config), yes) SOGo_HEADER_FILES += SOGoSAML2Session.h SOGoSAML2Exceptions.h diff --git a/SoObjects/SOGo/NSCalendarDate+SOGo.m b/SoObjects/SOGo/NSCalendarDate+SOGo.m index 2b1306ef7..4ba4cd4ed 100644 --- a/SoObjects/SOGo/NSCalendarDate+SOGo.m +++ b/SoObjects/SOGo/NSCalendarDate+SOGo.m @@ -113,19 +113,10 @@ static NSString *rfc822Months[] = {@"", @"Jan", @"Feb", @"Mar", @"Apr", timeZoneShift]; } -/* <<<<<<< variant A -#define secondsOfDistantFuture 1073741823.0 -#define secondsOfDistantPast -1518491648.0 ->>>>>>> variant B */ + #define secondsOfDistantFuture 1073741823.0 #define secondsOfDistantPast -1073741823.0 -/* -####### Ancestor -#define secondsOfDistantFuture 63113990400.0 -#define secondsOfDistantPast -63113817600.0 -======= end */ - + (id) distantFuture { static NSCalendarDate *date = nil; diff --git a/SoObjects/SOGo/NSString+Utilities.m b/SoObjects/SOGo/NSString+Utilities.m index 506344284..afca514b9 100644 --- a/SoObjects/SOGo/NSString+Utilities.m +++ b/SoObjects/SOGo/NSString+Utilities.m @@ -1,6 +1,6 @@ /* NSString+Utilities.m - this file is part of SOGo * - * Copyright (C) 2006-2011 Inverse inc. + * Copyright (C) 2006-2013 Inverse inc. * * Author: Wolfgang Sourdeau * Ludovic Marcotte @@ -279,24 +279,28 @@ static int cssEscapingCount; - (NSString *) jsonRepresentation { - static char thisCharCode[28]; + static unichar thisCharCode[29]; static NSString *controlCharString = nil; static NSCharacterSet *controlCharSet = nil; NSString *cleanedString; - int i; + int i, j; if (!controlCharSet) { // Create an array of chars for all control characters between 0x00 and 0x1F, // apart from \t, \n, \f and \r (0x08, 0x09, 0x0A, 0x0C and 0x0D) - for (i = 0x00; i <= 0x08; i++) { - thisCharCode[i] = i; + for (i = 0, j = 0x00; j < 0x08; i++, j++) { + thisCharCode[i] = j; } - thisCharCode[9] = 0x0B; - for (i = 0x0E; i <= 0x1F; i++) { - thisCharCode[i - 4] = i; + thisCharCode[i++] = 0x0B; + for (j = 0x0E; j <= 0x1F; i++, j++) { + thisCharCode[i] = j; } - controlCharString = [NSString stringWithCString: thisCharCode length: 28]; + + // Also add some unicode separators + thisCharCode[i++] = 0x2028; // line separator + thisCharCode[i++] = 0x2029; // paragraph separator + controlCharString = [NSString stringWithCharacters:thisCharCode length:i]; controlCharSet = [NSCharacterSet characterSetWithCharactersInString: controlCharString]; [controlCharSet retain]; } @@ -304,7 +308,6 @@ static int cssEscapingCount; // Escape double quotes and remove control characters cleanedString = [[[self doubleQuotedString] componentsSeparatedByCharactersInSet: controlCharSet] componentsJoinedByString: @""]; - return cleanedString; } diff --git a/SoObjects/SOGo/SOGoCASSession.h b/SoObjects/SOGo/SOGoCASSession.h index ee958b5a4..ade1cf0a5 100644 --- a/SoObjects/SOGo/SOGoCASSession.h +++ b/SoObjects/SOGo/SOGoCASSession.h @@ -51,6 +51,8 @@ + (SOGoCASSession *) CASSessionWithIdentifier: (NSString *) newIdentifier fromProxy: (BOOL) fromProxy; ++ (void) handleLogoutRequest: (NSString *) logoutRequest; + - (NSString *) identifier; - (NSString *) ticket; @@ -65,4 +67,13 @@ @end +@interface CASLogoutRequest : NSObject +{ + NSString *sessionIndex; +} + +- (NSString *) sessionIndex; + +@end + #endif /* SOGOCASSESSION_H */ diff --git a/SoObjects/SOGo/SOGoCASSession.m b/SoObjects/SOGo/SOGoCASSession.m index 9104dbe03..ce0f8917e 100644 --- a/SoObjects/SOGo/SOGoCASSession.m +++ b/SoObjects/SOGo/SOGoCASSession.m @@ -20,6 +20,7 @@ * Boston, MA 02111-1307, USA. */ +#import #import #import @@ -35,6 +36,11 @@ #import #import +#import +#import +#import +#import + #import "NSDictionary+Utilities.h" #import "NSString+Utilities.h" #import "SOGoCache.h" @@ -106,6 +112,41 @@ return session; } ++ (void) handleLogoutRequest: (NSString *) logoutRequest +{ + CASLogoutRequest *rq; + SOGoCache *cache; + NSBundle *bundle; + NSString *mapFile, *sessionIndex; + + bundle = [NSBundle bundleForClass: [self class]]; + mapFile = [bundle pathForResource: @"CASLogoutRequestMap" ofType: @"plist"]; + if (![mapFile length]) + { + [self errorWithFormat: @"mapFile not found (CASLogoutRequest.plist)"]; + return; + } + + id parser = nil; + SaxObjectDecoder *sax = nil; + parser = [[SaxXMLReaderFactory standardXMLReaderFactory] + createXMLReaderForMimeType:@"text/xml"]; + + sax = [[SaxObjectDecoder alloc] initWithMappingAtPath: mapFile]; + [sax autorelease]; + [parser setContentHandler:sax]; + [parser setErrorHandler:sax]; + [parser parseFromSource: logoutRequest]; + + rq = [sax rootObject]; + sessionIndex = [rq sessionIndex]; + + if ([sessionIndex length]) + { + [[SOGoCache sharedCache] removeCASSessionWithTicket: sessionIndex]; + } +} + - (id) init { if ((self = [super init])) @@ -381,7 +422,12 @@ NSString *serviceURL; soURL = [[WOApplication application] soURL]; - serviceURL = [soURL absoluteString]; + + /* appending 'index' to /SOGo/so/ so that callbacks made by the CAS server + * are not greeted with a 302 generated by sope. The CAS server doesn't + * follow redirects, and we'd end up losing the LogoutRequests + */ + serviceURL = [NSString stringWithFormat: @"%@index", [soURL absoluteString]]; params = [NSDictionary dictionaryWithObjectsAndKeys: ticket, @"ticket", serviceURL, @"service", @@ -466,3 +512,28 @@ } @end + +@implementation CASLogoutRequest + +- (id) init +{ + if ((self = [super init])) + { + sessionIndex = nil; + } + + return self; +} + +- (void) dealloc +{ + [sessionIndex release]; + [super dealloc]; +} + +- (NSString *) sessionIndex +{ + return sessionIndex; +} + +@end diff --git a/SoObjects/SOGo/SOGoCache.h b/SoObjects/SOGo/SOGoCache.h index b1277937b..af6be9787 100644 --- a/SoObjects/SOGo/SOGoCache.h +++ b/SoObjects/SOGo/SOGoCache.h @@ -1,6 +1,6 @@ /* SOGoCache.h - this file is part of SOGo * - * Copyright (C) 2008-2011 Inverse inc. + * Copyright (C) 2008-2013 Inverse inc. * * Author: Wolfgang Sourdeau * Ludovic Marcotte @@ -98,6 +98,17 @@ forLogin: (NSString *) login; - (NSString *) userSettingsForLogin: (NSString *) theLogin; +- (void) setFailedCount: (int) theCount + forLogin: (NSString *) theLogin; + +- (NSDictionary *) failedCountForLogin: (NSString *) login; + +- (void) setMessageSubmissionsCount: (int) theCount + recipientsCount: (int) theRecipientsCount + forLogin: (NSString *) theLogin; + +- (NSDictionary *) messageSubmissionsCountForLogin: (NSString *) theLogin; + // // CAS support // @@ -111,6 +122,8 @@ - (void) setCASPGTId: (NSString *) pgtId forPGTIOU: (NSString *) pgtIou; +- (void) removeCASSessionWithTicket: (NSString *) ticket; + // SAML2 support - (NSDictionary *) saml2LoginDumpsForIdentifier: (NSString *) identifier; - (void) setSaml2LoginDumps: (NSDictionary *) dump diff --git a/SoObjects/SOGo/SOGoCache.m b/SoObjects/SOGo/SOGoCache.m index e815224e1..6e46fa11c 100644 --- a/SoObjects/SOGo/SOGoCache.m +++ b/SoObjects/SOGo/SOGoCache.m @@ -1,6 +1,6 @@ /* SOGoCache.m - this file is part of SOGo * - * Copyright (C) 2008-2010 Inverse inc. + * Copyright (C) 2008-2013 Inverse inc. * * Author: Wolfgang Sourdeau * Ludovic Marcotte @@ -24,22 +24,24 @@ /* * [ Cache Structure ] * - * users value = instances of SOGoUser > flushed after the completion of every SOGo requests - * groups value = instances of SOGoGroup > flushed after the completion of every SOGo requests - * imap4Connections value = - * localCache value = any value of what's in memcached - this is used to NOT query memcached within the same sogod instance + * users value = instances of SOGoUser > flushed after the completion of every SOGo requests + * groups value = instances of SOGoGroup > flushed after the completion of every SOGo requests + * imap4Connections value = + * localCache value = any value of what's in memcached - this is used to NOT query memcached within the same sogod instance * * [ Distributed (using memcached) cache structure ] * - * +defaults value = NSDictionary instance > user's defaults - * +settings value = NSDictionary instance > user's settings - * +attributes value = NSMutableDictionary instance > user's LDAP attributes - * +acl value = NSDictionary instance > ACLs on an object at specified path - * + value = NSString instance (array components separated by ",") or group member logins for a specific group in domain - * cas-id:< > value = - * cas-ticket:< > value = - * cas-pgtiou:< > value = - * session:< > value = + * +defaults value = NSDictionary instance > user's defaults + * +settings value = NSDictionary instance > user's settings + * +attributes value = NSMutableDictionary instance > user's LDAP attributes + * +acl value = NSDictionary instance > ACLs on an object at specified path + * + value = NSString instance (array components separated by ",") or group member logins for a specific group in domain + * cas-id:< > value = + * cas-ticket:< > value = + * cas-pgtiou:< > value = + * session:< > value = + * +failedlogins value = NSDictionary instance holding the failed count and the date of the first failed authentication + * +messagesubmissions value = NSDictionary instance holding the number of messages sent, and number of recipients */ @@ -47,6 +49,7 @@ #import #import #import +#import #import #import @@ -327,6 +330,9 @@ static memcached_st *handle = NULL; " no handle exists"), key]; } +// +// +// - (void) setValue: (NSString *) value forKey: (NSString *) key { @@ -334,6 +340,9 @@ static memcached_st *handle = NULL; expire: cleanupInterval]; } +// +// +// - (NSString *) valueForKey: (NSString *) key { NSString *valueString; @@ -372,6 +381,9 @@ static memcached_st *handle = NULL; return valueString; } +// +// +// - (void) removeValueForKey: (NSString *) key { NSData *keyData; @@ -420,6 +432,9 @@ static memcached_st *handle = NULL; } } +// +// +// - (NSString *) _valuesOfType: (NSString *) theType forKey: (NSString *) theKey { @@ -439,6 +454,9 @@ static memcached_st *handle = NULL; return valueString; } +// +// +// - (void) setUserAttributes: (NSString *) theAttributes forLogin: (NSString *) login { @@ -475,6 +493,116 @@ static memcached_st *handle = NULL; return [self _valuesOfType: @"settings" forKey: theLogin]; } +// +// SOGo password failed counts +// +- (void) setFailedCount: (int) theCount + forLogin: (NSString *) theLogin +{ + NSMutableDictionary *d; + NSNumber *count; + + if (theCount) + { + count = [NSNumber numberWithInt: theCount]; + + d = [NSMutableDictionary dictionaryWithDictionary: [self failedCountForLogin: theLogin]]; + + if (![d objectForKey: @"InitialDate"]) + { + [d setObject: [NSNumber numberWithUnsignedInt: [[NSCalendarDate date] timeIntervalSince1970]] forKey: @"InitialDate"]; + } + + [d setObject: count forKey: @"FailedCount"]; + [self _cacheValues: [d jsonRepresentation] + ofType: @"failedlogins" + forKey: theLogin]; + } + else + { + [self removeValueForKey: [NSString stringWithFormat: @"%@+failedlogins", theLogin]]; + } +} + +// +// Returns a dictionary with two keys/values +// +// FailedCount -> +// InitialDate -> +// +- (NSDictionary *) failedCountForLogin: (NSString *) theLogin +{ + NSDictionary *d; + NSString *s; + + s = [self _valuesOfType: @"failedlogins" forKey: theLogin]; + d = nil; + + if (s) + { + d = [s objectFromJSONString]; + } + + return d; +} + +// +// +// +- (void) setMessageSubmissionsCount: (int) theCount + recipientsCount: (int) theRecipientsCount + forLogin: (NSString *) theLogin +{ + NSNumber *messages_count, *recipients_count; + NSMutableDictionary *d; + + if (theCount) + { + messages_count = [NSNumber numberWithInt: theCount]; + recipients_count = [NSNumber numberWithInt: theRecipientsCount]; + + d = [NSMutableDictionary dictionaryWithDictionary: [self messageSubmissionsCountForLogin: theLogin]]; + + if (![d objectForKey: @"InitialDate"]) + { + [d setObject: [NSNumber numberWithUnsignedInt: [[NSCalendarDate date] timeIntervalSince1970]] forKey: @"InitialDate"]; + } + + [d setObject: messages_count forKey: @"MessagesCount"]; + [d setObject: recipients_count forKey: @"RecipientsCount"]; + + [self _cacheValues: [d jsonRepresentation] + ofType: @"messagesubmissions" + forKey: theLogin]; + } + else + { + [self removeValueForKey: [NSString stringWithFormat: @"%@+messagesubmissions", theLogin]]; + } +} + +// +// MessagesCount -> +// RecipientsCount -> +// InitialDate -> +// +- (NSDictionary *) messageSubmissionsCountForLogin: (NSString *) theLogin +{ + NSDictionary *d; + NSString *s; + + s = [self _valuesOfType: @"messagesubmissions" forKey: theLogin]; + d = nil; + + if (s) + { + d = [s objectFromJSONString]; + } + + return d; +} + + // // CAS session support // @@ -520,6 +648,17 @@ static memcached_st *handle = NULL; forKey: [NSString stringWithFormat: @"cas-pgtiou:%@", pgtIou]]; } +- (void) removeCASSessionWithTicket: (NSString *) ticket +{ + NSString *key, *session; + if ((session = [self CASSessionWithTicket: ticket])) + { + key = [NSString stringWithFormat: @"cas-ticket:%@", ticket]; + [self removeValueForKey: key]; + [self debugWithFormat: @"Removed session: %@", session]; + } +} + // SAML2 support - (NSDictionary *) saml2LoginDumpsForIdentifier: (NSString *) identifier { diff --git a/SoObjects/SOGo/SOGoContentObject.m b/SoObjects/SOGo/SOGoContentObject.m index 801ab2d33..b1afaa699 100644 --- a/SoObjects/SOGo/SOGoContentObject.m +++ b/SoObjects/SOGo/SOGoContentObject.m @@ -1,5 +1,5 @@ /* - Copyright (C) 2006-2010 Inverse inc. + Copyright (C) 2006-2013 Inverse inc. Copyright (C) 2004-2005 SKYRIX Software AG This file is part of SOGo. diff --git a/SoObjects/SOGo/SOGoDAVAuthenticator.m b/SoObjects/SOGo/SOGoDAVAuthenticator.m index 0755c223c..ad3bb7bce 100644 --- a/SoObjects/SOGo/SOGoDAVAuthenticator.m +++ b/SoObjects/SOGo/SOGoDAVAuthenticator.m @@ -51,7 +51,7 @@ } - (BOOL) checkLogin: (NSString *) _login - password: (NSString *) _pwd + password: (NSString *) _pwd { NSString *domain; SOGoSystemDefaults *sd; @@ -66,7 +66,7 @@ checkLogin: [_login stringByReplacingString: @"%40" withString: @"@"] password: _pwd - domain: &domain + domain: &domain perr: &perr expire: &expire grace: &grace] @@ -99,7 +99,7 @@ { creds = [self parseCredentials: auth]; if ([creds count] > 1) - password = [creds objectAtIndex: 1]; + password = [creds objectAtIndex: 1]; } return password; @@ -122,17 +122,19 @@ session = [SOGoCASSession CASSessionWithTicket: password fromProxy: YES]; - // We must NOT assume the scheme exists - scheme = [server scheme]; + // We must NOT assume the scheme exists + scheme = [server scheme]; - if (!scheme) - scheme = @"imap"; + if (!scheme) + scheme = @"imap"; - service = [NSString stringWithFormat: @"%@://%@", scheme, [server host]]; + service = [NSString stringWithFormat: @"%@://%@", scheme, [server host]]; if (renew) [session invalidateTicketForService: service]; + password = [session ticketForService: service]; + if ([password length] || renew) [session updateCache]; } diff --git a/SoObjects/SOGo/SOGoDefaults.plist b/SoObjects/SOGo/SOGoDefaults.plist index 1ff09173c..69e506d8e 100644 --- a/SoObjects/SOGo/SOGoDefaults.plist +++ b/SoObjects/SOGo/SOGoDefaults.plist @@ -26,7 +26,7 @@ SOGoMailingMechanism = "sendmail"; SOGoSMTPServer = "localhost"; - SOGoMailSpoolPath = "/tmp"; + SOGoMailSpoolPath = "/var/spool/sogo"; SOGoWebAccessEnabled = YES; SOGoCalendarDAVAccessEnabled = YES; diff --git a/SoObjects/SOGo/SOGoDomainDefaults.h b/SoObjects/SOGo/SOGoDomainDefaults.h index 557c86002..694714ae6 100644 --- a/SoObjects/SOGo/SOGoDomainDefaults.h +++ b/SoObjects/SOGo/SOGoDomainDefaults.h @@ -43,6 +43,7 @@ - (NSString *) mailDomain; - (NSString *) imapServer; - (NSString *) sieveServer; +- (NSString *) imapCASServiceName; - (NSString *) imapAclStyle; - (NSString *) imapAclGroupIdPrefix; - (NSString *) imapFolderSeparator; diff --git a/SoObjects/SOGo/SOGoDomainDefaults.m b/SoObjects/SOGo/SOGoDomainDefaults.m index 9a4747eca..98e675ddf 100644 --- a/SoObjects/SOGo/SOGoDomainDefaults.m +++ b/SoObjects/SOGo/SOGoDomainDefaults.m @@ -123,6 +123,11 @@ return [self stringForKey: @"SOGoSieveServer"]; } +- (NSString *) imapCASServiceName +{ + return [self objectForKey: @"SOGoIMAPCASServiceName"]; +} + #warning should be removed when we make use of imap namespace - (NSString *) imapAclStyle { diff --git a/SoObjects/SOGo/SOGoProxyAuthenticator.m b/SoObjects/SOGo/SOGoProxyAuthenticator.m index 08825fe98..b054512e6 100644 --- a/SoObjects/SOGo/SOGoProxyAuthenticator.m +++ b/SoObjects/SOGo/SOGoProxyAuthenticator.m @@ -1,8 +1,6 @@ /* SOGoProxyAuthenticator.h - this file is part of SOGo * - * Copyright (C) 2009-2011 Inverse inc. - * - * Author: Wolfgang Sourdeau + * Copyright (C) 2009-2013 Inverse inc. * * This file is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -32,6 +30,7 @@ #import #import "SOGoPermissions.h" +#import "SOGoSystemDefaults.h" #import "SOGoUser.h" #import "SOGoProxyAuthenticator.h" @@ -49,7 +48,7 @@ } - (BOOL) checkLogin: (NSString *) _login - password: (NSString *) _pwd + password: (NSString *) _pwd { return YES; } @@ -60,10 +59,16 @@ { NSString *remoteUser; + /* If such a header is not provided by the proxy, SOPE will attempt to deduce it from the "Authorization" header. */ remoteUser = [[context request] headerForKey: @"x-webobjects-remote-user"]; - + + if ([remoteUser length] == 0 && [[SOGoSystemDefaults sharedSystemDefaults] trustProxyAuthentication]) + { + remoteUser = @"anonymous"; + } + return remoteUser; } diff --git a/SoObjects/SOGo/SOGoPublicBaseFolder.m b/SoObjects/SOGo/SOGoPublicBaseFolder.m index 53bec7d6b..b26969a62 100644 --- a/SoObjects/SOGo/SOGoPublicBaseFolder.m +++ b/SoObjects/SOGo/SOGoPublicBaseFolder.m @@ -1,8 +1,6 @@ /* SOGoPublicBaseFolder.m - this file is part of SOGo * - * Copyright (C) 2010 Inverse inc. - * - * Author: Wolfgang Sourdeau + * Copyright (C) 2010-2013 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 diff --git a/SoObjects/SOGo/SOGoSystemDefaults.h b/SoObjects/SOGo/SOGoSystemDefaults.h index 2057f24b0..cb705f560 100644 --- a/SoObjects/SOGo/SOGoSystemDefaults.h +++ b/SoObjects/SOGo/SOGoSystemDefaults.h @@ -1,6 +1,6 @@ /* SOGoSystemDefaults.h - this file is part of SOGo * - * Copyright (C) 2009-2011 Inverse inc. + * Copyright (C) 2009-2013 Inverse inc. * * Author: Wolfgang Sourdeau * Francis Lachapelle @@ -86,6 +86,15 @@ - (BOOL) enablePublicAccess; +- (int) maximumFailedLoginCount; +- (int) maximumFailedLoginInterval; +- (int) failedLoginBlockInterval; + +- (int) maximumMessageSubmissionCount; +- (int) maximumRecipientCount; +- (int) maximumSubmissionInterval; +- (int) messageSubmissionBlockInterval; + @end #endif /* SOGOSYSTEMDEFAULTS_H */ diff --git a/SoObjects/SOGo/SOGoSystemDefaults.m b/SoObjects/SOGo/SOGoSystemDefaults.m index e724c4fb9..0457fae4b 100644 --- a/SoObjects/SOGo/SOGoSystemDefaults.m +++ b/SoObjects/SOGo/SOGoSystemDefaults.m @@ -1,6 +1,6 @@ /* SOGoSystemDefaults.m - this file is part of SOGo * - * Copyright (C) 2009-2012 Inverse inc. + * Copyright (C) 2009-2013 Inverse inc. * Copyright (C) 2012 Jeroen Dekkers * * Author: Wolfgang Sourdeau @@ -513,4 +513,73 @@ _injectConfigurationFromFile (NSMutableDictionary *defaultsDict, return [self boolForKey: @"SOGoEnablePublicAccess"]; } +// +// +// +- (int) maximumFailedLoginCount +{ + return [self integerForKey: @"SOGoMaximumFailedLoginCount"]; +} + +- (int) maximumFailedLoginInterval +{ + int v; + + v = [self integerForKey: @"SOGoMaximumFailedLoginInterval"]; + + if (!v) + v = 10; + + return v; +} + +- (int) failedLoginBlockInterval +{ + int v; + + v = [self integerForKey: @"SOGoFailedLoginBlockInterval"]; + + if (!v) + v = 300; + + return v; +} + +// +// +// +- (int) maximumMessageSubmissionCount +{ + return [self integerForKey: @"SOGoMaximumMessageSubmissionCount"]; +} + +- (int) maximumRecipientCount +{ + return [self integerForKey: @"SOGoMaximumRecipientCount"]; +} + +- (int) maximumSubmissionInterval +{ + int v; + + v = [self integerForKey: @"SOGoMaximumSubmissionInterval"]; + + if (!v) + v = 30; + + return v; +} + +- (int) messageSubmissionBlockInterval +{ + int v; + + v = [self integerForKey: @"SOGoMessageSubmissionBlockInterval"]; + + if (!v) + v = 300; + + return v; +} + @end diff --git a/SoObjects/SOGo/SOGoUser.h b/SoObjects/SOGo/SOGoUser.h index 2ed1b1cf8..e1737cd2f 100644 --- a/SoObjects/SOGo/SOGoUser.h +++ b/SoObjects/SOGo/SOGoUser.h @@ -1,5 +1,5 @@ /* - Copyright (C) 2006-2011 Inverse inc. + Copyright (C) 2006-2013 Inverse inc. Copyright (C) 2005 SKYRIX Software AG This file is part of SOGo. diff --git a/SoObjects/SOGo/SOGoUserManager.m b/SoObjects/SOGo/SOGoUserManager.m index d6a0f9a5b..03cf78e62 100644 --- a/SoObjects/SOGo/SOGoUserManager.m +++ b/SoObjects/SOGo/SOGoUserManager.m @@ -1,6 +1,6 @@ /* SOGoUserManager.m - this file is part of SOGo * - * Copyright (C) 2007-2011 Inverse inc. + * Copyright (C) 2007-2013 Inverse inc. * * Author: Wolfgang Sourdeau * Francis Lachapelle @@ -447,6 +447,9 @@ static Class NSNullK; return checkOK; } +// +// +// - (BOOL) checkLogin: (NSString *) _login password: (NSString *) _pwd domain: (NSString **) _domain @@ -463,6 +466,9 @@ static Class NSNullK; useCache: YES]; } +// +// +// - (BOOL) checkLogin: (NSString *) _login password: (NSString *) _pwd domain: (NSString **) _domain @@ -471,8 +477,9 @@ static Class NSNullK; grace: (int *) _grace useCache: (BOOL) useCache { + NSMutableDictionary *currentUser, *failedCount; NSString *dictPassword, *username, *jsonUser; - NSMutableDictionary *currentUser; + SOGoSystemDefaults *dd; BOOL checkOK; // We check for cached passwords. If the entry is cached, we @@ -482,6 +489,41 @@ static Class NSNullK; username = [NSString stringWithFormat: @"%@@%@", _login, *_domain]; else username = _login; + + failedCount = [[SOGoCache sharedCache] failedCountForLogin: username]; + dd = [SOGoSystemDefaults sharedSystemDefaults]; + + // + // We check the fail count per user in memcache (per server). If the + // fail count reaches X in Y minutes, we deny immediately the + // authentications for Z minutes + // + if (failedCount) + { + unsigned int current_time, start_time, delta, block_time; + + current_time = [[NSCalendarDate date] timeIntervalSince1970]; + start_time = [[failedCount objectForKey: @"InitialDate"] unsignedIntValue]; + delta = current_time - start_time; + + block_time = [dd failedLoginBlockInterval]; + + if ([[failedCount objectForKey: @"FailedCount"] intValue] >= [dd maximumFailedLoginCount] && + delta >= [dd maximumFailedLoginInterval] && + delta <= block_time ) + { + *_perr = PolicyAccountLocked; + return NO; + } + + if (delta > block_time) + { + [[SOGoCache sharedCache] setFailedCount: 0 + forLogin: username]; + } + } + + jsonUser = [[SOGoCache sharedCache] userAttributesForLogin: username]; currentUser = [jsonUser objectFromJSONString]; dictPassword = [currentUser objectForKey: @"password"]; @@ -514,7 +556,16 @@ static Class NSNullK; forLogin: username]; } else - checkOK = NO; + { + // If failed login "rate-limiting" is enabled, we adjust the stats + if ([dd maximumFailedLoginCount]) + { + [[SOGoCache sharedCache] setFailedCount: ([[failedCount objectForKey: @"FailedCount"] intValue] + 1) + forLogin: username]; + } + + checkOK = NO; + } // We MUST, for all LDAP sources, update the bindDN and bindPassword // to the user's value if bindAsCurrentUser is set to true in the @@ -538,6 +589,9 @@ static Class NSNullK; return checkOK; } +// +// +// - (BOOL) changePasswordForLogin: (NSString *) login inDomain: (NSString *) domain oldPassword: (NSString *) oldPassword diff --git a/SoObjects/SOGo/SOGoWebAuthenticator.m b/SoObjects/SOGo/SOGoWebAuthenticator.m index 639ca4ec0..923ee7d15 100644 --- a/SoObjects/SOGo/SOGoWebAuthenticator.m +++ b/SoObjects/SOGo/SOGoWebAuthenticator.m @@ -74,7 +74,7 @@ } - (BOOL) checkLogin: (NSString *) _login - password: (NSString *) _pwd + password: (NSString *) _pwd { NSString *username, *password, *domain, *value; SOGoPasswordPolicyError perr; @@ -168,7 +168,7 @@ perr: _perr expire: _expire grace: _grace - useCache: _useCache]; + useCache: _useCache]; //[self logWithFormat: @"Checked login with ppolicy enabled: %d %d %d", *_perr, *_expire, *_grace]; @@ -269,31 +269,42 @@ forceRenew: (BOOL) renew { NSString *authType, *password; + SOGoSystemDefaults *sd; password = [self passwordInContext: context]; if ([password length]) { - authType = [[SOGoSystemDefaults sharedSystemDefaults] - authenticationType]; + sd = [SOGoSystemDefaults sharedSystemDefaults]; + authType = [sd authenticationType]; if ([authType isEqualToString: @"cas"]) { SOGoCASSession *session; + SOGoUser *user; NSString *service, *scheme; session = [SOGoCASSession CASSessionWithIdentifier: password fromProxy: NO]; - // We must NOT assume the scheme exists - scheme = [server scheme]; + user = [self userInContext: context]; + // Try configured CAS service name first + service = [[user domainDefaults] imapCASServiceName]; + if (!service) + { + // We must NOT assume the scheme exists + scheme = [server scheme]; - if (!scheme) - scheme = @"imap"; + if (!scheme) + scheme = @"imap"; - service = [NSString stringWithFormat: @"%@://%@", scheme, [server host]]; + service = [NSString stringWithFormat: @"%@://%@", + scheme, [server host]]; + } if (renew) [session invalidateTicketForService: service]; + password = [session ticketForService: service]; + if ([password length] || renew) [session updateCache]; } @@ -322,8 +333,8 @@ /* create SOGoUser */ - (SOGoUser *) userWithLogin: (NSString *) login - andRoles: (NSArray *) roles - inContext: (WOContext *) ctx + andRoles: (NSArray *) roles + inContext: (WOContext *) ctx { /* the actual factory method */ return [SOGoUser userWithLogin: login roles: roles]; @@ -339,7 +350,7 @@ NSString *auth; auth = [[context request] - cookieValueForKey: [self cookieNameInContext:context]]; + cookieValueForKey: [self cookieNameInContext:context]]; if ([auth isEqualToString: @"discard"]) { [context setObject: [NSArray arrayWithObject: SoRole_Anonymous] @@ -353,8 +364,8 @@ } - (void) setupAuthFailResponse: (WOResponse *) response - withReason: (NSString *) reason - inContext: (WOContext *) context + withReason: (NSString *) reason + inContext: (WOContext *) context { WOComponent *page; WORequest *request; diff --git a/Tests/README-cas-logoutRequest b/Tests/README-cas-logoutRequest new file mode 100644 index 000000000..c9b9c9eab --- /dev/null +++ b/Tests/README-cas-logoutRequest @@ -0,0 +1,23 @@ +Sample HTTP request to exercise the CAS handleLogoutRequest code. +Can be fed to sogo using nc or openssl s_client: + nc devsogo 80 + * Copyright (C) 2007-2013 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 diff --git a/UI/Common/UIxFolderActions.m b/UI/Common/UIxFolderActions.m index 76a2bbe37..a527699ec 100644 --- a/UI/Common/UIxFolderActions.m +++ b/UI/Common/UIxFolderActions.m @@ -1,8 +1,6 @@ /* UIxFolderActions.m - this file is part of SOGo * - * Copyright (C) 2007-2010 Inverse inc. - * - * Author: Wolfgang Sourdeau + * Copyright (C) 2007-2013 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 diff --git a/UI/Contacts/UIxContactFolderActions.m b/UI/Contacts/UIxContactFolderActions.m index ea416bc3a..887ce3b7b 100644 --- a/UI/Contacts/UIxContactFolderActions.m +++ b/UI/Contacts/UIxContactFolderActions.m @@ -1,6 +1,6 @@ /* Copyright (C) 2004-2005 SKYRIX Software AG - Copyright (C) 2006-2012 Inverse inc. + Copyright (C) 2006-2013 Inverse inc. This file is part of SOGo diff --git a/UI/Contacts/UIxContactFoldersView.m b/UI/Contacts/UIxContactFoldersView.m index ecfc542cb..f38a3532f 100644 --- a/UI/Contacts/UIxContactFoldersView.m +++ b/UI/Contacts/UIxContactFoldersView.m @@ -1,8 +1,6 @@ /* UIxContactFoldersView.m - this file is part of SOGo * - * Copyright (C) 2006-2010 Inverse inc. - * - * Author: Wolfgang Sourdeau + * Copyright (C) 2006-2013 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 diff --git a/UI/Contacts/product.plist b/UI/Contacts/product.plist index dbec6108a..a2e2847ae 100644 --- a/UI/Contacts/product.plist +++ b/UI/Contacts/product.plist @@ -105,7 +105,7 @@ actionName = "export"; }; import = { - protectedBy = "View"; + protectedBy = "Add Documents, Images, and Files"; actionClass = "UIxContactFolderActions"; actionName = "import"; }; diff --git a/UI/MailPartViewers/UIxMailPartHTMLViewer.m b/UI/MailPartViewers/UIxMailPartHTMLViewer.m index 29d425a03..aa2176d06 100644 --- a/UI/MailPartViewers/UIxMailPartHTMLViewer.m +++ b/UI/MailPartViewers/UIxMailPartHTMLViewer.m @@ -158,16 +158,16 @@ static NSData* _sanitizeContent(NSData *theData) { // We check if we see in which case, we don't do any kind // of substitution there after. - if (i < len-5) + if (i < len-6) { if ((*bytes == '<') && (*(bytes+1) == '/') && - (*(bytes+1) == 'h' || *(bytes+1) == 'H') && - (*(bytes+2) == 'e' || *(bytes+2) == 'E') && - (*(bytes+3) == 'a' || *(bytes+3) == 'A') && - (*(bytes+4) == 'd' || *(bytes+4) == 'D') && - (*(bytes+7) == '>')) - break; + (*(bytes+2) == 'h' || *(bytes+2) == 'H') && + (*(bytes+3) == 'e' || *(bytes+3) == 'E') && + (*(bytes+4) == 'a' || *(bytes+4) == 'A') && + (*(bytes+5) == 'd' || *(bytes+5) == 'D') && + (*(bytes+6) == '>')) + break; } // We search for something like : @@ -189,7 +189,7 @@ static NSData* _sanitizeContent(NSData *theData) j = 8; found_delimiter = YES; - while (*(bytes+j) != ' ' && *(bytes+j) != '"') + while (*(bytes+j) != ' ' && *(bytes+j) != '"' && *(bytes+j) != '\'') { j++; diff --git a/UI/MailerUI/UIxMailEditor.m b/UI/MailerUI/UIxMailEditor.m index 6cc5c5819..0c695bcc8 100644 --- a/UI/MailerUI/UIxMailEditor.m +++ b/UI/MailerUI/UIxMailEditor.m @@ -46,6 +46,8 @@ #import #import +#import +#import #import #import #import @@ -659,12 +661,57 @@ static NSArray *infoKeys = nil; return error; } +// +// +// - (WOResponse *) sendAction { SOGoDraftObject *co; NSDictionary *jsonResponse; NSException *error; NSMutableArray *errorMsg; + NSDictionary *messageSubmissions; + SOGoSystemDefaults *dd; + + int messages_count, recipients_count; + + messageSubmissions = [[SOGoCache sharedCache] messageSubmissionsCountForLogin: [[context activeUser] login]]; + dd = [SOGoSystemDefaults sharedSystemDefaults]; + messages_count = recipients_count = 0; + + if (messageSubmissions) + { + unsigned int current_time, start_time, delta, block_time; + + current_time = [[NSCalendarDate date] timeIntervalSince1970]; + start_time = [[messageSubmissions objectForKey: @"InitialDate"] unsignedIntValue]; + delta = current_time - start_time; + + block_time = [dd messageSubmissionBlockInterval]; + messages_count = [[messageSubmissions objectForKey: @"MessagesCount"] intValue]; + recipients_count = [[messageSubmissions objectForKey: @"RecipientsCount"] intValue]; + + if ((messages_count >= [dd maximumMessageSubmissionCount] || recipients_count >= [dd maximumRecipientCount]) && + delta >= [dd maximumSubmissionInterval] && + delta <= block_time ) + { + jsonResponse = [NSDictionary dictionaryWithObjectsAndKeys: + @"failure", @"status", + [self labelForKey: @"Tried to send too many mails. Please wait."], + @"message", + nil]; + return [self responseWithStatus: 200 + andString: [jsonResponse jsonRepresentation]]; + } + + if (delta > block_time) + { + [[SOGoCache sharedCache] setMessageSubmissionsCount: 0 + recipientsCount: 0 + forLogin: [[context activeUser] login]]; + } + + } co = [self clientObject]; @@ -691,11 +738,23 @@ static NSArray *infoKeys = nil; nil]; } else - jsonResponse = [NSDictionary dictionaryWithObjectsAndKeys: - @"success", @"status", - [co sourceFolder], @"sourceFolder", - [NSNumber numberWithInt: [co IMAP4ID]], @"messageID", - nil]; + { + jsonResponse = [NSDictionary dictionaryWithObjectsAndKeys: + @"success", @"status", + [co sourceFolder], @"sourceFolder", + [NSNumber numberWithInt: [co IMAP4ID]], @"messageID", + nil]; + + recipients_count += [[co allRecipients] count]; + messages_count += 1; + + if ([dd maximumMessageSubmissionCount] > 0 && [dd maximumRecipientCount] > 0) + { + [[SOGoCache sharedCache] setMessageSubmissionsCount: messages_count + recipientsCount: recipients_count + forLogin: [[context activeUser] login]]; + } + } return [self responseWithStatus: 200 andString: [jsonResponse jsonRepresentation]]; diff --git a/UI/MailerUI/UIxMailUserRightsEditor.h b/UI/MailerUI/UIxMailUserRightsEditor.h index ef4633fca..f2e7126ac 100644 --- a/UI/MailerUI/UIxMailUserRightsEditor.h +++ b/UI/MailerUI/UIxMailUserRightsEditor.h @@ -1,8 +1,6 @@ /* UIxMailUserRightsEditor.h - this file is part of SOGo * - * Copyright (C) 2007 Inverse inc. - * - * Author: Wolfgang Sourdeau + * Copyright (C) 2007-2013 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 diff --git a/UI/MailerUI/UIxMailUserRightsEditor.m b/UI/MailerUI/UIxMailUserRightsEditor.m index bdd7b55e7..7bab14031 100644 --- a/UI/MailerUI/UIxMailUserRightsEditor.m +++ b/UI/MailerUI/UIxMailUserRightsEditor.m @@ -1,8 +1,6 @@ /* UIxMailUserRightsEditor.m - this file is part of SOGo * - * Copyright (C) 2007 Inverse inc. - * - * Author: Wolfgang Sourdeau + * Copyright (C) 2007-2013 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 diff --git a/UI/MailerUI/UIxMailView.m b/UI/MailerUI/UIxMailView.m index d621aa353..e714bf6ab 100644 --- a/UI/MailerUI/UIxMailView.m +++ b/UI/MailerUI/UIxMailView.m @@ -1,14 +1,15 @@ /* Copyright (C) 2004-2005 SKYRIX Software AG + Copyright (C) 2005-2013 Inverse inc. - This file is part of OpenGroupware.org. + This file is part of SOGo. - OGo is free software; you can redistribute it and/or modify it under + SOGo is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2, or (at your option) any later version. - OGo is distributed in the hope that it will be useful, but WITHOUT ANY + SOGo is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. @@ -30,6 +31,8 @@ #import #import +#import +#import #import #import @@ -216,7 +219,7 @@ static NSString *mailETag = nil; { identityEmail = [[identities objectAtIndex: count] objectForKey: @"email"]; - rc = [identityEmail isEqualToString: email]; + rc = ([identityEmail caseInsensitiveCompare: email] == NSOrderedSame); } return rc; @@ -489,6 +492,7 @@ static NSString *mailETag = nil; - (NSString *) shouldAskReceipt { + NGMailAddress *mailAddress; NSDictionary *mailHeaders; NSString *email, *action; @@ -504,6 +508,15 @@ static NSString *mailETag = nil; email = [mailHeaders objectForKey: @"return-receipt-to"]; } + // email here can be "foo@bar.com" or "Foo Bar " + // we must extract the actual email address + mailAddress = [[NGMailAddressParser mailAddressParserWithString: email] parse]; + + if ([mailAddress isKindOfClass: [NGMailAddress class]]) + email = [mailAddress address]; + else + email = nil; + if (email) { if (![self _userHasEMail: email] diff --git a/UI/MainUI/SOGoRootPage.m b/UI/MainUI/SOGoRootPage.m index fb349ef61..5f10391f4 100644 --- a/UI/MainUI/SOGoRootPage.m +++ b/UI/MainUI/SOGoRootPage.m @@ -172,6 +172,9 @@ andJSONRepresentation: jsonError]; } +// +// +// - (id ) connectAction { WOResponse *response; @@ -269,10 +272,13 @@ { NSDictionary *redirectKeys; NSURL *soURL; + NSString *serviceURL; soURL = [[WOApplication application] soURL]; + // appending 'index' to /SOGo/so/. Matches serviceURL sent by _pgtUrlFromURL + serviceURL = [NSString stringWithFormat: @"%@index", [soURL absoluteString]]; - redirectKeys = [NSDictionary dictionaryWithObject: [soURL absoluteString] + redirectKeys = [NSDictionary dictionaryWithObject: serviceURL forKey: @"service"]; return redirectKeys; @@ -299,7 +305,7 @@ - (id ) _casDefaultAction { WOResponse *response; - NSString *login, *newLocation, *oldLocation, *ticket; + NSString *login, *logoutRequest, *newLocation, *oldLocation, *ticket; SOGoCASSession *casSession; SOGoWebAuthenticator *auth; WOCookie *casCookie, *casLocationCookie; @@ -337,6 +343,18 @@ withName: @"cas-location"]; } } + else + { + /* anonymous and no ticket, possibly a logout request from CAS + * See: https://wiki.jasig.org/display/CASUM/Single+Sign+Out + */ + logoutRequest = [rq formValueForKey: @"logoutRequest"]; + if ([logoutRequest length]) + { + [SOGoCASSession handleLogoutRequest: logoutRequest]; + return [self responseWithStatus: 200]; + } + } } else ticket = nil; diff --git a/UI/Scheduler/English.lproj/Localizable.strings b/UI/Scheduler/English.lproj/Localizable.strings index 7f6188e55..6378e645e 100644 --- a/UI/Scheduler/English.lproj/Localizable.strings +++ b/UI/Scheduler/English.lproj/Localizable.strings @@ -179,6 +179,8 @@ "Reminder:" = "Reminder:"; "General:" = "General:"; "Reply:" = "Reply:"; +"Created by:" = "Created by:"; + "Target:" = "Target:"; @@ -373,6 +375,9 @@ "Show Time as Free" = "Show Time as Free"; +/* email notifications */ +"Send Appointment Notifications" = "Send Appointment Notifications"; + /* validation errors */ validate_notitle = "No title is set, continue?"; diff --git a/UI/Scheduler/French.lproj/Localizable.strings b/UI/Scheduler/French.lproj/Localizable.strings index 6414e1f80..cb81c3d21 100644 --- a/UI/Scheduler/French.lproj/Localizable.strings +++ b/UI/Scheduler/French.lproj/Localizable.strings @@ -179,6 +179,8 @@ "Reminder:" = "Rappel :"; "General:" = "Général:"; "Reply:" = "Réponse:"; +"Created by:" = "Créé par:"; + "Target:" = "Destination :"; @@ -373,6 +375,9 @@ "Show Time as Free" = "Affiché comme disponible"; +/* email notifications */ +"Send Appointment Notifications" = "Envoyer les notifications des événements"; + /* validation errors */ validate_notitle = "Le titre n'est pas rempli. Continuer quand-même ?"; diff --git a/UI/Scheduler/UIxAppointmentEditor.h b/UI/Scheduler/UIxAppointmentEditor.h index f3482ce1d..daa0193f2 100644 --- a/UI/Scheduler/UIxAppointmentEditor.h +++ b/UI/Scheduler/UIxAppointmentEditor.h @@ -1,8 +1,6 @@ /* UIxAppointmentEditor.h - this file is part of SOGo * - * Copyright (C) 2007-2012 Inverse inc. - * - * Author: Wolfgang Sourdeau + * Copyright (C) 2007-2013 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 @@ -31,7 +29,7 @@ @interface UIxAppointmentEditor : UIxComponent { iCalEvent *event; - BOOL isAllDay, isTransparent; + BOOL isAllDay, isTransparent, sendAppointmentNotifications; NSCalendarDate *aptStartDate; NSCalendarDate *aptEndDate; NSString *item; @@ -50,6 +48,9 @@ - (void) setIsTransparent: (BOOL) newIsOpaque; - (BOOL) isTransparent; +- (void) setSendAppointmentNotifications: (BOOL) theBOOL; +- (BOOL) sendAppointmentNotifications; + - (void) setAptStartDate: (NSCalendarDate *) newAptStartDate; - (NSCalendarDate *) aptStartDate; diff --git a/UI/Scheduler/UIxAppointmentEditor.m b/UI/Scheduler/UIxAppointmentEditor.m index 98609a034..3c4b92b76 100644 --- a/UI/Scheduler/UIxAppointmentEditor.m +++ b/UI/Scheduler/UIxAppointmentEditor.m @@ -2,9 +2,6 @@ * * Copyright (C) 2007-2013 Inverse inc. * - * Author: Wolfgang Sourdeau - * Francis Lachapelle - * * 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) @@ -51,6 +48,7 @@ #import #import #import +#import #import #import #import @@ -75,6 +73,7 @@ event = nil; isAllDay = NO; isTransparent = NO; + sendAppointmentNotifications = YES; componentCalendar = nil; user = [[self context] activeUser]; @@ -139,6 +138,17 @@ isTransparent = newIsTransparent; } +- (void) setSendAppointmentNotifications: (BOOL) theBOOL +{ + sendAppointmentNotifications = theBOOL; +} + +- (BOOL) sendAppointmentNotifications +{ + return sendAppointmentNotifications; +} + + - (void) setAptStartDate: (NSCalendarDate *) newAptStartDate { ASSIGN (aptStartDate, newAptStartDate); @@ -279,6 +289,7 @@ endDate = [startDate dateByAddingYears: 0 months: 0 days: 0 hours: hours minutes: minutes seconds: 0]; + sendAppointmentNotifications = YES; } else { @@ -296,7 +307,8 @@ endDate = [endDate dateByAddingYears:0 months:0 days:0 hours:0 minutes:0 seconds:-offset]; } - isTransparent = ![event isOpaque]; + isTransparent = ![event isOpaque]; + sendAppointmentNotifications = ([event firstChildWithTag: @"X-SOGo-Send-Appointment-Notifications"] ? NO : YES); } [startDate setTimeZone: timeZone]; @@ -456,6 +468,8 @@ NSTimeZone *timeZone; SOGoUserDefaults *ud; SOGoCalendarComponent *co; + NSString *created_by; + BOOL resetAlarm; unsigned int snoozeAlarm; @@ -501,6 +515,8 @@ } } + created_by = [event createdBy]; + data = [NSDictionary dictionaryWithObjectsAndKeys: [componentCalendar displayName], @"calendar", [event tag], @"component", @@ -512,6 +528,7 @@ ([event isAllDay] ? @"1": @"0"), @"isAllDay", [event summary], @"summary", [event location], @"location", + created_by, @"created_by", [event comment], @"description", nil]; @@ -541,6 +558,7 @@ NSTimeZone *timeZone; SOGoUserDefaults *ud; signed int offset; + id o; [self event]; [super takeValuesFromRequest: _rq inContext: _ctx]; @@ -594,6 +612,14 @@ } [event setTransparency: (isTransparent? @"TRANSPARENT" : @"OPAQUE")]; + + o = [event firstChildWithTag: @"X-SOGo-Send-Appointment-Notifications"]; + + if (!sendAppointmentNotifications && !o) + [event addChild: [CardElement simpleElementWithTag: @"X-SOGo-Send-Appointment-Notifications" value: @"NO"]]; + else if (sendAppointmentNotifications && o) + [event removeChild: o]; + } - (id) _statusChangeAction: (NSString *) newStatus diff --git a/UI/Scheduler/UIxCalFolderActions.h b/UI/Scheduler/UIxCalFolderActions.h index 3a7645429..a7c3c146a 100644 --- a/UI/Scheduler/UIxCalFolderActions.h +++ b/UI/Scheduler/UIxCalFolderActions.h @@ -1,5 +1,5 @@ /* - Copyright (C) 2006-2012 Inverse inc. + Copyright (C) 2006-2013 Inverse inc. Copyright (C) 2004-2005 SKYRIX Software AG This file is part of SOGo diff --git a/UI/Scheduler/UIxCalFolderActions.m b/UI/Scheduler/UIxCalFolderActions.m index 862e08496..0a676472f 100644 --- a/UI/Scheduler/UIxCalFolderActions.m +++ b/UI/Scheduler/UIxCalFolderActions.m @@ -1,5 +1,5 @@ /* - Copyright (C) 2006-2012 Inverse inc. + Copyright (C) 2006-2013 Inverse inc. Copyright (C) 2004-2005 SKYRIX Software AG This file is part of SOGo diff --git a/UI/Scheduler/UIxCalMainActions.h b/UI/Scheduler/UIxCalMainActions.h index d133e7fdf..36ba4fbf3 100644 --- a/UI/Scheduler/UIxCalMainActions.h +++ b/UI/Scheduler/UIxCalMainActions.h @@ -1,8 +1,6 @@ /* UIxCalMainActions.h - this file is part of SOGo * - * Copyright (C) 2009 Inverse inc. - * - * Author: Cyril Robert + * Copyright (C) 2009-2013 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 diff --git a/UI/Scheduler/UIxCalMainActions.m b/UI/Scheduler/UIxCalMainActions.m index 07c16e7d2..e416d221c 100644 --- a/UI/Scheduler/UIxCalMainActions.m +++ b/UI/Scheduler/UIxCalMainActions.m @@ -1,8 +1,6 @@ /* UIxCalMainActions.m - this file is part of SOGo * - * Copyright (C) 2009 Inverse inc. - * - * Author: Cyril Robert + * Copyright (C) 2009-2013 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 diff --git a/UI/Scheduler/UIxComponentEditor.h b/UI/Scheduler/UIxComponentEditor.h index 4e292b8ea..ed7b38d5a 100644 --- a/UI/Scheduler/UIxComponentEditor.h +++ b/UI/Scheduler/UIxComponentEditor.h @@ -143,6 +143,10 @@ - (NSString *) attach; - (BOOL) hasAttendees; +- (BOOL) hasCreatedBy; +- (NSString *) createdBy; +- (NSString *) createdByLink; +- (NSString *) createdByName; - (NSString *) jsonAttendees; diff --git a/UI/Scheduler/UIxComponentEditor.m b/UI/Scheduler/UIxComponentEditor.m index 07728523c..398c2cd15 100644 --- a/UI/Scheduler/UIxComponentEditor.m +++ b/UI/Scheduler/UIxComponentEditor.m @@ -834,6 +834,41 @@ iRANGE(2); return organizerProfile; } + +- (BOOL) hasCreatedBy +{ + return ([[[component firstChildWithTag: @"X-SOGo-Component-Created-By"] flattenedValuesForKey: @""] length] > 0); +} + +- (NSString *) createdBy +{ + return [[component firstChildWithTag: @"X-SOGo-Component-Created-By"] flattenedValuesForKey: @""]; +} + +- (NSString *) createdByLink +{ + return [NSString stringWithFormat: @"mailto:%@", + [[component firstChildWithTag: @"X-SOGo-Component-Created-By"] flattenedValuesForKey: @""]]; +} + +- (NSString *) createdByName +{ + NSString *login; + SOGoUser *user; + + login = [[SOGoUserManager sharedUserManager] getUIDForEmail: [self createdBy]]; + + if (login) + { + user = [SOGoUser userWithLogin: login]; + + if (user) + return [user cn]; + } + + return @""; +} + - (NSString *) organizerName { NSDictionary *profile; @@ -1834,7 +1869,7 @@ RANGE(2); - (void) _handleOrganizer { - NSString *owner, *login; + NSString *owner, *login, *currentEmail; BOOL isOwner, hasAttendees; //owner = [[self clientObject] ownerInContext: context]; @@ -1842,8 +1877,8 @@ RANGE(2); login = [[context activeUser] login]; isOwner = [owner isEqualToString: login]; hasAttendees = [self hasAttendees]; + currentEmail = [[[context activeUser] allEmails] objectAtIndex: 0]; -#if 1 if (hasAttendees) { SOGoUser *user; @@ -1859,9 +1894,8 @@ RANGE(2); if (!isOwner) { - NSString *currentEmail, *quotedEmail; + NSString *quotedEmail; - currentEmail = [[[context activeUser] allEmails] objectAtIndex: 0]; quotedEmail = [NSString stringWithFormat: @"\"MAILTO:%@\"", currentEmail]; [organizer setValue: 0 ofAttribute: @"SENT-BY" @@ -1873,30 +1907,17 @@ RANGE(2); organizer = nil; } [component setOrganizer: organizer]; -#else - NSString *organizerEmail; - BOOL hasOrganizer; - organizerEmail = [[component organizer] email]; - hasOrganizer = ([organizerEmail length] > 0); - if (hasOrganizer) + + // In case of a new component, if the current user isn't the owner of the calendar, we + // add the "X-SOGo-Component-Created-By: " attribute + if ([[self clientObject] isNew] && + !isOwner && + [currentEmail length]) { - if (isOwner && !hasAttendees) - { - ASSIGN (organizer, [iCalPerson elementWithTag: @"organizer"]); - [component setOrganizer: organizer]; - } + [component addChild: [CardElement simpleElementWithTag: @"X-SOGo-Component-Created-By" + value: currentEmail]]; } - else - { - if (!isOwner || hasAttendees) - { - ASSIGN (organizer, [iCalPerson elementWithTag: @"organizer"]); - [organizer setCn: [organizerIdentity objectForKey: @"fullName"]]; - [organizer setEmail: [organizerIdentity objectForKey: @"email"]]; - [component setOrganizer: organizer]; - } - } -#endif + } - (void) _handleCustomRRule: (iCalRecurrenceRule *) theRule diff --git a/UI/Scheduler/product.plist b/UI/Scheduler/product.plist index 128fe8117..c433d5f84 100644 --- a/UI/Scheduler/product.plist +++ b/UI/Scheduler/product.plist @@ -150,7 +150,7 @@ actionName = "redirectForUIDs"; }; import = { - protectedBy = "View"; + protectedBy = "Add Documents, Images, and Files"; actionClass = "UIxCalFolderActions"; actionName = "import"; }; diff --git a/UI/Templates/SchedulerUI/UIxAppointmentEditor.wox b/UI/Templates/SchedulerUI/UIxAppointmentEditor.wox index 8550b4487..607b32a38 100644 --- a/UI/Templates/SchedulerUI/UIxAppointmentEditor.wox +++ b/UI/Templates/SchedulerUI/UIxAppointmentEditor.wox @@ -53,6 +53,13 @@ var:checked="isTransparent" /> + + diff --git a/UI/Templates/SchedulerUI/UIxCalMainView.wox b/UI/Templates/SchedulerUI/UIxCalMainView.wox index 8b5e2e556..2b34302c6 100644 --- a/UI/Templates/SchedulerUI/UIxCalMainView.wox +++ b/UI/Templates/SchedulerUI/UIxCalMainView.wox @@ -100,6 +100,7 @@

+

diff --git a/UI/Templates/SchedulerUI/UIxComponentEditor.wox b/UI/Templates/SchedulerUI/UIxComponentEditor.wox index 586f86150..45af34b81 100644 --- a/UI/Templates/SchedulerUI/UIxComponentEditor.wox +++ b/UI/Templates/SchedulerUI/UIxComponentEditor.wox @@ -73,6 +73,12 @@ >
+ +