Projects
home:sicherha:branches:Kolab:16
kolab-syncroton
Log In
Username
Password
Problem getting expanded diff: bad link: conflict in file debian.control
×
Overview
Repositories
Revisions
Requests
Users
Attributes
Meta
Expand all
Collapse all
Changes of Revision 3
View file
kolab-syncroton.spec
Changed
@@ -34,9 +34,10 @@ %endif %global _ap_sysconfdir %{_sysconfdir}/%{httpd_name} +%global upstream_version 2.4.2 Name: kolab-syncroton -Version: 2.3.10 +Version: 2.4.2.10 Release: 1%{?dist} Summary: ActiveSync for Kolab Groupware @@ -44,55 +45,46 @@ License: LGPLv2 URL: http://www.syncroton.org -Source0: https://mirror.kolabenterprise.com/pub/releases/%{name}-%{version}.tar.gz +Source0: %{name}-%{upstream_version}.tar.gz Source1: kolab-syncroton.logrotate +Source2: plesk.kolab_syncroton.inc.php BuildArch: noarch -# Use this build requirement to make sure we are using -# up to date vendorized copies of the plugins. -%if 0%{?plesk} < 1 -BuildRequires: roundcubemail-plugin-kolab_auth >= 3.2 -%endif -BuildRequires: roundcubemail-plugin-kolab_folders >= 3.2 -BuildRequires: roundcubemail-plugin-libcalendaring >= 3.2 -BuildRequires: roundcubemail-plugin-libkolab >= 3.2 - +%if 0%{?rhel} < 8 %if 0%{?suse_version} BuildRequires: roundcubemail Requires: php Requires: php-pear-Auth_SASL Requires: php-pear-MDB2_Driver_mysqli -Requires: php-pear-Net_IDNA2 Requires: php-pear-Net_SMTP Requires: php-pear-Net_Socket %else +BuildRequires: roundcubemail >= 1.5 Requires: php-common >= 5.3 Requires: php-pear-Auth-SASL Requires: php-pear-MDB2-Driver-mysqli -Requires: php-pear-Net-IDNA2 Requires: php-pear-Net-SMTP Requires: php-pear-Net-Socket %endif +%endif +Requires: roundcubemail Requires: logrotate -Requires: roundcubemail(core) -%if 0%{?plesk} < 1 -Requires: roundcubemail-plugin-kolab_auth >= 3.2 -%endif -Requires: roundcubemail-plugin-kolab_folders >= 3.2 -Requires: roundcubemail-plugin-libcalendaring >= 3.2 -Requires: roundcubemail-plugin-libkolab >= 3.2 +%if 0%{?rhel} < 8 Requires: php-kolabformat Requires: php-pear-MDB2 Requires: php-ZendFramework +%endif + %description Kolab Groupware provides ActiveSync for Calendars, Address Books and Tasks though this package - based on Syncroton technology. %prep -%setup -q -n %{name}-%{version} +%setup -q -n %{name}-%{upstream_version} +#%patch0 -p1 %build @@ -102,7 +94,7 @@ %if 0%{?plesk} < 1 %{buildroot}/%{_ap_sysconfdir}/conf.d/ \ %endif - %{buildroot}/%{_sysconfdir}/%{name} \ + %{buildroot}/%{_sysconfdir}/roundcubemail/ \ %{buildroot}/%{_var}/log/%{name} mkdir -p %{buildroot}%{_sysconfdir}/logrotate.d @@ -115,6 +107,12 @@ cp -a lib %{buildroot}/%{_datadir}/%{name}/. cp -a index.php %{buildroot}/%{_datadir}/%{name}/. +%if 0%{?plesk} +cp -a %SOURCE2 %{buildroot}/%{_sysconfdir}/roundcubemail/kolab_syncroton.inc.php +%else +cp -a config/config.inc.php.dist %{buildroot}/%{_sysconfdir}/roundcubemail/kolab_syncroton.inc.php +%endif + pushd %{buildroot}/%{_datadir}/%{name} ln -s ../../..%{_sysconfdir}/roundcubemail config ln -s ../../..%{_var}/log/%{name} logs @@ -174,8 +172,12 @@ fi fi +if -f "/usr/lib/systemd/system/php-fpm.service" ; then + /bin/systemctl reload php-fpm.service || : +fi + /usr/share/roundcubemail/bin/updatedb.sh \ - --dir /usr/share/doc/kolab-syncroton-%{version}/SQL/ \ + --dir /usr/share/doc/kolab-syncroton/SQL/ \ --package syncroton \ >/dev/null 2>&1 || : @@ -191,10 +193,53 @@ %config(noreplace) %{_ap_sysconfdir}/conf.d/kolab-syncroton.conf %endif %config(noreplace) %{_sysconfdir}/logrotate.d/%{name} +%config(noreplace) %{_sysconfdir}/roundcubemail/kolab_syncroton.inc.php %{_datadir}/%{name} %attr(0770,%{httpd_user},%{httpd_group}) %{_var}/log/%{name} %changelog +* Thu Jul 27 2023 Christian Mollekopf <mollekopf@apheleia-it.ch> - 2.4.2-1 +- Release version 2.4.2 + +* Mon Jul 17 2023 Christian Mollekopf <mollekopf@apheleia-it.ch> - 2.4.1-1 +- Release version 2.4.1 + +* Wed May 10 2023 Christian Mollekopf <mollekopf@apheleia-it.ch> - 2.4.0-1 +- Release version 2.4.0 + +* Fri Feb 04 2022 Jeroen van Meeuwen <vanmeeuwen@apheleia-it.ch> - 2.3.22-1 +- Release version 2.3.22 + +* Fri Jul 02 2021 Jeroen van Meeuwen <vanmeeuwen@apheleia-it.ch> - 2.3.21-1 +- Release version 2.3.21 + +* Thu Jan 28 2021 Christian Mollekopf <mollekopf@kolabsys.com> - 2.3.19-1 +- Release version 2.3.19 + +* Mon May 4 2020 Christian Mollekopf <mollekopf@kolabsys.com> - 2.3.17-1 +- Release version 2.3.17 + +* Wed Dec 4 2019 Jeroen van Meeuwen <vanmeeuwen@kolabsys.com> - 2.3.16-1 +- Release version 2.3.16 + +* Mon Jul 29 2019 Jeroen van Meeuwen (Kolab Systems) <vanmeeuwen@kolabsys.com> - 2.3.15-3 +- Fix MeetingResponse for Calendar events + +* Thu Apr 11 2019 Jeroen van Meeuwen (Kolab Systems) <vanmeeuwen@kolabsys.com> - 2.3.15-2 +- Update defaults + +* Fri Feb 1 2019 Jeroen van Meeuwen (Kolab Systems) <vanmeeuwen@kolabsys.com> - 2.3.15-1 +- Release 2.3.15 + +* Thu Dec 27 2018 Jeroen van Meeuwen (Kolab Systems) <vanmeeuwen@kolabsys.com> - 2.3.14-1 +- Release 2.3.14 + +* Fri Aug 17 2018 Jeroen van Meeuwen (Kolab Systems) <vanmeeuwen@kolabsys.com> - 2.3.13-1 +- Release 2.3.13 + +* Wed Jun 13 2018 Jeroen van Meeuwen (Kolab Systems) <vanmeeuwen@kolabsys.com> - 2.3.12-1 +- Release 2.3.12 + * Thu Mar 8 2018 Jeroen van Meeuwen (Kolab Systems) <vanmeeuwen@kolabsys.com> - 2.3.10-1 - Release 2.3.10
View file
_link
Changed
@@ -1,4 +1,4 @@ -<link project="Kolab:16" baserev="dd538f7b82bd9c7a957c830645c5ea1b"> +<link project="Kolab:16" baserev="cda5fd56ba5f683501ff6dda3d784c7c"> <patches> <branch/> </patches>
View file
buildtarball.sh
Added
@@ -0,0 +1,22 @@ +#!/bin/bash + +set -e + +VERSION=2.4.2 +GIT_REF=master +NAME=kolab-syncroton-$VERSION + +ROOT_DIR=$(pwd) + +rm -Rf /tmp/$NAME +mkdir /tmp/$NAME +cd /tmp/$NAME + +rm -f $NAME.tar.gz + -d "$NAME" && rm -rf "$NAME" +git clone --branch master ssh://git@git.kolab.org/diffusion/S/syncroton.git $NAME +pushd $NAME +git reset --hard $GIT_REF +git archive --prefix=$NAME/ -o "$ROOT_DIR/$NAME.tar.gz" HEAD + +cd "$PWD"
View file
debian.changelog
Changed
@@ -1,3 +1,123 @@ +kolab-syncroton (2.4.2.10-0~kolab1) unstable; urgency=low + + * Release version 2.4.2 + + -- Christian Mollekopf <mollekopf@apheleia-it.ch> Thu, 27 July 2023 15:13:40 +0200 + +kolab-syncroton (2.4.1-0~kolab1) unstable; urgency=low + + * Release version 2.4.1 + + -- Christian Mollekopf <mollekopf@apheleia-it.ch> Mon, 17 July 2023 15:13:40 +0200 + +kolab-syncroton (2.4.0-0~kolab1) unstable; urgency=low + + * Release version 2.4.0 + + -- Christian Mollekopf <mollekopf@apheleia-it.ch> Wed, 10 May 2023 15:13:40 +0200 + +kolab-syncroton (2.3.23-0~kolab1) unstable; urgency=low + + * Release version 2.3.23 + + -- Christian Mollekopf <mollekopf@apheleia-it.ch> Wed, 19 Apr 2023 15:13:40 +0200 + +kolab-syncroton (2.3.22-1~kolab1) unstable; urgency=low + + * Fixed timezone issue + + -- Christian Mollekopf <mollekopf@apheleia-it.ch> Mon, 19 Dec 2022 15:13:40 +0200 + +kolab-syncroton (2.3.22-0~kolab1) unstable; urgency=low + + * Release version 2.3.22 + + -- Jeroen van Meeuwen <vanmeeuwen@kolabsys.com> Fri, 4 Feb 2022 15:13:40 +0200 + +kolab-syncroton (2.3.21-0~kolab1) unstable; urgency=low + + * Release version 2.3.21 + + -- Jeroen van Meeuwen <vanmeeuwen@kolabsys.com> Fri, 2 Jul 2021 15:13:40 +0200 + +kolab-syncroton (2.3.19-0~kolab1) unstable; urgency=low + + * Release version 2.3.19 + + -- Christian Mollekopf <mollekopf@kolabsys.com> Thu, 28 Jan 2021 15:13:40 +0200 + +kolab-syncroton (2.3.18-0~kolab2) unstable; urgency=low + + * Release version 2.3.18 + + -- Christian Mollekopf <mollekopf@kolabsys.com> Wed, 16 Dec 2020 15:13:40 +0200 + +kolab-syncroton (2.3.17-1~kolab3) unstable; urgency=low + + * Adjusted kolab-syncroton.logrotate according to new filenames + + -- Christian Mollekopf <mollekopf@kolabsys.com> Wed, 20 May 2020 15:13:40 +0200 + +kolab-syncroton (2.3.17-0~kolab2) unstable; urgency=low + + * Release version 2.3.17 + + -- Christian Mollekopf <mollekopf@kolabsys.com> Mon, 4 May 2020 15:13:40 +0200 + +kolab-syncroton (2.3.16-0~kolab2) unstable; urgency=low + + * Release version 2.3.16 + + -- Jeroen van Meeuwen <vanmeeuwen@kolabsys.com> Wed, 4 Dec 2019 15:13:40 +0200 + +kolab-syncroton (2.3.15-0~kolab6) unstable; urgency=low + + * Fix MeetingResponse parsing for Calendar events + + -- Jeroen van Meeuwen (Kolab Systems) <vanmeeuwen@kolabsys.com> Mon, 29 Jul 2019 15:13:40 +0200 + +kolab-syncroton (2.3.15-0~kolab5) unstable; urgency=low + + * Correctly clause dependency on kolab-auth + + -- Jeroen van Meeuwen (Kolab Systems) <vanmeeuwen@kolabsys.com> Wed, 17 Apr 2019 15:13:40 +0200 + +kolab-syncroton (2.3.15-0~kolab3) unstable; urgency=low + + * Adjusted kolab-syncroton.logrotate according to new filenames + + -- Christian Mollekopf (Kolab Systems) <mollekopf@kolabys.com> Mon, 15 Apr 2019 12:12:12 +0100 + +kolab-syncroton (2.3.15-0~kolab2) unstable; urgency=low + + * Update defaults + + -- Jeroen van Meeuwen (Kolab Systems) <vanmeeuwen@kolabsys.com> Thu, 11 Apr 2019 15:13:40 +0200 + +kolab-syncroton (2.3.15-0~kolab1) unstable; urgency=low + + * Release 2.3.15 + + -- Jeroen van Meeuwen (Kolab Systems) <vanmeeuwen@kolabsys.com> Fri, 1 Feb 2019 15:13:40 +0200 + +kolab-syncroton (2.3.14-0~kolab1) unstable; urgency=low + + * Release 2.3.14 + + -- Jeroen van Meeuwen (Kolab Systems) <vanmeeuwen@kolabsys.com> Thu, 27 Dec 2018 15:13:40 +0200 + +kolab-syncroton (2.3.13-0~kolab1) unstable; urgency=low + + * Release 2.3.13 + + -- Jeroen van Meeuwen (Kolab Systems) <vanmeeuwen@kolabsys.com> Fri, 17 Aug 2018 15:13:40 +0200 + +kolab-syncroton (2.3.12-0~kolab1) unstable; urgency=low + + * Release 2.3.12 + + -- Jeroen van Meeuwen (Kolab Systems) <vanmeeuwen@kolabsys.com> Wed, 13 Jun 2018 15:13:40 +0200 + kolab-syncroton (2.3.10-0~kolab1) unstable; urgency=low * Release 2.3.10
View file
debian.control
Changed
@@ -3,26 +3,14 @@ Priority: extra Maintainer: Jeroen van Meeuwen (Kolab Systems) <vanmeeuwen@kolabsys.com> Uploaders: Paul Klos <kolab@klos2day.nl> -Build-Depends: debhelper (>= 8), po-debconf +Build-Depends: debhelper (>= 8), po-debconf, psa | bash Homepage: http://www.kolab.org/ Standards-Version: 3.9.3 Vcs-Git: http://git.kolab.org/kolab-syncroton Package: kolab-syncroton Architecture: all -Depends: php-auth-sasl, - php-mail-mime, - php-mdb2 (>= 2.5.0), - php-net-idna2, - php-net-smtp, - php-net-socket, - php-cli | php5-cli, - php-mysql | php5-mysql, - roundcubemail-core, - roundcubemail-plugin-kolab-folders (>= 3.1.12), - roundcubemail-plugin-libcalendaring (>= 3.1.12), - roundcubemail-plugin-libkolab (>= 3.1.12), - zendframework | zend-framework, +Depends: roundcubemail-core, ${misc:Depends} Description: ActiveSync for Kolab Groupware Kolab Groupware provides ActiveSync for Calendars,
View file
debian.rules
Changed
@@ -21,7 +21,11 @@ mkdir -p debian/tmp dh_install --list-missing -XLICENSE + mkdir -p $(CURDIR)/debian/kolab-syncroton/etc/roundcubemail + cp -a config/config.inc.php.dist $(CURDIR)/debian/kolab-syncroton/etc/roundcubemail/kolab_syncroton.inc.php + if ! -f "/etc/plesk-release" ; then \ + cp -v ../SOURCES/plesk.kolab_syncroton.inc.php $(CURDIR)/debian/kolab-syncroton/etc/roundcubemail/kolab_syncroton.inc.php; \ mkdir -p $(CURDIR)/debian/kolab-syncroton/etc/apache2/sites-available ; \ install -pm 644 $(CURDIR)/docs/kolab-syncroton.conf \ $(CURDIR)/debian/kolab-syncroton/etc/apache2/sites-available/kolab-syncroton.conf ; \
View file
debian.tar.gz/kolab-syncroton.logrotate
Changed
@@ -1,4 +1,4 @@ -/var/log/kolab-syncroton/console /var/log/kolab-syncroton/errors /var/log/kolab-syncroton/imap /var/log/kolab-syncroton/ldap /var/log/kolab-syncroton/sendmail /var/log/kolab-syncroton/sieve /var/log/kolab-syncroton/smtp /var/log/kolab-syncroton/sql /var/log/kolab-syncroton/userlogins { +/var/log/kolab-syncroton/*.log { missingok compress notifempty
View file
kolab-syncroton-2.3.10.tar.gz/composer.json-dist -> kolab-syncroton-2.4.2.tar.gz/composer.json-dist
Changed
@@ -19,9 +19,10 @@ "pear/net_ldap2": "~2.2.0", "pear/net_sieve": "~1.4.0", "kolab/net_ldap3": "dev-master", - "zf1/zend-log": "~1.12.11" + "zf1s/zend-json": "~1.12.20", + "zf1s/zend-log": "~1.12.20" }, "require-dev": { - "phpunit/phpunit": "~4.4.0" + "phpunit/phpunit": "^4.8 || ^5.7 || ^6 || ^7 || ^9" } }
View file
kolab-syncroton-2.3.10.tar.gz/config/config.inc.php.dist -> kolab-syncroton-2.4.2.tar.gz/config/config.inc.php.dist
Changed
@@ -5,9 +5,6 @@ // Enables ActiveSync protocol debuging $config'activesync_debug' = true; -// Enables logging to a separate directory for every user/device -$config'activesync_user_log' = false; - // If specified all ActiveSync-related logs will be saved to this file // Note: This doesn't change Roundcube Framework log locations $config'activesync_log_file' = null; @@ -70,7 +67,10 @@ // List of Roundcube plugins // WARNING: Not all plugins used in Roundcube can be listed here -$config'activesync_plugins' = array(); +$config'activesync_plugins' = array( + 'libcalendaring', + 'libkolab' +); // Defines for how many seconds we'll sleep between every // action for detecting changes in folders. Default: 60 @@ -100,13 +100,13 @@ // 8 - all folders in other users namespace // 16 - all subscribed folders in shared namespace // 32 - all folders in shared namespace -$config'activesync_init_subscriptions' = 0; +$config'activesync_init_subscriptions' = 21; // Defines blacklist of devices (device type strings) that do not support folder hierarchies. // When set to an array folder hierarchies are used on all devices not listed here. // When set to null an old whitelist approach will be used where we do opposite // action and enable folder hierarchies only on device types known to support it. -$config'activesync_multifolder_blacklist' = null; +$config'activesync_multifolder_blacklist' = array(); // Blacklist overwrites for specified object type. If set to an array // it will have a precedence over 'activesync_multifolder_blacklist' list only for that type. @@ -114,7 +114,7 @@ // in that case use $config'activesync_multifolder_blacklist_contact' = array('windowsoutlook'); $config'activesync_multifolder_blacklist_mail' = null; $config'activesync_multifolder_blacklist_event' = null; -$config'activesync_multifolder_blacklist_contact' = null; +$config'activesync_multifolder_blacklist_contact' = array('windowsoutlook'); $config'activesync_multifolder_blacklist_note' = null; $config'activesync_multifolder_blacklist_task' = null;
View file
kolab-syncroton-2.4.2.tar.gz/docs/SQL/mysql/2023100500.sql
Added
@@ -0,0 +1,2 @@ + +ALTER TABLE `syncroton_synckey` ADD `client_id_map` longblob DEFAULT NULL;
View file
kolab-syncroton-2.3.10.tar.gz/lib/ext/Syncroton/Backend/ABackend.php -> kolab-syncroton-2.4.2.tar.gz/lib/ext/Syncroton/Backend/ABackend.php
Changed
@@ -128,7 +128,7 @@ unset($data$key); if (!empty($value) && preg_match('/^\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2}/', $value)) { # 2012-08-12 07:43:26 - $value = new DateTime($value, new DateTimeZone('utc')); + $value = new DateTime($value, new DateTimeZone('UTC')); } $data$this->_toCamelCase($key, false) = $value;
View file
kolab-syncroton-2.3.10.tar.gz/lib/ext/Syncroton/Command/FolderCreate.php -> kolab-syncroton-2.4.2.tar.gz/lib/ext/Syncroton/Command/FolderCreate.php
Changed
@@ -98,16 +98,31 @@ $this->_folder->deviceId = $this->_device; $this->_folder->creationTime = $this->_syncTimeStamp; - $this->_folderBackend->create($this->_folder); + // Check if the folder already exists to avoid a duplicate insert attempt in db + try { + $this->_folderBackend->getFolder($this->_device, $this->_folder->serverId); + + if ($this->_logger instanceof Zend_Log) + $this->_logger->info(__METHOD__ . '::' . __LINE__ . " Attempted to create a folder that already exists. parentId: {$folder->parentId} displayName: {$folder->displayName}"); + + // The folder already exists + $this->_status = Syncroton_Command_FolderSync::STATUS_FOLDER_EXISTS; + } catch (Syncroton_Exception_NotFound $e) { + // This is the normal case + if ($this->_logger instanceof Zend_Log) + $this->_logger->debug(__METHOD__ . '::' . __LINE__ . " " . $e->getMessage()); + + $this->_folderBackend->create($this->_folder); + } } } catch (Syncroton_Exception_Status $e) { if ($this->_logger instanceof Zend_Log) - $this->_logger->debug(__METHOD__ . '::' . __LINE__ . " " . $e->getMessage()); + $this->_logger->warn(__METHOD__ . '::' . __LINE__ . " " . $e->getMessage()); $this->_status = $e->getCode(); } catch (Exception $e) { if ($this->_logger instanceof Zend_Log) - $this->_logger->debug(__METHOD__ . '::' . __LINE__ . " " . $e->getMessage()); + $this->_logger->warn(__METHOD__ . '::' . __LINE__ . " " . $e->getMessage()); $this->_status = Syncroton_Command_FolderSync::STATUS_UNKNOWN_ERROR; }
View file
kolab-syncroton-2.3.10.tar.gz/lib/ext/Syncroton/Command/GetAttachment.php -> kolab-syncroton-2.4.2.tar.gz/lib/ext/Syncroton/Command/GetAttachment.php
Changed
@@ -49,7 +49,7 @@ if (PHP_SAPI !== 'cli') { // cache for 3600 seconds $maxAge = 3600; - $now = new DateTime(null, new DateTimeZone('UTC')); + $now = new DateTime('now', new DateTimeZone('UTC')); header('Cache-Control: private, max-age=' . $maxAge); header("Expires: " . gmdate('D, d M Y H:i:s', $now->modify("+{$maxAge} sec")->getTimestamp()) . " GMT");
View file
kolab-syncroton-2.3.10.tar.gz/lib/ext/Syncroton/Command/ItemOperations.php -> kolab-syncroton-2.4.2.tar.gz/lib/ext/Syncroton/Command/ItemOperations.php
Changed
@@ -59,9 +59,6 @@ $this->_emptyFolderContents = $this->_handleEmptyFolderContents($emptyFolderContents); } } - - if ($this->_logger instanceof Zend_Log) - $this->_logger->debug(__METHOD__ . '::' . __LINE__ . " fetches: " . print_r($this->_fetches, true)); } /** @@ -118,7 +115,7 @@ $fileReference = $dataController->getFileReference($fetch'fileReference'); // unset data field and move content to stream - if ($this->_requestParameters'acceptMultipart' == true) { + if (!empty($this->_requestParameters'acceptMultipart')) { $this->_headers'Content-Type' = 'application/vnd.ms-sync.multipart'; $partStream = fopen("php://temp", 'r+'); @@ -252,12 +249,13 @@ } } - if (isset($fetch->Options->MIMESupport)){ - $fetchArray'options''mimeSupport' = (int) $fetch->Options->MIMESupport; + $airSync = $fetch->Options->children('uri:AirSync'); + if (isset($airSync->MIMESupport)) { + $fetchArray'options''mimeSupport' = (int) $airSync->MIMESupport; } - if (isset($airSyncBase->Range)) { - $fetchArray'options''range' = (string) $airSyncBase->Range; + if (isset($fetch->Options->Range)) { + $fetchArray'options''range' = (string) $fetch->Options->Range; } }
View file
kolab-syncroton-2.3.10.tar.gz/lib/ext/Syncroton/Command/MeetingResponse.php -> kolab-syncroton-2.4.2.tar.gz/lib/ext/Syncroton/Command/MeetingResponse.php
Changed
@@ -50,9 +50,6 @@ ); } } - - if ($this->_logger instanceof Zend_Log) - $this->_logger->debug(__METHOD__ . '::' . __LINE__ . " results: " . print_r($this->_results, true)); } /**
View file
kolab-syncroton-2.3.10.tar.gz/lib/ext/Syncroton/Command/MoveItems.php -> kolab-syncroton-2.4.2.tar.gz/lib/ext/Syncroton/Command/MoveItems.php
Changed
@@ -46,9 +46,6 @@ 'dstFldId' => (string)$move->DstFldId ); } - - if ($this->_logger instanceof Zend_Log) - $this->_logger->debug(__METHOD__ . '::' . __LINE__ . " moves: " . print_r($this->_moves, true)); } /** @@ -82,9 +79,12 @@ $dataController = Syncroton_Data_Factory::factory($sourceFolder->class, $this->_device, $this->_syncTimeStamp); $newId = $dataController->moveItem($move'srcFldId', $move'srcMsgId', $move'dstFldId'); + if (!$newId) { + throw new Syncroton_Exception_Status_MoveItems(Syncroton_Exception_Status_MoveItems::INVALID_SOURCE); + } + $response->appendChild($this->_outputDom->createElementNS('uri:Move', 'Status', Syncroton_Command_MoveItems::STATUS_SUCCESS)); - if ($newId) - $response->appendChild($this->_outputDom->createElementNS('uri:Move', 'DstMsgId', $newId)); + $response->appendChild($this->_outputDom->createElementNS('uri:Move', 'DstMsgId', $newId)); } catch (Syncroton_Exception_Status $e) { $response->appendChild($this->_outputDom->createElementNS('uri:Move', 'Status', $e->getCode())); } catch (Exception $e) {
View file
kolab-syncroton-2.3.10.tar.gz/lib/ext/Syncroton/Command/Ping.php -> kolab-syncroton-2.4.2.tar.gz/lib/ext/Syncroton/Command/Ping.php
Changed
@@ -90,7 +90,7 @@ } } - $this->_device->lastping = new DateTime('now', new DateTimeZone('utc')); + $this->_device->lastping = new DateTime('now', new DateTimeZone('UTC')); if ($status == self::STATUS_NO_CHANGES_FOUND) { $this->_device = $this->_deviceBackend->update($this->_device); @@ -113,7 +113,7 @@ $intervalEnd = $intervalStart + $lifeTime; $secondsLeft = $intervalEnd; - $folders = unserialize($this->_device->pingfolder); + $folders = $this->_device->pingfolder ? unserialize($this->_device->pingfolder) : array(); if ($status === self::STATUS_NO_CHANGES_FOUND && (!is_array($folders) || count($folders) == 0)) { $status = self::STATUS_MISSING_PARAMETERS; @@ -141,6 +141,10 @@ // reconnect external connections, etc. call_user_func($wakeupCallback); + // Calculate secondsLeft before any loop break just to have a correct value + // for logging purposes in case we breaked from the loop early + $secondsLeft = $intervalEnd - time(); + try { $device = $this->_deviceBackend->get($this->_device->id); } catch (Syncroton_Exception_NotFound $e) { @@ -181,7 +185,7 @@ continue; } - $now = new DateTime('now', new DateTimeZone('utc')); + $now = new DateTime('now', new DateTimeZone('UTC')); foreach ($folders as $folderId) { try { @@ -237,7 +241,8 @@ if ($status != self::STATUS_NO_CHANGES_FOUND) { break; } - + + // Update secondsLeft (again) $secondsLeft = $intervalEnd - time(); if ($this->_logger instanceof Zend_Log)
View file
kolab-syncroton-2.3.10.tar.gz/lib/ext/Syncroton/Command/Search.php -> kolab-syncroton-2.4.2.tar.gz/lib/ext/Syncroton/Command/Search.php
Changed
@@ -33,15 +33,18 @@ /** * parse search command request * + * @throws Syncroton_Exception_UnexpectedValue */ public function handle() { + if (! $this->_requestBody instanceof DOMDocument) { + throw new Syncroton_Exception_UnexpectedValue( + 'request body is no DOMDocument. got: ' . print_r($this->_requestBody, true)); + } + $xml = simplexml_import_dom($this->_requestBody); $this->_store = new Syncroton_Model_StoreRequest($xml->Store); - - if ($this->_logger instanceof Zend_Log) - $this->_logger->debug(__METHOD__ . '::' . __LINE__ . " stores: " . print_r($this->_store, true)); } /**
View file
kolab-syncroton-2.3.10.tar.gz/lib/ext/Syncroton/Command/SendMail.php -> kolab-syncroton-2.4.2.tar.gz/lib/ext/Syncroton/Command/SendMail.php
Changed
@@ -32,7 +32,8 @@ */ public function handle() { - if ($this->_requestParameters'contentType' == 'message/rfc822') { + if (isset($this->_requestParameters'contentType') && + $this->_requestParameters'contentType' === 'message/rfc822') { $this->_mime = $this->_requestBody; $this->_saveInSent = $this->_requestParameters'saveInSent'; $this->_replaceMime = false;
View file
kolab-syncroton-2.3.10.tar.gz/lib/ext/Syncroton/Command/Settings.php -> kolab-syncroton-2.4.2.tar.gz/lib/ext/Syncroton/Command/Settings.php
Changed
@@ -110,8 +110,10 @@ // Out-of-Office if (!empty($this->_OofGet)) { + $OofGet = null; try { $OofGet = $this->_deviceBackend->getOOF($this->_OofGet); + $OofStatus = self::STATUS_SUCCESS; } catch (Exception $e) { if ($e instanceof Syncroton_Exception_Status) { $OofStatus = $e->getCode();
View file
kolab-syncroton-2.3.10.tar.gz/lib/ext/Syncroton/Command/Sync.php -> kolab-syncroton-2.4.2.tar.gz/lib/ext/Syncroton/Command/Sync.php
Changed
@@ -243,7 +243,12 @@ continue; } - // check for invalid sycnkey + $syncKeyReused = $this->_syncStateBackend->haveNext($this->_device, $collectionData->folder, $collectionData->syncKey); + if ($syncKeyReused) { + if ($this->_logger instanceof Zend_Log) + $this->_logger->warn(__METHOD__ . '::' . __LINE__ . " already known synckey {$collectionData->syncKey} provided"); + } + // check for invalid synckey if(($collectionData->syncState = $this->_syncStateBackend->validate($this->_device, $collectionData->folder, $collectionData->syncKey)) === false) { if ($this->_logger instanceof Zend_Log) $this->_logger->warn(__METHOD__ . '::' . __LINE__ . " invalid synckey {$collectionData->syncKey} provided"); @@ -301,6 +306,11 @@ if ($this->_logger instanceof Zend_Log) $this->_logger->info(__METHOD__ . '::' . __LINE__ . " found " . count($adds) . " entries to be added to server"); + $clientIdMap = ; + if ($syncKeyReused && $collectionData->syncState->clientIdMap) { + $clientIdMap = Zend_Json::decode($collectionData->syncState->clientIdMap); + } + foreach ($adds as $add) { if ($this->_logger instanceof Zend_Log) $this->_logger->debug(__METHOD__ . '::' . __LINE__ . " add entry with clientId " . (string) $add->ClientId); @@ -309,20 +319,35 @@ if ($this->_logger instanceof Zend_Log) $this->_logger->info(__METHOD__ . '::' . __LINE__ . " adding entry as new"); - $serverId = $dataController->createEntry($collectionData->collectionId, new $dataClass($add->ApplicationData)); - - $clientModifications'added'$serverId = array( - 'clientId' => (string)$add->ClientId, - 'serverId' => $serverId, - 'status' => self::STATUS_SUCCESS, - 'contentState' => $this->_contentStateBackend->create(new Syncroton_Model_Content(array( - 'device_id' => $this->_device, - 'folder_id' => $collectionData->folder, - 'contentid' => $serverId, - 'creation_time' => $this->_syncTimeStamp, - 'creation_synckey' => $collectionData->syncKey + 1 - ))) - ); + $clientId = (string)$add->ClientId; + // If the sync key was reused, but we don't have a $clientId mapping, + // this means the client sent a new item with the same sync_key. + if ($syncKeyReused && array_key_exists($clientId, $clientIdMap)) { + // We don't normally store the clientId, so if a command with Add's is resent, + // we have to look-up the corresponding serverId using a cached clientId => serverId mapping, + // otherwise we would duplicate all added items on resend. + $serverId = $clientIdMap$clientId; + $clientModifications'added'$serverId = array( + 'clientId' => (string)$add->ClientId, + 'serverId' => $serverId, + 'status' => self::STATUS_SUCCESS, + 'contentState' => null + ); + } else { + $serverId = $dataController->createEntry($collectionData->collectionId, new $dataClass($add->ApplicationData)); + $clientModifications'added'$serverId = array( + 'clientId' => (string)$add->ClientId, + 'serverId' => $serverId, + 'status' => self::STATUS_SUCCESS, + 'contentState' => $this->_contentStateBackend->create(new Syncroton_Model_Content(array( + 'device_id' => $this->_device, + 'folder_id' => $collectionData->folder, + 'contentid' => $serverId, + 'creation_time' => $this->_syncTimeStamp, + 'creation_synckey' => $collectionData->syncKey + 1 + ))) + ); + } } catch (Exception $e) { if ($this->_logger instanceof Zend_Log) @@ -336,7 +361,7 @@ } // handle changes, but only if not first sync - if($collectionData->syncKey > 1 && $collectionData->hasClientChanges()) { + if(!$syncKeyReused && $collectionData->syncKey > 1 && $collectionData->hasClientChanges()) { $changes = $collectionData->getClientChanges(); if ($this->_logger instanceof Zend_Log) @@ -367,7 +392,7 @@ } // handle deletes, but only if not first sync - if($collectionData->hasClientDeletes()) { + if(!$syncKeyReused && $collectionData->hasClientDeletes()) { $deletes = $collectionData->getClientDeletes(); if ($this->_logger instanceof Zend_Log) $this->_logger->info(__METHOD__ . '::' . __LINE__ . " found " . count($deletes) . " entries to be deleted on server"); @@ -414,7 +439,7 @@ if ($this->_logger instanceof Zend_Log) $this->_logger->info(__METHOD__ . '::' . __LINE__ . " found " . count($fetches) . " entries to be fetched from server"); - $toBeFecthed = array(); + $toBeFetched = array(); foreach ($fetches as $fetch) { $serverId = (string)$fetch->ServerId; @@ -465,7 +490,7 @@ $wakeupCallback(); - $now = new DateTime(null, new DateTimeZone('utc')); + $now = new DateTime('now', new DateTimeZone('UTC')); foreach($this->_collections as $collectionData) { // continue immediately if folder does not exist @@ -548,6 +573,10 @@ // First check for folders hierarchy changes foreach ($this->_collections as $collectionData) { if (! ($collectionData->folder instanceof Syncroton_Model_IFolder)) { + + if ($this->_logger instanceof Zend_Log) + $this->_logger->warn(__METHOD__ . '::' . __LINE__ . " Detected a folder hierarchy change on {$collectionData->collectionId}."); + $sync->appendChild($this->_outputDom->createElementNS('uri:AirSync', 'Status', self::STATUS_FOLDER_HIERARCHY_HAS_CHANGED)); return $this->_outputDom; } @@ -629,13 +658,14 @@ if ($hasChanges) { // update _syncTimeStamp as $dataController->hasChanges might have spent some time - $this->_syncTimeStamp = new DateTime(null, new DateTimeZone('utc')); + $this->_syncTimeStamp = new DateTime('now', new DateTimeZone('UTC')); try { // fetch entries added since last sync $allClientEntries = $this->_contentStateBackend->getFolderState( $this->_device, - $collectionData->folder + $collectionData->folder, + $collectionData->syncState->counter ); // fetch entries changed since last sync @@ -666,8 +696,8 @@ if ($this->_logger instanceof Zend_Log) $this->_logger->info(__METHOD__ . '::' . __LINE__ . " skipped added entry: " . $serverId); unset($serverModifications'added'$id); - } } + } // entries to be deleted $serverModifications'deleted' = array_diff($allClientEntries, $allServerEntries); @@ -727,7 +757,7 @@ foreach($clientModifications'added' as $entryData) { $add = $responses->appendChild($this->_outputDom->createElementNS('uri:AirSync', 'Add')); $add->appendChild($this->_outputDom->createElementNS('uri:AirSync', 'ClientId', $entryData'clientId')); - // we have no serverId is the add failed + // we have no serverId if the add failed if(isset($entryData'serverId')) { $add->appendChild($this->_outputDom->createElementNS('uri:AirSync', 'ServerId', $entryData'serverId')); } @@ -826,6 +856,14 @@ $commands->appendChild($add); + $newContentStates = new Syncroton_Model_Content(array( + 'device_id' => $this->_device, + 'folder_id' => $collectionData->folder, + 'contentid' => $serverId, + 'creation_time' => $this->_syncTimeStamp, + 'creation_synckey' => $collectionData->syncState->counter + 1 + )); + $collectionChanges++; } catch (Syncroton_Exception_MemoryExhausted $seme) { // continue to next entry, as there is not enough memory left for the current entry @@ -833,23 +871,18 @@ if ($this->_logger instanceof Zend_Log) $this->_logger->warn(__METHOD__ . '::' . __LINE__ . " memory exhausted for entry: " . $serverId); - continue; + break; } catch (Exception $e) { if ($this->_logger instanceof Zend_Log) $this->_logger->warn(__METHOD__ . '::' . __LINE__ . " unable to convert entry to xml: " . $e->getMessage()); if ($this->_logger instanceof Zend_Log) $this->_logger->debug(__METHOD__ . '::' . __LINE__ . " unable to convert entry to xml: " . $e->getTraceAsString()); + // We bump collectionChanges anyways to make sure the windowSize still applies. + $collectionChanges++; } // mark as sent to the client, even the conversion to xml might have failed - $newContentStates = new Syncroton_Model_Content(array( - 'device_id' => $this->_device, - 'folder_id' => $collectionData->folder, - 'contentid' => $serverId, - 'creation_time' => $this->_syncTimeStamp, - 'creation_synckey' => $collectionData->syncState->counter + 1 - )); unset($serverModifications'added'$id); } @@ -881,11 +914,13 @@ if ($this->_logger instanceof Zend_Log) $this->_logger->warn(__METHOD__ . '::' . __LINE__ . " memory exhausted for entry: " . $serverId); - continue; + break; } catch (Exception $e) { if ($this->_logger instanceof Zend_Log) $this->_logger->warn(__METHOD__ . '::' . __LINE__ . " unable to convert entry to xml: " . $e->getMessage()); + // We bump collectionChanges anyways to make sure the windowSize still applies. + $collectionChanges++; } unset($serverModifications'changed'$id); @@ -911,6 +946,8 @@ } catch (Exception $e) { if ($this->_logger instanceof Zend_Log) $this->_logger->warn(__METHOD__ . '::' . __LINE__ . " unable to convert entry to xml: " . $e->getMessage()); + // We bump collectionChanges anyways to make sure the windowSize still applies. + $collectionChanges++; } unset($serverModifications'deleted'$id); @@ -918,8 +955,12 @@ $countOfPendingChanges = (count($serverModifications'added') + count($serverModifications'changed') + count($serverModifications'deleted')); if ($countOfPendingChanges > 0) { + if ($this->_logger instanceof Zend_Log) + $this->_logger->info(__METHOD__ . '::' . __LINE__ . " there are ". $countOfPendingChanges . " more items available"); $collection->appendChild($this->_outputDom->createElementNS('uri:AirSync', 'MoreAvailable')); } else { + if ($this->_logger instanceof Zend_Log) + $this->_logger->info(__METHOD__ . '::' . __LINE__ . " there are no more items available"); $serverModifications = null; } @@ -975,50 +1016,90 @@ $collectionData->syncState->pendingdata = null; } - - if (!empty($clientModifications'added')) { - if ($this->_logger instanceof Zend_Log) - $this->_logger->info(__METHOD__ . '::' . __LINE__ . " remove previous synckey as client added new entries"); - $keepPreviousSyncKey = false; - } else { - $keepPreviousSyncKey = true; - } - $collectionData->syncState->lastsync = clone $this->_syncTimeStamp; // increment sync timestamp by 1 second $collectionData->syncState->lastsync->modify('+1 sec'); - - try { - $transactionId = Syncroton_Registry::getTransactionManager()->startTransaction(Syncroton_Registry::getDatabase()); - - // store new synckey - $this->_syncStateBackend->create($collectionData->syncState, $keepPreviousSyncKey); - - // store contentstates for new entries added to client - foreach($newContentStates as $state) { - $this->_contentStateBackend->create($state); - } - - // remove contentstates for entries to be deleted on client - foreach($deletedContentStates as $state) { - $this->_contentStateBackend->delete($state); + if (!empty($clientModifications'added')) { + // Store a client id mapping in case we encounter a reused sync_key in a future request. + $newClientIdMap = ; + foreach($clientModifications'added' as $entryData) { + // No serverId if we failed to add + if ($entryData'status' == self::STATUS_SUCCESS) { + $newClientIdMap$entryData'clientId' = $entryData'serverId'; + } } - - Syncroton_Registry::getTransactionManager()->commitTransaction($transactionId); - } catch (Zend_Db_Statement_Exception $zdse) { - // something went wrong - // maybe another parallel request added a new synckey - // we must remove data added from client - if (!empty($clientModifications'added')) { - foreach ($clientModifications'added' as $added) { - $this->_contentStateBackend->delete($added'contentState'); - $dataController->deleteEntry($collectionData->collectionId, $added'serverId', array()); + $collectionData->syncState->clientIdMap = Zend_Json::encode($newClientIdMap); + } + + //Retry in case of deadlock + $retryCounter = 0; + while (True) { + try { + $transactionId = Syncroton_Registry::getTransactionManager()->startTransaction(Syncroton_Registry::getDatabase()); + // store new synckey + $this->_syncStateBackend->create($collectionData->syncState, true); + + // store contentstates for new entries added to client + foreach($newContentStates as $state) { + try { + //This can happen if we rerun a previous sync-key + $state = $this->_contentStateBackend->getContentState($state->device_id, $state->folder_id, $state->contentid); + $this->_contentStateBackend->update($state); + } catch(Exception $zdse) { + $this->_contentStateBackend->create($state); + } + } + + // remove contentstates for entries to be deleted on client + foreach($deletedContentStates as $state) { + $this->_contentStateBackend->delete($state); + } + + Syncroton_Registry::getTransactionManager()->commitTransaction($transactionId); + break; + } catch (Syncroton_Exception_DeadlockDetected $zdse) { + $retryCounter++; + if ($retryCounter > 60) { + if ($this->_logger instanceof Zend_Log) + $this->_logger->warn(__METHOD__ . '::' . __LINE__ . ' exception while storing new synckey. Aborting after 5 retries.'); + + // something went wrong + // maybe another parallel request added a new synckey + // we must remove data added from client + if (!empty($clientModifications'added')) { + foreach ($clientModifications'added' as $added) { + $this->_contentStateBackend->delete($added'contentState'); + $dataController->deleteEntry($collectionData->collectionId, $added'serverId', array()); + } + } + + Syncroton_Registry::getTransactionManager()->rollBack(); + + throw $zdse; + } + + Syncroton_Registry::getTransactionManager()->rollBack(); + // Give the other transactions some time before we try again + sleep(1); + if ($this->_logger instanceof Zend_Log) + $this->_logger->warn(__METHOD__ . '::' . __LINE__ . ' error during transaction, trying again.'); + } catch (Exception $zdse) { + if ($this->_logger instanceof Zend_Log) + $this->_logger->warn(__METHOD__ . '::' . __LINE__ . ' exception while storing new synckey.'); + // something went wrong + // maybe another parallel request added a new synckey + // we must remove data added from client + if (!empty($clientModifications'added')) { + foreach ($clientModifications'added' as $added) { + $this->_contentStateBackend->delete($added'contentState'); + $dataController->deleteEntry($collectionData->collectionId, $added'serverId', array()); + } } + + Syncroton_Registry::getTransactionManager()->rollBack(); + + throw $zdse; } - - Syncroton_Registry::getTransactionManager()->rollBack(); - - throw $zdse; } }
View file
kolab-syncroton-2.3.10.tar.gz/lib/ext/Syncroton/Command/Wbxml.php -> kolab-syncroton-2.4.2.tar.gz/lib/ext/Syncroton/Command/Wbxml.php
Changed
@@ -143,8 +143,8 @@ $this->_requestBody = $requestBody; $this->_device = $device; $this->_requestParameters = $requestParameters; - $this->_policyKey = $requestParameters'policyKey'; - + $this->_policyKey = isset($requestParameters'policyKey') ? $requestParameters'policyKey' : null; + $this->_deviceBackend = Syncroton_Registry::getDeviceBackend(); $this->_folderBackend = Syncroton_Registry::getFolderBackend(); $this->_syncStateBackend = Syncroton_Registry::getSyncStateBackend(); @@ -154,7 +154,7 @@ $this->_logger = Syncroton_Registry::get('loggerBackend'); } - $this->_syncTimeStamp = new DateTime(null, new DateTimeZone('UTC')); + $this->_syncTimeStamp = new DateTime('now', new DateTimeZone('UTC')); // set default content type $this->_headers'Content-Type' = 'application/vnd.ms-sync.wbxml'; @@ -181,12 +181,12 @@ $policy = $this->_policyBackend->get($this->_device->policyId); if((int)$policy->policyKey != (int)$this->_policyKey) { - $this->_outputDom->documentElement->appendChild($this->_outputDom->createElementNS($this->_defaultNameSpace, 'Status', 142)); - - $sepn = new Syncroton_Exception_ProvisioningNeeded(); - $sepn->domDocument = $this->_outputDom; - - throw $sepn; + $this->_outputDom->documentElement->appendChild($this->_outputDom->createElementNS($this->_defaultNameSpace, 'Status', 142)); + + $sepn = new Syncroton_Exception_ProvisioningNeeded(); + $sepn->domDocument = $this->_outputDom; + + throw $sepn; } // should we wipe the mobile phone?
View file
kolab-syncroton-2.4.2.tar.gz/lib/ext/Syncroton/Exception/DeadlockDetected.php
Added
@@ -0,0 +1,20 @@ +<?php +/** + * Syncroton + * + * @package Syncroton + * @subpackage Exception + * @license http://www.tine20.org/licenses/lgpl.html LGPL Version 3 + * @copyright Copyright (c) 2013-2013 Metaways Infosystems GmbH (http://www.metaways.de) + * @author Lars Kneschke <l.kneschke@metaways.de> + */ + +/** + * exception for database deadlocks + * + * @package Syncroton + * @subpackage Exception + */ +class Syncroton_Exception_DeadlockDetected extends Syncroton_Exception +{ +}
View file
kolab-syncroton-2.3.10.tar.gz/lib/ext/Syncroton/Exception/Status/Settings.php -> kolab-syncroton-2.4.2.tar.gz/lib/ext/Syncroton/Exception/Status/Settings.php
Changed
@@ -32,11 +32,11 @@ * @var array */ protected $_errorMessages = array( - self::PROTOCOL_ERROR => "Protocol error"; - self::ACCESS_DENIED => "Access denied"; - self::SERVICE_UNAVAILABLE => "Server unavailable"; - self::INVALID_ARGUMENTS => "Invalid arguments"; - self::CONFLICTING_ARGUMENTS => "Conflicting arguments"; - self::DENIED_BY_POLICY => "Denied by policy. Disabled by administrator"; + self::PROTOCOL_ERROR => "Protocol error", + self::ACCESS_DENIED => "Access denied", + self::SERVICE_UNAVAILABLE => "Server unavailable", + self::INVALID_ARGUMENTS => "Invalid arguments", + self::CONFLICTING_ARGUMENTS => "Conflicting arguments", + self::DENIED_BY_POLICY => "Denied by policy. Disabled by administrator", ); }
View file
kolab-syncroton-2.3.10.tar.gz/lib/ext/Syncroton/Model/AEntry.php -> kolab-syncroton-2.4.2.tar.gz/lib/ext/Syncroton/Model/AEntry.php
Changed
@@ -40,6 +40,7 @@ * (non-PHPdoc) * @see Countable::count() */ + #\ReturnTypeWillChange public function count() { return count($this->_elements); @@ -49,6 +50,7 @@ * (non-PHPdoc) * @see IteratorAggregate::getIterator() */ + #\ReturnTypeWillChange public function getIterator() { return new ArrayIterator($this->_elements);
View file
kolab-syncroton-2.3.10.tar.gz/lib/ext/Syncroton/Model/AXMLEntry.php -> kolab-syncroton-2.4.2.tar.gz/lib/ext/Syncroton/Model/AXMLEntry.php
Changed
@@ -174,7 +174,7 @@ $element->appendChild($element->ownerDocument->createCDATASection(base64_encode($value))); } else { // strip off any non printable control characters - if (!ctype_print($value)) { + if (!ctype_print((string)$value)) { $value = $this->_removeControlChars($value); }
View file
kolab-syncroton-2.3.10.tar.gz/lib/ext/Syncroton/Model/EmailMeetingRequest.php -> kolab-syncroton-2.4.2.tar.gz/lib/ext/Syncroton/Model/EmailMeetingRequest.php
Changed
@@ -68,8 +68,6 @@ const MESSAGE_TYPE_COPY = 5; const MESSAGE_TYPE_DELEGATED = 6; - protected $_dateTimeFormat = "Ymd\THis\Z"; - protected $_xmlBaseElement = 'MeetingRequest'; protected $_properties = array( @@ -80,7 +78,7 @@ 'dtStamp' => array('type' => 'datetime'), 'endTime' => array('type' => 'datetime'), 'globalObjId' => array('type' => 'string'), - 'instanceType' => array('type' => 'datetime'), + 'instanceType' => array('type' => 'number'), 'location' => array('type' => 'string'), 'organizer' => array('type' => 'string'), //e-mail address 'recurrenceId' => array('type' => 'datetime'),
View file
kolab-syncroton-2.3.10.tar.gz/lib/ext/Syncroton/Model/ISyncState.php -> kolab-syncroton-2.4.2.tar.gz/lib/ext/Syncroton/Model/ISyncState.php
Changed
@@ -19,6 +19,7 @@ * @property string counter * @property DateTime lastsync * @property string pendingdata + * @property string client_id_map */ interface Syncroton_Model_ISyncState {
View file
kolab-syncroton-2.3.10.tar.gz/lib/ext/Syncroton/Model/StoreRequest.php -> kolab-syncroton-2.4.2.tar.gz/lib/ext/Syncroton/Model/StoreRequest.php
Changed
@@ -178,12 +178,12 @@ switch ($this->_store'name') { case 'DocumentLibrary': case 'Document Library': //? - '0-999'; + $this->_store'options''range' = '0-999'; break; case 'Mailbox': case 'GAL': default: - '0-99'; + $this->_store'options''range' = '0-99'; break; } }
View file
kolab-syncroton-2.3.10.tar.gz/lib/ext/Syncroton/Registry.php -> kolab-syncroton-2.4.2.tar.gz/lib/ext/Syncroton/Registry.php
Changed
@@ -517,16 +517,4 @@ { parent::__construct($array, $flags); } - - /** - * @param string $index - * @returns mixed - * - * Workaround for http://bugs.php.net/bug.php?id=40442 (ZF-960). - */ - public function offsetExists($index) - { - return array_key_exists($index, $this); - } - }
View file
kolab-syncroton-2.3.10.tar.gz/lib/ext/Syncroton/Server.php -> kolab-syncroton-2.4.2.tar.gz/lib/ext/Syncroton/Server.php
Changed
@@ -105,9 +105,9 @@ $className = 'Syncroton_Command_' . $requestParameters'command'; - if(!class_exists($className)) { + if (!class_exists($className)) { if ($this->_logger instanceof Zend_Log) - $this->_logger->crit(__METHOD__ . '::' . __LINE__ . " command not supported: " . $requestParameters'command'); + $this->_logger->notice(__METHOD__ . '::' . __LINE__ . " command not supported: " . $requestParameters'command'); header("HTTP/1.1 501 not implemented"); @@ -123,10 +123,11 @@ $decoder = new Syncroton_Wbxml_Decoder($this->_body); $requestBody = $decoder->decode(); if ($this->_logger instanceof Zend_Log) { - $requestBody->formatOutput = true; - $this->_logger->debug(__METHOD__ . '::' . __LINE__ . " xml request:\n" . $requestBody->saveXML()); + $this->_logDomDocument($requestBody, 'request', __METHOD__, __LINE__); } } catch(Syncroton_Wbxml_Exception_UnexpectedEndOfFile $e) { + if ($this->_logger instanceof Zend_Log) + $this->_logger->warn(__METHOD__ . '::' . __LINE__ . " unexpected end of file."); $requestBody = NULL; } } else { @@ -161,11 +162,11 @@ } catch (Exception $e) { if ($this->_logger instanceof Zend_Log) - $this->_logger->crit(__METHOD__ . '::' . __LINE__ . " unexpected exception occured: " . get_class($e)); + $this->_logger->err(__METHOD__ . '::' . __LINE__ . " unexpected exception occured: " . get_class($e)); if ($this->_logger instanceof Zend_Log) - $this->_logger->crit(__METHOD__ . '::' . __LINE__ . " exception message: " . $e->getMessage()); + $this->_logger->err(__METHOD__ . '::' . __LINE__ . " exception message: " . $e->getMessage()); if ($this->_logger instanceof Zend_Log) - $this->_logger->crit(__METHOD__ . '::' . __LINE__ . " " . $e->getTraceAsString()); + $this->_logger->err(__METHOD__ . '::' . __LINE__ . " " . $e->getTraceAsString()); header("HTTP/1.1 500 Internal server error"); @@ -174,7 +175,7 @@ if ($response instanceof DOMDocument) { if ($this->_logger instanceof Zend_Log) { - $this->_logDomDocument(Zend_Log::DEBUG, $response, __METHOD__, __LINE__); + $this->_logDomDocument($response, 'response', __METHOD__, __LINE__); } if (isset($command) && $command instanceof Syncroton_Command_ICommand) { @@ -190,7 +191,6 @@ } catch (Syncroton_Wbxml_Exception $swe) { if ($this->_logger instanceof Zend_Log) { $this->_logger->err(__METHOD__ . '::' . __LINE__ . " Could not encode output: " . $swe); - $this->_logDomDocument(Zend_Log::WARN, $response, __METHOD__, __LINE__); } header("HTTP/1.1 500 Internal server error"); @@ -245,27 +245,31 @@ /** * write (possible big) DOMDocument in smaller chunks to log file * - * @param unknown $priority * @param DOMDocument $dom + * @param string $action * @param string $method * @param int $line */ - protected function _logDomDocument($priority, DOMDocument $dom, $method, $line) + protected function _logDomDocument(DOMDocument $dom, $action, $method, $line) { - $loops = 0; + if (method_exists($this->_logger, 'hasDebug') && !$this->_logger->hasDebug()) { + return; + } + + $tempStream = tmpfile(); + + $meta_data = stream_get_meta_data($tempStream); + $filename = $meta_data"uri"; - $tempStream = fopen('php://temp/maxmemory:5242880', 'r+'); - $dom->formatOutput = true; - fwrite($tempStream, $dom->saveXML()); + $dom->save($filename); $dom->formatOutput = false; rewind($tempStream); - - // log data in 1MByte chunks + + $loops = 0; while (!feof($tempStream)) { - $this->_logger->log($method . '::' . $line . " xml response($loops):\n" . fread($tempStream, 1048576), $priority); - + $this->_logger->debug("{$method}::{$line} xml {$action} ({$loops}):\n" . fread($tempStream, 1048576)); $loops++; } @@ -343,6 +347,11 @@ $tag = ord(fread($stream, 1)); $length = ord(fread($stream, 1)); + // If the stream is at the end we'll get a 0-length + if (!$length) { + continue; + } + switch ($tag) { case self::PARAMETER_ATTACHMENTNAME: $unpacked = unpack('A' . $length . 'string', fread($stream, $length));
View file
kolab-syncroton-2.3.10.tar.gz/lib/ext/Syncroton/Wbxml/Encoder.php -> kolab-syncroton-2.4.2.tar.gz/lib/ext/Syncroton/Wbxml/Encoder.php
Changed
@@ -20,27 +20,6 @@ class Syncroton_Wbxml_Encoder extends Syncroton_Wbxml_Abstract { /** - * stack of dtd objects - * - * @var array - */ - protected $_dtdStack = array(); - - /** - * stack of stream resources - * - * @var array - */ - protected $_streamStack = array(); - - /** - * stack of levels when to pop data from the other stacks - * - * @var array - */ - protected $_popStack = array(); - - /** * count level of tags * * @var string @@ -48,27 +27,6 @@ protected $_level = 0; /** - * when to take data next time from the different stacks - * - * @var unknown_type - */ - protected $_nextStackPop = NULL; - - /** - * collect data trough different calls to _handleCharacters - * - * @var string - */ - protected $_currentTagData = NULL; - - /** - * the current tag as read by the parser - * - * @var string - */ - protected $_currentTag = NULL; - - /** * the constructor * * @param resource $_stream @@ -151,64 +109,66 @@ { $_dom->formatOutput = false; - $tempStream = fopen('php://temp/maxmemory:5242880', 'r+'); - fwrite($tempStream, $_dom->saveXML()); - rewind($tempStream); - $this->_initialize($_dom); - - $parser = xml_parser_create_ns($this->_charSet, ';'); - xml_set_object($parser, $this); - xml_set_element_handler($parser, '_handleStartTag', '_handleEndTag'); - xml_set_character_data_handler($parser, '_handleCharacters'); - xml_parser_set_option($parser, XML_OPTION_CASE_FOLDING, 0); - - while (!feof($tempStream)) { - if (!xml_parse($parser, fread($tempStream, 1048576), feof($tempStream))) { - // uncomment to write xml document to file - #rewind($tempStream); - #$xmlStream = fopen(tempnam(sys_get_temp_dir(), "xmlerrors"), 'r+'); - #stream_copy_to_stream($tempStream, $xmlStream); - #fclose($xmlStream); - - throw new Syncroton_Wbxml_Exception(sprintf('XML error: %s at line %d', - xml_error_string(xml_get_error_code($parser)), - xml_get_current_line_number($parser) - )); + $this->_traverseDom($_dom); + } + + private function getAttributes($node) + { + $attributes = array(); + if ($node->attributes) { + for ($i = 0; $i < $node->attributes->length; ++$i) { + $attributes$node->attributes->item($i)->name = $node->attributes->item($i)->value; } } + return $attributes; + } - fclose($tempStream); - xml_parser_free($parser); + private function writeNode($node, $withContent = false, $data = null) { + if($this->_codePage->getNameSpace() != $node->namespaceURI) { + $this->_switchCodePage($node->namespaceURI); + } + $this->_writeTag($node->localName, $this->getAttributes($node), $withContent, $data); } - /** - * get's called by xml parser when tag starts - * - * @param resource $_parser - * @param string $_tag current tag prefixed with namespace - * @param array $_attributes list of tag attributes - */ - protected function _handleStartTag($_parser, $_tag, $_attributes) + protected function _traverseDom($_dom) { - $this->_level++; - $this->_currentTagData = null; - - // write data for previous tag happens whith <tag1><tag2> - if($this->_currentTag !== NULL) { - $this->_writeTag($this->_currentTag, $this->_attributes, true); + if ($_dom->childNodes->length == 0) { + return false; } - - list($nameSpace, $this->_currentTag) = explode(';', $_tag); - - if($this->_codePage->getNameSpace() != $nameSpace) { - $this->_switchCodePage($nameSpace); + // print(str_pad("", $this->_level, " ") . "traversing {$_dom->nodeName}" . "\n"); + $this->_level++; + $prevNode = $_dom; + $foundElementNode = false; + foreach ($_dom->childNodes as $node) { + if ($node->nodeType == XML_ELEMENT_NODE) { + $foundElementNode = true; + if ($prevNode && $this->_level > 1) { + // print(str_pad("", $this->_level, " ") . "{$node->nodeName} creating parent {$prevNode->nodeName}" . "\n"); + $this->writeNode($prevNode, true); + $prevNode = null; + } + if (!$this->_traverseDom($node)) { + // print(str_pad("", $this->_level, " ") . "{$node->nodeName} content {$node->nodeValue}" . "\n"); + $data = $node->nodeValue; + if (strlen($data) == 0) { + $this->writeNode($node); + } else { + $this->writeNode($node, true, $data); + $this->_writeByte(Syncroton_Wbxml_Abstract::END); + // print("Closing tag after writing tag\n"); + } + } else { + $this->_writeByte(Syncroton_Wbxml_Abstract::END); + // print("Closing tag\n"); + } + } } + $this->_level--; - $this->_attributes = $_attributes; - + return $foundElementNode; } - + /** * strip uri: from nameSpace * @@ -221,73 +181,6 @@ } /** - * get's called by xml parser when tag ends - * - * @param resource $_parser - * @param string $_tag current tag prefixed with namespace - */ - protected function _handleEndTag($_parser, $_tag) - { - #echo "$_tag Level: $this->_level == $this->_nextStackPop \n"; - - if($this->_nextStackPop !== NULL && $this->_nextStackPop == $this->_level) { - #echo "TAG: $_tag\n"; - $this->_writeByte(Syncroton_Wbxml_Abstract::END); - - $subStream = $this->_stream; - $subStreamLength = ftell($subStream); - - $this->_dtd = array_pop($this->_dtdStack); - $this->_stream = array_pop($this->_streamStack); - $this->_nextStackPop = array_pop($this->_popStack); - $this->_codePage = $this->_dtd->getCurrentCodePage(); - - rewind($subStream); - #while (!feof($subStream)) {$buffer = fgets($subStream, 4096);echo $buffer;} - $this->_writeByte(Syncroton_Wbxml_Abstract::OPAQUE); - $this->_writeMultibyteUInt($subStreamLength); - - $writenBytes = stream_copy_to_stream($subStream, $this->_stream); - if($writenBytes !== $subStreamLength) { - //echo "$writenBytes !== $subStreamLength\n"; - throw new Syncroton_Wbxml_Exception('blow'); - } - fclose($subStream); - #echo "$this->_nextStackPop \n"; exit; - } else { - if ($this->_currentTag !== NULL && $this->_currentTagData !== NULL) { - $this->_writeTag($this->_currentTag, $this->_attributes, true, $this->_currentTagData); - $this->_writeByte(Syncroton_Wbxml_Abstract::END); - } elseif ($this->_currentTag !== NULL && $this->_currentTagData === NULL) { - // for example <UTC/> tag with no data, jumps directly from _handleStartTag to _handleEndTag - $this->_writeTag($this->_currentTag, $this->_attributes); - // no end tag required, tag has no content - } else { - $this->_writeByte(Syncroton_Wbxml_Abstract::END); - } - } - - #list($urn, $tag) = explode(';', $_tag); echo "</$tag> ($this->_level)\n"; - - // reset $this->_currentTag, as tag got writen to stream already - $this->_currentTag = NULL; - - $this->_level--; - } - - /** - * collects data(value) of tag - * can be called multiple lines if the value contains linebreaks - * - * @param resource $_parser the xml parser - * @param string $_data the data(value) of the tag - */ - protected function _handleCharacters($_parser, $_data) - { - $this->_currentTagData .= $_data; - } - - /** * writes tag with data to stream * * @param string $_tag @@ -329,8 +222,6 @@ $this->_writeTerminatedString($_data); } } - - $this->_currentTagData = NULL; } /** @@ -340,30 +231,16 @@ */ protected function _switchCodePage($_nameSpace) { - try { - $codePageName = $this->_stripNameSpace($_nameSpace); - if(!defined('Syncroton_Wbxml_Dtd_ActiveSync::CODEPAGE_'. strtoupper($codePageName))) { - throw new Syncroton_Wbxml_Exception('codepage ' . $codePageName . ' not found'); - } - // switch to another codepage - // no need to write the wbxml header again - $codePageId = constant('Syncroton_Wbxml_Dtd_ActiveSync::CODEPAGE_'. strtoupper($codePageName)); - $this->_codePage = $this->_dtd->switchCodePage($codePageId); - - $this->_writeByte(Syncroton_Wbxml_Abstract::SWITCH_PAGE); - $this->_writeByte($codePageId); - } catch (Syncroton_Wbxml_Dtd_Exception_CodePageNotFound $e) { - // switch to another dtd - // need to write the wbxml header again - // put old dtd and stream on stack - $this->_dtdStack = $this->_dtd; - $this->_streamStack = $this->_stream; - $this->_popStack = $this->_nextStackPop; - $this->_nextStackPop = $this->_level; - - $this->_stream = fopen("php://temp", 'r+'); - - $this->_initialize($_urn); + $codePageName = $this->_stripNameSpace($_nameSpace); + if(!defined('Syncroton_Wbxml_Dtd_ActiveSync::CODEPAGE_'. strtoupper($codePageName))) { + throw new Syncroton_Wbxml_Exception('codepage ' . $codePageName . ' not found'); } + // switch to another codepage + // no need to write the wbxml header again + $codePageId = constant('Syncroton_Wbxml_Dtd_ActiveSync::CODEPAGE_'. strtoupper($codePageName)); + $this->_codePage = $this->_dtd->switchCodePage($codePageId); + + $this->_writeByte(Syncroton_Wbxml_Abstract::SWITCH_PAGE); + $this->_writeByte($codePageId); } -} \ No newline at end of file +}
View file
kolab-syncroton-2.3.10.tar.gz/lib/ext/Syncroton/Wbxml/Exception/UnexpectedEndOfFile.php -> kolab-syncroton-2.4.2.tar.gz/lib/ext/Syncroton/Wbxml/Exception/UnexpectedEndOfFile.php
Changed
@@ -19,5 +19,5 @@ class Syncroton_Wbxml_Exception_UnexpectedEndOfFile extends Syncroton_Wbxml_Exception { - protected $message = 'unexpcted end of file detected'; -} \ No newline at end of file + protected $message = 'unexpected end of file detected'; +}
View file
kolab-syncroton-2.3.10.tar.gz/lib/ext/rtf.php -> kolab-syncroton-2.4.2.tar.gz/lib/ext/rtf.php
Changed
@@ -101,6 +101,7 @@ var $cfirst; // could this be the first character ? so watch out for control symbols var $flags = array(); // parser flags + var $fonttable = array(); var $queue; // every character which is no sepcial char, not belongs to a control word/symbol; is generally considered being 'plain' @@ -214,20 +215,20 @@ $flagCount = 0; $flags = 0; while ($out<$oblen) { - $flags = ($flagCount++ % 8 == 0) ? ord($src{$in++}) : $flags >> 1; + $flags = ($flagCount++ % 8 == 0) ? ord($src$in++) : $flags >> 1; if (($flags & 1) == 1) { - $offset = ord($src{$in++}); - $length = ord($src{$in++}); + $offset = ord($src$in++); + $length = ord($src$in++); $offset = ($offset << 4) | ($length >> 4); $length = ($length & 0xF) + 2; $offset = (int)($out / 4096) * 4096 + $offset; if ($offset >= $out) $offset -= 4096; $end = $offset + $length; while ($offset < $end) { - $dst{$out++} = $dst{$offset++}; + $dst$out++ = $dst$offset++; }; } else { - $dst{$out++} = $src{$in++}; + $dst$out++ = $src$in++; } } $src = $dst; @@ -249,7 +250,7 @@ $c=0; $end = $off + $len; for($i=$off;$i < $end;$i++) { - $c=$this->CRC32_TABLE($c ^ ord($buf{$i})) & 0xFF ^ (($c >> 8) & 0x00ffffff); + $c=$this->CRC32_TABLE($c ^ ord($buf$i)) & 0xFF ^ (($c >> 8) & 0x00ffffff); } return $c; } @@ -399,7 +400,6 @@ $this->out.="<group>"; } if($state == "close") { /* pop from the stack */ - $this->last_flags = $this->flags; $this->flags = array_pop($this->stack); $this->flags"fonttbl_current_write" = ""; // on group close, no more fontdefinition will be written to this id
View file
kolab-syncroton-2.3.10.tar.gz/lib/kolab_sync.php -> kolab-syncroton-2.4.2.tar.gz/lib/kolab_sync.php
Changed
@@ -45,8 +45,11 @@ public $username; public $password; + public $task = null; + protected $per_user_log_dir; + const CHARSET = 'UTF-8'; - const VERSION = "2.3.10"; + const VERSION = "2.4.2"; /** @@ -207,7 +210,7 @@ } // LDAP server failure... send 503 error - if ($auth'kolab_ldap_error') { + if ($auth'kolab_ldap_error' ?? null) { self::server_error(); } @@ -222,8 +225,10 @@ $auth'pass' = $password; } + $err = null; + // Authenticate - get Roundcube user ID - if (!$auth'abort' && ($userid = $this->login($auth'user', $auth'pass', $auth'host', $err))) { + if (!($auth'abort' ?? false) && ($userid = $this->login($auth'user', $auth'pass', $auth'host', $err))) { // set real username $this->username = $auth'user'; return $userid; @@ -232,7 +237,9 @@ $err_str = $this->get_storage()->get_error_str(); } - kolab_auth::log_login_error($auth'user', $err_str ?: $err); + if (class_exists('kolab_auth', false)) { + kolab_auth::log_login_error($auth'user', !empty($err_str) ? $err_str : $err); + } $this->plugins->exec_hook('login_failed', array( 'host' => $auth'host', @@ -296,7 +303,9 @@ // parse $host $a_host = parse_url($host); - if ($a_host'host') { + $port = null; + $ssl = null; + if (!empty($a_host'host')) { $host = $a_host'host'; $ssl = (isset($a_host'scheme') && in_array($a_host'scheme', array('ssl','imaps','tls'))) ? $a_host'scheme' : null; if (!empty($a_host'port')) { @@ -392,48 +401,63 @@ $this->logger->set_username($this->username); - $user_debug = $this->config->get('per_user_logging'); - $user_log = $user_debug || $this->config->get('activesync_user_log'); + $user_debug = (bool) $this->config->get('per_user_logging'); - if (!$user_log) { + if (!$user_debug) { return; } $log_dir = $this->config->get('log_dir'); $log_dir .= DIRECTORY_SEPARATOR . $this->username; - if (!$user_debug && !is_dir($log_dir)) { - if (!mkdir($log_dir, 0770)) { - return; - } + // No automatically creating any log directories + if (!is_dir($log_dir)) { + $this->logger->set_log_dir(null); + return; } + $deviceId = null; + if (!empty($_GET'DeviceId')) { - $log_dir .= DIRECTORY_SEPARATOR . $_GET'DeviceId'; + $deviceId = $_GET'DeviceId'; } - - if (!is_dir($log_dir)) { - if (!mkdir($log_dir, 0770)) { - return; + else if ( + !empty($_SERVER'QUERY_STRING') + && strpos($_SERVER'QUERY_STRING', '&') == false + && ($query = base64_decode($_SERVER'QUERY_STRING')) + && strlen($query) > 8 + ) { + // unpack the first 5 bytes, the last one is a length of the device id + $unpacked = unpack('Cversion/Ccommand/vlocale/Clength', substr($query, 0, 5)); + + // unpack the deviceId, with some input sanity checks + if ( + !empty($unpacked'version') + && !empty($unpacked'length') + && $unpacked'version' >= 121 + && ($length = $unpacked'length') > 0 && $length <= 32 + ) { + $unpacked = unpack("H" . ($length * 2) . "string", $query, 5); + $deviceId = $unpacked'string'; } } - if ($user_debug) { - $this->per_user_log_dir = $log_dir; - } - else { - $this->config->set('log_dir', $log_dir); - } + if (!empty($deviceId)) { + $dev_dir = $log_dir . DIRECTORY_SEPARATOR . $deviceId; - // re-set PHP error logging - if (($this->config->get('debug_level') & 1) && $this->config->get('log_driver') != 'syslog') { - ini_set('error_log', $log_dir . '/errors'); + if (is_dir($dev_dir) || mkdir($dev_dir, 0770)) { + $log_dir = $dev_dir; + } } + + $this->per_user_log_dir = $log_dir; + $this->logger->set_log_dir($log_dir); + $this->config->set('log_dir', $log_dir); } /** - * Get the per-user log directory - */ + * Get the per-user log directory + */ public function get_user_log_dir() { return $this->per_user_log_dir; @@ -464,13 +488,16 @@ // write performance stats to logs/console if ($this->config->get('devel_mode') || $this->config->get('performance_stats')) { + // we have to disable per_user_logging to make sure stats end up in the main console log + $this->config->set('per_user_logging', false); + // make sure logged numbers use unified format setlocale(LC_NUMERIC, 'en_US.utf8', 'en_US.UTF-8', 'en_US', 'C'); if (function_exists('memory_get_usage')) - $mem = sprintf('%.1f', memory_get_usage() / 1048576); + $mem = round(memory_get_usage() / 1048576, 1); if (function_exists('memory_get_peak_usage')) - $mem .= '/' . sprintf('%.1f', memory_get_peak_usage() / 1048576); + $mem .= '/' . round(memory_get_peak_usage() / 1048576, 1); $query = $_SERVER'QUERY_STRING'; $log = $query . ($mem ? ($query ? ' ' : '') . "$mem" : '');
View file
kolab-syncroton-2.3.10.tar.gz/lib/kolab_sync_backend.php -> kolab-syncroton-2.4.2.tar.gz/lib/kolab_sync_backend.php
Changed
@@ -149,7 +149,7 @@ public function folders_list($deviceid, $type, $flat_mode = false) { // get all folders of specified type - $folders = (array) kolab_storage::list_folders('', '*', $type, false, $typedata); + $folders = kolab_storage::list_folders('', '*', $type, false, $typedata); // get folders activesync config $folderdata = $this->folder_meta(); @@ -176,7 +176,7 @@ } // Activesync folder identifier (serverId) - $folder_type = $typedata$folder ?: 'mail'; + $folder_type = !empty($typedata$folder) ? $typedata$folder : 'mail'; $folder_id = self::folder_id($folder, $folder_type); $folders_list$folder_id = $this->folder_data($folder, $folder_type); @@ -215,8 +215,8 @@ while (count($items) > 0) { array_pop($items); - $parent_name = implode($items, $delim); - $parent_type = $typedata$parent_name ?: 'mail'; + $parent_name = implode($delim, $items); + $parent_type = !empty($typedata$parent_name) ? $typedata$parent_name : 'mail'; $parent_id = self::folder_id($parent_name, $parent_type); if (isset($folders$parent_id)) { @@ -253,14 +253,15 @@ public function folder_meta() { if (!isset($this->folder_meta)) { - $this->folder_meta = array(); // get folders activesync config $folderdata = $this->storage->get_metadata("*", self::ASYNC_KEY); if (!is_array($folderdata)) { - return false; + return $this->folder_meta = false; } + $this->folder_meta = array(); + foreach ($folderdata as $folder => $meta) { if ($asyncdata = $metaself::ASYNC_KEY) { if ($metadata = $this->unserialize_metadata($asyncdata)) { @@ -364,7 +365,7 @@ return false; } - $metadata = $metadata$name; + $metadata = isset($metadata$name) ? $metadata$name : array(); if ($flag) { if (empty($metadata)) { @@ -384,8 +385,7 @@ // 2 - synchronize with alarms $metadata'FOLDER'$deviceid'S' = $flag; } - - if (!$flag) { + else { unset($metadata'FOLDER'$deviceid'S'); if (empty($metadata'FOLDER'$deviceid)) { @@ -402,7 +402,7 @@ } // Return if nothing's been changed - if (!self::data_array_diff($this->folder_meta$name, $metadata)) { + if (!self::data_array_diff(isset($this->folder_meta$name) ? $this->folder_meta$name : null, $metadata)) { return true; } @@ -416,10 +416,7 @@ public function device_get($id) { $devices_list = $this->devices_list(); - - $result = $devices_list$id; - - return $result; + return $devices_list$id ?? null; } /** @@ -589,7 +586,11 @@ 'event', 'contact', 'note', - 'task' + 'task', + 'event.confidential', + 'event.private', + 'task.confidential', + 'task.private', ); // This default set can be extended by adding following values: @@ -665,7 +666,7 @@ continue; } - $type = $foldertypes$folder ?: 'mail'; + $type = ($foldertypes$folder ?? null) ?: 'mail'; if ($type == 'mail' && isset($special_folders$folder)) { $type = $special_folders$folder; } @@ -736,6 +737,8 @@ */ public static function type_kolab2activesync($type) { + $type = preg_replace('/\.(confidential|private)$/i', '', $type); + if ($key = array_search($type, self::$types)) { return $key; } @@ -769,11 +772,18 @@ $folder_id = $this->folder_id($folder, $type); // Folder type - $type = self::type_kolab2activesync($type); - // fix type, if there's no type annotation it's detected as UNKNOWN - // we'll use 'mail' (12) or 'mail.inbox' (2) - if ($type == 1) { - $type = $folder == 'INBOX' ? 2 : 12; + if (strcasecmp($folder, 'INBOX') === 0) { + // INBOX is always inbox, prevent from issues related with a change of + // folder type annotation (it can be initially unset). + $type = 2; + } + else { + $type = self::type_kolab2activesync($type); + + // fix type, if there's no type annotation it's detected as UNKNOWN we'll use 'mail' (12) + if ($type == 1) { + $type = 12; + } } // Syncroton folder data array @@ -818,8 +828,23 @@ return $this->folder_uids$name = $uid; } */ + if (strcasecmp($name, 'INBOX') === 0) { + // INBOX is always inbox, prevent from issues related with a change of + // folder type annotation (it can be initially unset). + $type = 'mail.inbox'; + } + else { + if ($type === null) { + $type = kolab_storage::folder_type($name); + } + + if ($type != null) { + $type = preg_replace('/\.(confidential|private)$/i', '', $type); + } + } + // Add type to folder UID hash, so type change can be detected by Syncroton - $uid = $name . '!!' . ($type !== null ? $type : kolab_storage::folder_type($name)); + $uid = $name . '!!' . $type; $uid = md5($uid); return $this->folder_uids$name = $uid; @@ -892,7 +917,7 @@ $synctime = $synctime->format('Y-m-d H:i:s'); $rcube = rcube::get_instance(); $db = $rcube->get_dbh(); - $old_data = $this->modseq$folderid$synctime; + $old_data = $this->modseq$folderid$synctime ?? null; if (empty($old_data)) { $this->modseq$folderid$synctime = $data; @@ -944,7 +969,7 @@ $synctime = $synctime->format('Y-m-d H:i:s'); $rcube = rcube::get_instance(); $db = $rcube->get_dbh(); - $old_data = $this->relations$folderid$synctime; + $old_data = $this->relations$folderid$synctime ?? null; if (empty($old_data)) { $this->relations$folderid$synctime = $relations;
View file
kolab-syncroton-2.3.10.tar.gz/lib/kolab_sync_backend_common.php -> kolab-syncroton-2.4.2.tar.gz/lib/kolab_sync_backend_common.php
Changed
@@ -81,8 +81,8 @@ * * @param Syncroton_Model_* $object Object * - * @throws InvalidArgumentException * @return Syncroton_Model_* Object + * @throws InvalidArgumentException|Syncroton_Exception_DeadlockDetected|Exception */ public function create($object) { @@ -104,8 +104,13 @@ array_values($data) ); - if ($this->db->is_error($result)) { - throw new Exception('Failed to save instance of ' . $this->interface_name); + if ($err = $this->db->is_error($result)) { + $err = "Failed to save instance of {$this->interface_name}: {$err}"; + if ($this->db->error_info()0 == '40001') { + throw new Syncroton_Exception_DeadlockDetected($err); + } else { + throw new Exception($err); + } } return $object; @@ -140,6 +145,7 @@ * @param string|Syncroton_Model_* $id Object or identifier * * @return bool True on success, False on failure + * @throws Syncroton_Exception_DeadlockDetected|Exception */ public function delete($id) { @@ -151,6 +157,15 @@ $result = $this->db->query('DELETE FROM `' . $this->table_name .'` WHERE `id` = ?', array($id)); + if ($err = $this->db->is_error($result)) { + $err = "Failed to delete instance of {$this->interface_name}: {$err}"; + if ($this->db->error_info()0 == '40001') { + throw new Syncroton_Exception_DeadlockDetected($err); + } else { + throw new Exception($rr); + } + } + return (bool) $this->db->affected_rows($result); } @@ -159,8 +174,8 @@ * * @param Syncroton_Model_* $object * - * @throws InvalidArgumentException * @return Syncroton_Model_* Object + * @throws InvalidArgumentException|Syncroton_Exception_DeadlockDetected|Exception */ public function update($object) { @@ -175,9 +190,18 @@ $set = $this->db->quote_identifier($key) . ' = ?'; } - $this->db->query('UPDATE `' . $this->table_name . '` SET ' . implode(', ', $set) + $result = $this->db->query('UPDATE `' . $this->table_name . '` SET ' . implode(', ', $set) . ' WHERE `id` = ' . $this->db->quote($object->id), array_values($data)); + if ($err = $this->db->is_error($result)) { + $err = "Failed to update instance of {$this->interface_name}: {$err}"; + if ($this->db->error_info()0 == '40001') { + throw new Syncroton_Exception_DeadlockDetected($err); + } else { + throw new Exception($err); + } + } + return $object; }
View file
kolab-syncroton-2.3.10.tar.gz/lib/kolab_sync_backend_content.php -> kolab-syncroton-2.4.2.tar.gz/lib/kolab_sync_backend_content.php
Changed
@@ -55,6 +55,7 @@ * @param Syncroton_Model_IDevice|string $_deviceId * @param Syncroton_Model_IFolder|string $_folderId * @param string $_contentId + * * @return Syncroton_Model_IContent */ public function getContentState($_deviceId, $_folderId, $_contentId) @@ -82,13 +83,15 @@ * * @param Syncroton_Model_IDevice|string $_deviceId * @param Syncroton_Model_IFolder|string $_folderId + * @param int $syncKey + * * @return array */ - public function getFolderState($_deviceId, $_folderId) + public function getFolderState($_deviceId, $_folderId, $syncKey = null) { $deviceId = $_deviceId instanceof Syncroton_Model_IDevice ? $_deviceId->id : $_deviceId; $folderId = $_folderId instanceof Syncroton_Model_IFolder ? $_folderId->id : $_folderId; - $cachekey = $deviceId . ':' . $folderId; + $cachekey = $deviceId . ':' . $folderId . ':' . ($syncKey ?: '*'); // in Sync request we call this function twice in case when // folder state changed - use cache to skip at least one SELECT query @@ -99,6 +102,9 @@ $where = $this->db->quote_identifier('device_id') . ' = ' . $this->db->quote($deviceId); $where = $this->db->quote_identifier('folder_id') . ' = ' . $this->db->quote($folderId); $where = $this->db->quote_identifier('is_deleted') . ' = 0'; + if ($syncKey) { + $where = $this->db->quote_identifier('creation_synckey') . ' < ' . $this->db->quote($syncKey + 1); + } $select = $this->db->query("SELECT `contentid` FROM `{$this->table_name}` WHERE " . implode(' AND ', $where)); $result = array(); @@ -120,9 +126,8 @@ { $deviceId = $_deviceId instanceof Syncroton_Model_IDevice ? $_deviceId->id : $_deviceId; $folderId = $_folderId instanceof Syncroton_Model_IFolder ? $_folderId->id : $_folderId; - $cachekey = $deviceId . ':' . $folderId; - unset($this->cache'content_folderstate'$cachekey); + unset($this->cache'content_folderstate'); $where = $this->db->quote_identifier('device_id') . ' = ' . $this->db->quote($deviceId); $where = $this->db->quote_identifier('folder_id') . ' = ' . $this->db->quote($folderId);
View file
kolab-syncroton-2.3.10.tar.gz/lib/kolab_sync_backend_device.php -> kolab-syncroton-2.4.2.tar.gz/lib/kolab_sync_backend_device.php
Changed
@@ -207,7 +207,11 @@ $status = Syncroton_Model_Oof::STATUS_GLOBAL; } + $message = null; + if ($vacation'message') { + $message = array(); + // convert message format, Roundcube supports plain text only if ($request'bodyType' == 'HTML') { $text2html = new rcube_text2html($vacation'message');
View file
kolab-syncroton-2.3.10.tar.gz/lib/kolab_sync_backend_state.php -> kolab-syncroton-2.4.2.tar.gz/lib/kolab_sync_backend_state.php
Changed
@@ -72,7 +72,7 @@ $data = parent::object_to_array($object); if (is_array($object->pendingdata)) { - $data'pendingdata' = json_encode($object->pendingdata); + $data'pendingdata' = gzdeflate(json_encode($object->pendingdata)); } return $data; @@ -86,7 +86,10 @@ $object = parent::get_object($data); if ($object->pendingdata) { - $object->pendingdata = json_decode($object->pendingdata, true); + $inflated = gzinflate($object->pendingdata); + // Inflation may fail for backward compatiblity + $data = $inflated ? $inflated : $object->pendingdata; + $object->pendingdata = json_decode($data, true); } return $object; @@ -177,21 +180,28 @@ $where'folder_id' = $this->db->quote_identifier('folder_id') . ' = ' . $this->db->quote($folder_id); $where'is_deleted' = $this->db->quote_identifier('is_deleted') . ' = 1'; - // found more recent synckey => the last sync response got not received by the client + // found more recent synckey => the last sync response was not received by the client if ($next > $sync_key) { - $where'synckey' = $this->db->quote_identifier('creation_synckey') . ' = ' . $this->db->quote($state->counter); - // undelete entries marked as deleted in syncroton_content table - $this->db->query("UPDATE `syncroton_content` SET `is_deleted` = 0 WHERE " . implode(' AND ', $where)); - - // remove entries added during latest sync in syncroton_content table - unset($where'is_deleted'); - $where'synckey' = $this->db->quote_identifier('creation_synckey') . ' > ' . $this->db->quote($state->counter); - - $this->db->query("DELETE FROM `syncroton_content` WHERE " . implode(' AND ', $where)); + // We store the clientIdMap with the "next" sync state, so we need to copy it back. + $state->clientIdMap = $states$next->clientIdMap; } else { - // finaly delete all entries marked for removal in syncroton_content table - $this->db->query("DELETE FROM `syncroton_content` WHERE " . implode(' AND ', $where)); + // finally delete all entries marked for removal in syncroton_content table + $retryCounter = 0; + while(True) { + $result = $this->db->query("DELETE FROM `syncroton_content` WHERE " . implode(' AND ', $where)); + if ($this->db->is_error($result)) { + $retryCounter++; + // Retry on deadlock + if ($this->db->error_info()0 != '40001' || $retryCounter > 60) { + throw new Exception('Failed to delete entries in sync_key check'); + } + } else { + break; + } + //Give the other transactions some time before we try again + sleep(1); + } } // remove all other synckeys @@ -201,4 +211,18 @@ return $state; } + + public function haveNext($deviceid, $folderid, $sync_key) + { + $device_id = $deviceid instanceof Syncroton_Model_IDevice ? $deviceid->id : $deviceid; + $folder_id = $folderid instanceof Syncroton_Model_IFolder ? $folderid->id : $folderid; + + $where'device_id' = $this->db->quote_identifier('device_id') . ' = ' . $this->db->quote($device_id); + $where'type' = $this->db->quote_identifier('type') . ' = ' . $this->db->quote($folder_id); + $where'counter' = $this->db->quote_identifier('counter') . ' > ' . $this->db->quote($sync_key); + + $select = $this->db->query("SELECT id FROM `{$this->table_name}` WHERE " . implode(' AND ', $where)); + return $this->db->num_rows($select) > 0; + } + }
View file
kolab-syncroton-2.3.10.tar.gz/lib/kolab_sync_data.php -> kolab-syncroton-2.4.2.tar.gz/lib/kolab_sync_data.php
Changed
@@ -311,6 +311,7 @@ $parentid = $folder->parentId; $type = $folder->type; $display_name = $folder->displayName; + $parent = null; if ($parentid) { $parent = $this->backend->folder_id2name($parentid, $this->device->deviceid); @@ -428,6 +429,11 @@ // Remove subfolders if (!empty($options'deleteSubFolders')) { $list = $this->listFolders($folderid); + + if (!is_array($list)) { + throw new Syncroton_Exception_Status_ItemOperations(Syncroton_Exception_Status_ItemOperations::ITEM_SERVER_ERROR); + } + foreach ($list as $folderid => $folder) { $foldername = $this->backend->folder_id2name($folderid, $this->device->deviceid); $folder = $this->getFolderObject($foldername); @@ -508,7 +514,7 @@ $oldEntry = $this->getObject($folderId, $serverId); if (empty($oldEntry)) { - throw new Syncroton_Exception_NotFound('id not found'); + throw new Syncroton_Exception_NotFound('entry not found'); } $entry = $this->toKolab($entry, $folderId, $oldEntry); @@ -670,7 +676,7 @@ */ protected function getChangesByRelations($folderid, $filter) { - if (!$this->tag_categories) { + if (isset($this->tag_categories) && !$this->tag_categories) { return; } @@ -897,7 +903,7 @@ */ public function getCountOfChanges(Syncroton_Backend_IContent $contentBackend, Syncroton_Model_IFolder $folder, Syncroton_Model_ISyncState $syncState) { - $allClientEntries = $contentBackend->getFolderState($this->device, $folder); + $allClientEntries = $contentBackend->getFolderState($this->device, $folder, $syncState->counter); $allServerEntries = $this->getServerEntries($folder->serverId, $folder->lastfiltertype); $changedEntries = $this->getChangedEntriesCount($folder->serverId, $syncState->lastsync, null, $folder->lastfiltertype); $addedEntries = array_diff($allServerEntries, $allClientEntries); @@ -922,7 +928,7 @@ return true; } - $allClientEntries = $contentBackend->getFolderState($this->device, $folder); + $allClientEntries = $contentBackend->getFolderState($this->device, $folder, $syncState->counter); // @TODO: Consider looping over all folders here, not in getServerEntries() and // getChangedEntriesCount(). This way we could break the loop and not check all folders @@ -947,6 +953,10 @@ { $folders = $this->extractFolders($folderid); + if (empty($folders)) { + return null; + } + foreach ($folders as $folderid) { $foldername = $this->backend->folder_id2name($folderid, $this->device->deviceid); $folder = $this->getFolderObject($foldername); @@ -1002,7 +1012,7 @@ } // convert categories into tags, save them after creating an object - if ($this->tag_categories) { + if (!empty($data'categories') && isset($this->tag_categories) && $this->tag_categories) { $tags = $data'categories'; unset($data'categories'); } @@ -1010,6 +1020,10 @@ $foldername = $this->backend->folder_id2name($folderid, $this->device->deviceid); $folder = $this->getFolderObject($foldername); + // Set User-Agent for saved objects + $app = kolab_sync::get_instance(); + $app->config->set('useragent', $app->app_name . ' ' . kolab_sync::VERSION); + if ($folder && $folder->valid && $folder->save($data)) { if (!empty($tags)) { $this->setKolabTags($data'uid', $tags); @@ -1032,11 +1046,15 @@ $folder = $this->getFolderObject($object'_mailbox'); // convert categories into tags, save them after updating an object - if ($this->tag_categories && array_key_exists('categories', $data)) { + if (isset($this->tag_categories) && $this->tag_categories && array_key_exists('categories', $data)) { $tags = (array) $data'categories'; unset($data'categories'); } + // Set User-Agent for saved objects + $app = kolab_sync::get_instance(); + $app->config->set('useragent', $app->app_name . ' ' . kolab_sync::VERSION); + if ($folder && $folder->valid && $folder->save($data)) { if (isset($tags)) { $this->setKolabTags($data'uid', $tags); @@ -1060,7 +1078,7 @@ $folder = $this->getFolderObject($object'_mailbox'); if ($folder && $folder->valid && $folder->delete($object'uid')) { - if ($this->tag_categories) { + if (isset($this->tag_categories) && $this->tag_categories) { $this->setKolabTags($object'uid', null); } @@ -1117,7 +1135,7 @@ $this->device->deviceid, $this->modelName, $this->isMultiFolder()); } - if ($parentid === null) { + if ($parentid === null || !is_array($this->imap_folders)) { return $this->imap_folders; } @@ -1247,10 +1265,12 @@ if ($count == 2 && $name_items0 == 'x-custom') { $value = null; - foreach ((array) $data'x-custom' as $val) { - if (is_array($val) && $val0 == $name_items1) { - $value = $val1; - break; + if (!empty($data'x-custom') && is_array($data'x-custom')) { + foreach ($data'x-custom' as $val) { + if (is_array($val) && $val0 == $name_items1) { + $value = $val1; + break; + } } } @@ -1314,7 +1334,8 @@ // custom properties if ($count == 2 && $name_items0 == 'x-custom') { - foreach ((array) $data'x-custom' as $idx => $val) { + $data'x-custom' = isset($data'x-custom') ? ((array) $data'x-custom') : array(); + foreach ($data'x-custom' as $idx => $val) { if (is_array($val) && $val0 == $name_items1) { $data'x-custom'$idx1 = $value; return; @@ -1445,16 +1466,17 @@ * @param int $type Result data type (to which the body will be converted, if specified). * One or array of Syncroton_Model_EmailBody constants. * - * @return string Body value + * @return string|null Body value */ protected function getBody($body, $type = null) { + $data = null; if ($body && $body->data) { $data = $body->data; } if (!$data || empty($type)) { - return; + return null; } $type = (array) $type; @@ -1505,7 +1527,7 @@ $real_length = $body_length = strlen($body); // truncate the body if needed - if (($truncateAt = $prefs$type'truncationSize') && $body_length > $truncateAt) { + if (isset($prefs$type'truncationSize') && ($truncateAt = $prefs$type'truncationSize') && $body_length > $truncateAt) { $body = mb_strcut($body, 0, $truncateAt); $body_length = strlen($body); @@ -1667,8 +1689,14 @@ */ protected function recurrence_to_kolab($data, $folderid, $timezone = null) { - if (!($data->recurrence instanceof Syncroton_Model_EventRecurrence) || !isset($data->recurrence->type)) { - return null; + if (!($data->recurrence instanceof Syncroton_Model_EventRecurrence) + && !($data->recurrence instanceof Syncroton_Model_TaskRecurrence) + ) { + return; + } + + if (!isset($data->recurrence->type)) { + return; } $recurrence = $data->recurrence; @@ -1745,35 +1773,39 @@ $ex_list = array(); // exceptions (modified occurences) - foreach ((array)$data'recurrence''EXCEPTIONS' as $exception) { - $exception'_mailbox' = $data'_mailbox'; + if (!empty($data'recurrence''EXCEPTIONS')) { + foreach ((array)$data'recurrence''EXCEPTIONS' as $exception) { + $exception'_mailbox' = $data'_mailbox'; - $ex = $this->getEntry($collection, $exception, true); - $date = clone ($exception'recurrence_date' ?: $ex'startTime'); + $ex = $this->getEntry($collection, $exception, true); + $date = clone ($exception'recurrence_date' ?: $ex'startTime'); - $ex'exceptionStartTime' = self::set_exception_time($date, $data'_start'); + $ex'exceptionStartTime' = self::set_exception_time($date, $data'_start'); - // remove fields not supported by Syncroton_Model_EventException - unset($ex'uID'); + // remove fields not supported by Syncroton_Model_EventException + unset($ex'uID'); - // @TODO: 'thisandfuture=true' is not supported in Activesync - // we'd need to slit the event into two separate events + // @TODO: 'thisandfuture=true' is not supported in Activesync + // we'd need to slit the event into two separate events - $ex_list = new Syncroton_Model_EventException($ex); + $ex_list = new Syncroton_Model_EventException($ex); + } } // exdate (deleted occurences) - foreach ((array)$data'recurrence''EXDATE' as $exception) { - if (!($exception instanceof DateTime)) { - continue; - } + if (!empty($data'recurrence''EXDATE')) { + foreach ((array)$data'recurrence''EXDATE' as $exception) { + if (!($exception instanceof DateTime)) { + continue; + } - $ex = array( - 'deleted' => 1, - 'exceptionStartTime' => self::set_exception_time($exception, $data'_start'), - ); + $ex = array( + 'deleted' => 1, + 'exceptionStartTime' => self::set_exception_time($exception, $data'_start'), + ); - $ex_list = new Syncroton_Model_EventException($ex); + $ex_list = new Syncroton_Model_EventException($ex); + } } return $ex_list; @@ -1790,17 +1822,20 @@ // handle exceptions from recurrence if (!empty($data->exceptions)) { foreach ($data->exceptions as $exception) { + $date = clone $exception->exceptionStartTime; + if ($timezone) { + $date->setTimezone($timezone); + } + if ($exception->deleted) { - $date = clone $exception->exceptionStartTime; - if ($timezone) { - $date->setTimezone($timezone); - } $date->setTime(0, 0, 0); $rrule'EXDATE' = $date; } else { $ex = $this->toKolab($exception, $folderid, null, $timezone); + $ex'recurrence_date' = $date; + if ($data->allDayEvent) { $ex'allday' = 1; }
View file
kolab-syncroton-2.3.10.tar.gz/lib/kolab_sync_data_calendar.php -> kolab-syncroton-2.4.2.tar.gz/lib/kolab_sync_data_calendar.php
Changed
@@ -116,6 +116,9 @@ const SENSITIVITY_PRIVATE = 2; const SENSITIVITY_CONFIDENTIAL = 3; + const KEY_DTSTAMP = 'x-custom.X-ACTIVESYNC-DTSTAMP'; + const KEY_REPLYTIME = 'x-custom.X-ACTIVESYNC-REPLYTIME'; + /** * Mapping of attendee status * @@ -181,21 +184,12 @@ $event = is_array($serverId) ? $serverId : $this->getObject($collection->collectionId, $serverId); $config = $this->getFolderConfig($event'_mailbox'); $result = array(); + $is_outlook = stripos($this->device->devicetype, 'outlook') !== false; + $is_android = stripos($this->device->devicetype, 'android') !== false; - // Timezone // Kolab Format 3.0 and xCal does support timezone per-date, but ActiveSync allows // only one timezone per-event. We'll use timezone of the start date - if ($event'start' instanceof DateTime) { - $timezone = $event'start'->getTimezone(); - - if ($timezone && ($tz_name = $timezone->getName()) != 'UTC') { - $tzc = kolab_sync_timezone_converter::getInstance(); - - if ($tz_name = $tzc->encodeTimezone($tz_name)) { - $result'timezone' = $tz_name; - } - } - } + $result'timezone' = kolab_sync_timezone_converter::encodeTimezoneFromDate($event'start'); // Calendar namespace fields foreach ($this->mapping as $key => $name) { @@ -233,11 +227,15 @@ break; case 'sensitivity': - $value = intval($this->sensitivityMap$value); + if (!empty($value)) { + $value = intval($this->sensitivityMap$value); + } break; case 'free_busy': - $value = $this->busyStatusMap$value; + if (!empty($value)) { + $value = $this->busyStatusMap$value; + } break; case 'description': @@ -283,27 +281,51 @@ } } + $resp_type = self::ATTENDEE_STATUS_UNKNOWN; + $user_rsvp = false; + // Attendees if (!empty($event'attendees')) { - foreach ($event'attendees' as $idx => $attendee) { - $att = array(); + $user_emails = $this->user_emails(); - if ($email = $attendee'email') { - $att'email' = $email; - } - else { + foreach ($event'attendees' as $idx => $attendee) { + if (empty($attendee'email')) { // In Activesync email is required continue; } - $att'name' = $attendee'name' ?: $email; + $email = $attendee'email'; + + $att = + 'email' => $email, + 'name' => !empty($attendee'name') ? $attendee'name' : $email, + ; + + $type = isset($attendee'role') ? $this->attendeeTypeMap$attendee'role' : null; + $status = isset($attendee'status') ? $this->attendeeStatusMap$attendee'status' : null; if ($this->asversion >= 12) { - $type = isset($attendee'role') ? $this->attendeeTypeMap$attendee'role' : null; - $status = isset($attendee'status') ? $this->attendeeStatusMap$attendee'status' : null; + if (isset($attendee'cutype') && strtolower($attendee'cutype') == 'resource') { + $att'attendeeType' = self::ATTENDEE_TYPE_RESOURCE; + } else { + $att'attendeeType' = $type ?: self::ATTENDEE_TYPE_REQUIRED; + } + $att'attendeeStatus' = $status ?: self::ATTENDEE_STATUS_UNKNOWN; + } - $att'attendeeType' = $type ? $type : self::ATTENDEE_TYPE_REQUIRED; - $att'attendeeStatus' = $status ? $status : self::ATTENDEE_STATUS_UNKNOWN; + if ($email && in_array_nocase($email, $user_emails)) { + $user_rsvp = !empty($attendee'rsvp'); + $resp_type = $status ?: self::ATTENDEE_STATUS_UNKNOWN; + + // Synchronize the attendee status to the event status to get the same behaviour as outlook. + if (($is_outlook || $is_android )&& isset($attendee'status')) { + if ($attendee'status' == 'ACCEPTED') { + $result'busyStatus' = self::BUSY_STATUS_BUSY; + } + if ($attendee'status' == 'TENTATIVE') { + $result'busyStatus' = self::BUSY_STATUS_TENTATIVE; + } + } } $result'attendees' = new Syncroton_Model_EventAttendee($att); @@ -311,11 +333,26 @@ } // Event meeting status - $this->meeting_status_from_kolab($collection, $event, $result); + $this->meeting_status_from_kolab($event, $result); // Recurrence (and exceptions) $this->recurrence_from_kolab($collection, $event, $result); + // RSVP status + $result'responseRequested' = $result'meetingStatus' == 3 && $user_rsvp ? 1 : 0; + $result'responseType' = $result'meetingStatus' == 3 ? $resp_type : null; + + // Appointment Reply Time (without it Outlook displays e.g. "Accepted on None") + if ($resp_type != self::ATTENDEE_STATUS_UNKNOWN) { + if ($reply_time = $this->getKolabDataItem($event, self::KEY_REPLYTIME)) { + $result'appointmentReplyTime' = new DateTime($reply_time, new DateTimeZone('UTC')); + } elseif (!empty($event'changed')) { + $reply_time = clone $event'changed'; + $reply_time->setTimezone(new DateTimeZone('UTC')); + $result'appointmentReplyTime' = $reply_time; + } + } + return $as_array ? $result : new Syncroton_Model_Event($result); } @@ -331,15 +368,33 @@ */ public function toKolab(Syncroton_Model_IEntry $data, $folderid, $entry = null, $timezone = null) { + $foldername = isset($entry'_mailbox') ? $entry'_mailbox' : $this->getFolderName($folderid); + if (empty($entry)) { + // If we don't have an existing event (not a modification) we nevertheless check for conflicts. + // This is necessary so we don't overwrite the server-side copy in case the client did not have it available + // when generating an Add command. + try { + $folder = $this->getFolderObject($foldername); + $entry = $folder->get_object($data->uID); + + if ($entry) { + $this->logger->debug('Found and existing event for UID: ' . $data->uID); + } + } catch (Exception $e) { + // uID is not available on exceptions, so we guard for that and silently ignore. + } + } $event = !empty($entry) ? $entry : array(); - $foldername = isset($event'_mailbox') ? $event'_mailbox' : $this->getFolderName($folderid); $config = $this->getFolderConfig($foldername); $is_exception = $data instanceof Syncroton_Model_EventException; $dummy_tz = str_repeat('A', 230) . '=='; $is_outlook = stripos($this->device->devicetype, 'outlook') !== false; + $is_android = stripos($this->device->devicetype, 'android') !== false; - // check data validity - $this->check_event($data); + // check data validity (of a new event) + if (empty($event)) { + $this->check_event($data); + } if (!empty($event'start') && ($event'start' instanceof DateTime)) { $old_timezone = $event'start'->getTimezone(); @@ -348,19 +403,20 @@ // Timezone if (!$timezone && isset($data->timezone) && $data->timezone != $dummy_tz) { $tzc = kolab_sync_timezone_converter::getInstance(); - $expected = $old_timezone ?: kolab_format::$timezone; + $expected = !empty($old_timezone) ? $old_timezone : kolab_format::$timezone; try { $timezone = $tzc->getTimezone($data->timezone, $expected->getName()); $timezone = new DateTimeZone($timezone); } catch (Exception $e) { + $this->logger->warn('Failed to convert the timezone information. UID: ' . $event'uid' . 'Timezone: ' . $data->timezone); $timezone = null; } } if (empty($timezone)) { - $timezone = $old_timezone ?: new DateTimeZone('UTC'); + $timezone = !empty($old_timezone) ? $old_timezone : new DateTimeZone('UTC'); } $event'allday' = 0; @@ -375,6 +431,11 @@ $value = $data->$key; + // Skip ghosted (unset) properties, (but make sure 'changed' timestamp is reset) + if ($value === null && $name != 'changed') { + continue; + } + switch ($name) { case 'changed': $value = null; @@ -404,12 +465,17 @@ case 'sensitivity': $map = array_flip($this->sensitivityMap); - $value = $map$value; + $value = isset($map$value) ? $map$value : null; break; case 'free_busy': + // Outlook sets the busy state to the attendance state, and we don't want to change the event state based on that. + // Outlook doesn't have the concept of an event state, so we just ignore this. + if ($is_outlook || $is_android) { + continue 2; + } $map = array_flip($this->busyStatusMap); - $value = $map$value; + $value = isset($map$value) ? $map$value : null; break; case 'description': @@ -426,7 +492,7 @@ // Try to fix allday events from Android // It doesn't set all-day flag but the period is a whole day - if (!$event'allday' && $event'end' && $event'start') { + if (empty($event'allday') && !empty($event'end') && !empty($event'start')) { $interval = @date_diff($event'start', $event'end'); if ($interval && $interval->format('%y%m%d%h%i%s') === '001000') { $event'allday' = 1; @@ -451,30 +517,41 @@ } // Organizer - if (!$is_exception && ($organizer_email = $data->organizerEmail)) { - $attendees = array( - 'role' => 'ORGANIZER', - 'name' => $data->organizerName, - 'email' => $organizer_email, - ); + if (!$is_exception) { + // Organizer specified + if ($organizer_email = $data->organizerEmail) { + $attendees = array( + 'role' => 'ORGANIZER', + 'name' => $data->organizerName, + 'email' => $organizer_email, + ); + } else if (!empty($event'attendees')) { + // Organizer not specified, use one from the original event if that's an update + foreach ($event'attendees' as $idx => $attendee) { + if (!empty($attendee'email') && !empty($attendee'role') && $attendee'role' == 'ORGANIZER') { + $organizer_email = $attendee'email'; + $attendees = array( + 'role' => 'ORGANIZER', + 'name' => $attendee'name' ?? '', + 'email' => $organizer_email, + ); + } + } + } } // Attendees - // Outlook 2013 sends a dummy update just after MeetingResponse has been processed, - // this update resets attendee status set in the MeetingResponse request. - // We ignore attendees data in such updates, they should not happen according to - // https://msdn.microsoft.com/en-us/library/office/hh428685(v=exchg.140).aspx - // but they will contain some data as alarms and free/busy status so we don't - // ignore them completely - if ($is_outlook && !empty($entry) && $data->timezone == $dummy_tz - && $data->responseRequested && !empty($data->attendees) - ) { + // Whenever Outlook sends dummy timezone it is an event where the user is an attendee. + // In these cases Attendees element is bogus: contains invalid status and does not + // contain all attendees. We have to ignore it. + if ($is_outlook && !$is_exception && $data->timezone === $dummy_tz) { + $this->logger->debug('Dummy outlook update detected, ignoring attendee changes.'); $attendees = $entry'attendees'; } else if (isset($data->attendees)) { - $statusMap = array_flip($this->attendeeStatusMap); foreach ($data->attendees as $attendee) { - if ($attendee->email && $attendee->email == $organizer_email) { + if (!empty($organizer_email) && $attendee->email && !strcasecmp($attendee->email, $organizer_email)) { + // skip the organizer continue; } @@ -493,6 +570,10 @@ 'email' => $attendee->email, ); + if (isset($attendee->attendeeType) && $attendee->attendeeType == self::ATTENDEE_TYPE_RESOURCE) { + $_attendee'cutype' = 'RESOURCE'; + } + if (isset($attendee->attendeeStatus)) { $_attendee'status' = $attendee->attendeeStatus ? array_search($attendee->attendeeStatus, $this->attendeeStatusMap) : null; if (!$_attendee'status') { @@ -515,23 +596,40 @@ } } - // Make sure the event has the organizer set - if (!$organizer_email && ($identity = kolab_sync::get_instance()->user->get_identity())) { - $attendees = array( - 'role' => 'ORGANIZER', - 'name' => $identity'name', - 'email' => $identity'email', - ); - } + // Outlook does not send the correct attendee status when changing between accepted and tentative, but it toggles the busyStatus. + if ($is_outlook || $is_android) { + $status = null; + if ($data->busyStatus == self::BUSY_STATUS_BUSY) { + $status = "ACCEPTED"; + } else if ($data->busyStatus == self::BUSY_STATUS_TENTATIVE) { + $status = "TENTATIVE"; + } - $event'attendees' = $attendees; - $event'categories' = $categories; + if ($status) { + $this->logger->debug("Updating our attendee status based on the busy status to {$status}."); + $emails = $this->user_emails(); + $this->find_and_update_attendee_status($attendees, $status, $emails); + } + } - // recurrence (and exceptions) if (!$is_exception) { + // Make sure the event has the organizer set + if (!$organizer_email && ($identity = kolab_sync::get_instance()->user->get_identity())) { + $attendees = array( + 'role' => 'ORGANIZER', + 'name' => $identity'name', + 'email' => $identity'email', + ); + } + + // recurrence (and exceptions) $event'recurrence' = $this->recurrence_to_kolab($data, $folderid, $timezone); } + $event'attendees' = $attendees; + $event'categories' = $categories; + $event'exceptions' = isset($event'recurrence''EXCEPTIONS') ? $event'recurrence''EXCEPTIONS' : array(); + // Bump SEQUENCE number on update (Outlook only). // It's been confirmed that any change of the event that has attendees specified // bumps SEQUENCE number of the event (we can see this in sent iTips). @@ -539,7 +637,7 @@ // is needed, e.g. when updating attendee status. // We try our best to bump the SEQUENCE only when expected if (!empty($entry) && !$is_exception && !empty($data->attendees) && $data->timezone != $dummy_tz) { - if ($last_update = $this->getKolabDataItem($event, 'x-custom.X-ACTIVESYNC-DTSTAMP')) { + if ($last_update = $this->getKolabDataItem($event, self::KEY_DTSTAMP)) { $last_update = new DateTime($last_update); } @@ -555,7 +653,7 @@ // the event modification time is not (re)set by the server, // we use the original Outlook's timestamp. if ($is_outlook && $data->dtStamp) { - $this->setKolabDataItem($event, 'x-custom.X-ACTIVESYNC-DTSTAMP', $data->dtStamp->format(DateTime::ATOM)); + $this->setKolabDataItem($event, self::KEY_DTSTAMP, $data->dtStamp->format(DateTime::ATOM)); } // This prevents kolab_format code to bump the sequence when not needed @@ -582,11 +680,8 @@ ); if ($status = $status_map$request->userResponse) { - // extract event data from the invitation - $event = $this->get_event_from_invitation($request); - - // find the event in calendar - $existing = $this->find_event_by_uid($event'uid'); + // extract event from the invitation + list($event, $existing) = $this->get_event_from_invitation($request); /* switch ($status) { case 'ACCEPTED': $event'free_busy' = 'busy'; break; @@ -594,6 +689,10 @@ case 'DECLINED': $event'free_busy' = 'free'; break; } */ + // Store response timestamp for further use + $reply_time = new DateTime('now', new DateTimeZone('UTC')); + $this->setKolabDataItem($event, self::KEY_REPLYTIME, $reply_time->format('Ymd\THis\Z')); + // Update/Save the event if (empty($existing)) { $folder = $this->save_event($event, $status); @@ -645,19 +744,26 @@ } /** - * Get an event from the invitation email + * Get an event from the invitation email or calendar folder */ protected function get_event_from_invitation(Syncroton_Model_MeetingResponse $request) { - // Limitations: - // 1. The meeting request may be in an iTip or the calendar event - // For now we support iTips only here - // 2. LongId might be used instead of RequestId, this is not supported + // Limitation: LongId might be used instead of RequestId, this is not supported + if ($request->requestId) { $mail_class = new kolab_sync_data_email($this->device, $this->syncTimeStamp); + // Event from an invitation email if ($event = $mail_class->get_invitation_event($request->requestId)) { - return $event; + // find the event in calendar + $existing = $this->find_event_by_uid($event'uid'); + + return array($event, $existing); + } + + // Event from calendar folder + if ($event = $this->getObject($request->collectionId, $request->requestId, $folder)) { + return array($event, $event); } throw new Syncroton_Exception_Status_MeetingResponse(Syncroton_Exception_Status_MeetingResponse::INVALID_REQUEST); @@ -697,8 +803,6 @@ throw new Syncroton_Exception_Status_MeetingResponse(Syncroton_Exception_Status_MeetingResponse::INVALID_REQUEST); } - $this->update_attendee_status($old, $status); - if ($event'free_busy') { $old'free_busy' = $event'free_busy'; } @@ -707,10 +811,15 @@ // to an iTip request with bumped SEQUENCE $old'sequence' += 1; - // TODO: Free/busy trigger? + // Copy new custom properties + if (!empty($event'x-custom')) { + foreach ($event'x-custom' as $key => $val) { + $old'x-custom'$key = $val; + } + } // Update the event - return $this->save_event($old); + return $this->save_event($old, $status); } /** @@ -720,7 +829,7 @@ protected function save_event(&$event, $status = null) { // Find default folder to which we'll save the event - if (empty($event'_mailbox')) { + if (!isset($event'_mailbox')) { $folders = $this->listFolders(); $storage = rcube::get_instance()->get_storage(); @@ -750,6 +859,8 @@ $this->update_attendee_status($event, $status); } + // TODO: Free/busy trigger? + if (isset($event'_mailbox')) { $folder = $this->getFolderObject($event'_mailbox'); @@ -762,27 +873,42 @@ } /** - * Update the attendee status of the user + * Update the attendee status of the user matching $emails */ - protected function update_attendee_status(&$event, $status) + protected function find_and_update_attendee_status(&$attendees, $status, $emails) { - $organizer = null; - $emails = $this->user_emails(); - - foreach ((array) $event'attendees' as $i => $attendee) { - if ($attendee'role' == 'ORGANIZER') { - $organizer = $attendee; - } - else if ($attendee'email' && in_array_nocase($attendee'email', $emails)) { - $event'attendees'$i'status' = $status; - $event'attendees'$i'rsvp' = false; - $event_attendee = $attendee; + $found = false; + foreach ((array) $attendees as $i => $attendee) { + if (!empty($attendee'email') + && (empty($attendee'role') || $attendee'role' != 'ORGANIZER') + && in_array_nocase($attendee'email', $emails) + ) { + $attendees$i'status' = $status; + $attendees$i'rsvp' = false; + $this->logger->debug('Updating existing attendee: ' . $attendee'email' . ' status: ' . $status); + $found = true; } } + return $found; + } - if (!$event_attendee) { - $this->logger->warn('MeetingResponse on an event where the user is not an attendee. UID: ' . $event'uid'); - throw new Syncroton_Exception_Status_MeetingResponse(Syncroton_Exception_Status_MeetingResponse::MEETING_ERROR); + /** + * Update the attendee status of the user + */ + protected function update_attendee_status(&$event, $status) + { + $emails = $this->user_emails(); + + if (!$this->find_and_update_attendee_status($event'attendees', $status, $emails)) { + $this->logger->debug('Adding new attendee ' . $emails0 . ' status: ' . $status); + // Add the user to the attendees list + $event'attendees' = array( + 'role' => 'OPT-PARTICIPANT', + 'name' => '', + 'email' => $emails0, + 'status' => $status, + 'rsvp' => false, + ); } } @@ -824,7 +950,7 @@ /** * Set MeetingStatus according to event data */ - protected function meeting_status_from_kolab($collection, $event, &$result) + protected function meeting_status_from_kolab($event, &$result) { // 0 - The event is an appointment, which has no attendees. // 1 - The event is a meeting and the user is the meeting organizer. @@ -868,14 +994,14 @@ } } - if ($value && $value instanceof DateTime) { - if ($event'start' && ($interval = $event'start'->diff($value))) { + if (!empty($value) && $value instanceof DateTime) { + if (!empty($event'start') && ($interval = $event'start'->diff($value))) { if ($interval->invert && !$interval->m && !$interval->y) { return intval(round($interval->s/60) + $interval->i + $interval->h * 60 + $interval->d * 60 * 24); } } } - else if ($value && preg_match('/^(-+*)PT*(0-9+)(WDHMS)$/', $value, $matches)) { + else if (!empty($value) && preg_match('/^(-+*)PT*(0-9+)(WDHMS)$/', $value, $matches)) { $value = intval($matches2); if ($value && $matches1 != '-') { @@ -899,7 +1025,7 @@ protected function to_kolab_alarm($value, $event) { if ($value === null || $value === '') { - return (array) $event'valarms'; + return isset($event'valarms') ? (array) $event'valarms' : array(); } $valarms = array(); @@ -907,7 +1033,7 @@ if (!empty($event'valarms')) { foreach ($event'valarms' as $alarm) { - if (!$current && in_array($alarm'action', array('DISPLAY', 'AUDIO'))) { + if (empty($current) && in_array($alarm'action', array('DISPLAY', 'AUDIO'))) { $current = $alarm; } else { @@ -917,8 +1043,8 @@ } $valarms = array( - 'action' => $current'action' ?: 'DISPLAY', - 'description' => $current'description' ?: '', + 'action' => !empty($current'action') ? $current'action' : 'DISPLAY', + 'description' => !empty($current'description') ? $current'description' : '', 'trigger' => sprintf('-PT%dM', $value), ); @@ -978,7 +1104,7 @@ { // Calendar namespace fields foreach (array('allday', 'start', 'end', 'location', 'recurrence') as $key) { - if ($event$key != $old$key) { + if ((isset($event$key) ? $event$key : null) != (isset($old$key) ? $old$key : null)) { // Comparing recurrence is tricky as there can be differences in default // value handling. Let's try to handle most common cases if ($key == 'recurrence' && $this->fixed_recurrence($event) == $this->fixed_recurrence($old)) {
View file
kolab-syncroton-2.3.10.tar.gz/lib/kolab_sync_data_contacts.php -> kolab-syncroton-2.4.2.tar.gz/lib/kolab_sync_data_contacts.php
Changed
@@ -168,7 +168,7 @@ if ($value) { // ActiveSync limits photo size to 48KB (of base64 encoded string) if (strlen($value) * 1.33 > 48 * 1024) { - continue; + continue 2; } } break; @@ -192,7 +192,8 @@ // email address(es): email1Address, email2Address, email3Address for ($x=0; $x<3; $x++) { - if ($email = $data'email'$x) { + if (!empty($data'email'$x)) { + $email = $data'email'$x; if (is_array($email)) { $email = $email'address'; }
View file
kolab-syncroton-2.3.10.tar.gz/lib/kolab_sync_data_email.php -> kolab-syncroton-2.4.2.tar.gz/lib/kolab_sync_data_email.php
Changed
@@ -50,6 +50,8 @@ 'to' => 'to', ); + static $memory_accumulated = 0; + /** * Special folder type/name map * @@ -91,6 +93,9 @@ */ protected $folderType = Syncroton_Command_FolderSync::FOLDERTYPE_MAIL_USER_CREATED; + private $lastsync_folder = null; + private $lastsync_time = null; + /** * the constructor @@ -113,6 +118,52 @@ } /** + * Encode a globalObjId according to https://interoperability.blob.core.windows.net/files/MS-ASEMAIL/%5bMS-ASEMAIL%5d-150526.pdf 2.2.2.3 + * + * @param array $data An array with the data to encode + * + * @return string the encoded globalObjId + */ + public static function encodeGlobalObjId(array $data): string + { + $classid = "040000008200e00074c5b7101a82e008"; + if (!empty($data'data')) { + $payload = $data'data'; + } else { + $uid = $data'uid'; + $payload = "vCal-Uid\1\0\0\0{$uid}\0"; + } + + $packed = pack( + "H32nCCPx8Va*", + $classid, + $data'year' ?? 0, + $data'month' ?? 0, + $data'day' ?? 0, + $data'now' ?? 0, + strlen($payload), + $payload + ); + + return base64_encode($packed); + } + + /** + * Decode a globalObjId according to https://interoperability.blob.core.windows.net/files/MS-ASEMAIL/%5bMS-ASEMAIL%5d-150526.pdf 2.2.2.3 + * + * @param string the encoded globalObjId + * + * @return array An array with the decoded data + */ + public static function decodeGlobalObjId(string $globalObjId): array + { + $unpackString = 'H32classid/nyear/Cmonth/Cday/Pnow/x8/Vbytecount/a*data'; + $decoded = unpack($unpackString, base64_decode($globalObjId)); + $decoded'uid' = substr($decoded'data', strlen("vCal-Uid\1\0\0\0"), -1); + return $decoded; + } + + /** * Creates model object * * @param Syncroton_Model_SyncCollection $collection Collection data @@ -131,6 +182,10 @@ $headers = $message->headers; // rcube_message_header + $this->storage->set_folder($message->folder); + + $this->logger->debug(sprintf("Processing message %s (size: %.2f MB)", $serverId, $headers->size / 1024 / 1024)); + // Calendar namespace fields foreach ($this->mapping as $key => $name) { $value = null; @@ -187,6 +242,8 @@ 'flagType' => 'FollowUp', 'status' => Syncroton_Model_EmailFlag::STATUS_ACTIVE, )); + } else { + $result'flag' = new Syncroton_Model_EmailFlag(); } // Importance/Priority @@ -258,7 +315,7 @@ $truncateAt = $prefs$type'truncationSize'; } - $preview = (int) $prefs$type'preview'; + $preview = (int) (isset($prefs$type'preview') ? $prefs$type'preview' : 0); $airSyncBaseType = $type; break; @@ -272,17 +329,26 @@ // In Sync examples there's one in which bodyPreferences is not defined // in such case Truncated=1 and there's no body sent to the client // only it's estimated size + $isTruncated = 0; if (empty($prefs)) { $messageBody = ''; - $real_length = $message->size; + $real_length = $headers->size; $truncateAt = 0; $body_length = 0; $isTruncated = 1; } else if ($airSyncBaseType == Syncroton_Command_Sync::BODY_TYPE_MIME) { - $messageBody = $this->storage->get_raw_body($message->uid); + // Check if we have enough memory to handle the message + $messageBody = $this->message_mem_check($message, $headers->size); + static::$memory_accumulated += $headers->size; + + if (empty($messageBody)) { + $messageBody = $this->storage->get_raw_body($message->uid); + } + // make the source safe (Bug #2715, #2757) $messageBody = kolab_sync_message::recode_message($messageBody); + // strip out any non utf-8 characters $messageBody = rcube_charset::clean($messageBody); $real_length = $body_length = strlen($messageBody); @@ -319,26 +385,91 @@ $result'nativeBodyType' = $message->has_html_part() ? 2 : 1; // Message class + $result'messageClass' = 'IPM.Note'; + $result'contentClass' = 'urn:content-classes:message'; + if ($headers->ctype == 'multipart/signed' - && count($message->attachments) == 1 && $message->attachments0->mimetype == 'application/pkcs7-signature' + && !empty($message->parts1) + && $message->parts1->mimetype == 'application/pkcs7-signature' ) { $result'messageClass' = 'IPM.Note.SMIME.MultipartSigned'; } else if ($headers->ctype == 'application/pkcs7-mime' || $headers->ctype == 'application/x-pkcs7-mime') { $result'messageClass' = 'IPM.Note.SMIME'; } - else { - $result'messageClass' = 'IPM.Note'; - } + // FIXME disabled for now because it results in broken invitations on android (gmail client) and ios + // else if ($event = $this->get_invitation_event_from_message($message)) { + else if (false) { + $result'messageClass' = 'IPM.Schedule.Meeting.Request'; + $result'contentClass' = 'urn:content-classes:calendarmessage'; + + $meeting = array(); + + $meeting'allDayEvent' = $event'allday' ?? null ? 1 : 0; + $meeting'startTime' = self::date_from_kolab($event'start'); + $meeting'dtStamp' = self::date_from_kolab($event'dtstamp' ?? null); + $meeting'endTime' = self::date_from_kolab($event'end' ?? null); + $meeting'location' = $event'location' ?? null; + $meeting'instanceType' = Syncroton_Model_EmailMeetingRequest::TYPE_NORMAL; + + if (!empty($event'recurrence_date')) { + $meeting'recurrenceId' = self::date_from_kolab($event'recurrence_date'); + if (!empty($event'status') && $event'status' == 'CANCELLED') { + $meeting'instanceType' = Syncroton_Model_EmailMeetingRequest::TYPE_RECURRING_EXCEPTION; + } else { + $meeting'instanceType' = Syncroton_Model_EmailMeetingRequest::TYPE_RECURRING_SINGLE; + } + } else if (!empty($event'recurrence')) { + $meeting'instanceType' = Syncroton_Model_EmailMeetingRequest::TYPE_RECURRING_MASTER; + // TODO: MeetingRequest recurrence is different that the one in Calendar + // $this->recurrence_from_kolab($collection, $event, $meeting); + } - $result'contentClass' = 'urn:content-classes:message'; + // Organizer + if (!empty($event'attendees')) { + foreach ($event'attendees' as $idx => $attendee) { + if (!empty($attendee'role') && $attendee'role' == 'ORGANIZER' && !empty($attendee'email')) { + $meeting'organizer' = $attendee'email'; + break; + } + } + } + + $fileTime = ($event'start'->getTimestamp() + 11644473600) * 10000000; // 1.1.1600 - 1.1.1970 difference in seconds. Converted to microseconds + + // Kolab Format 3.0 and xCal does support timezone per-date, but ActiveSync allows + // only one timezone per-event. We'll use timezone of the start date + $meeting'timeZone' = kolab_sync_timezone_converter::encodeTimezoneFromDate($event'start'); + $meeting'globalObjId' = self::encodeGlobalObjId( + 'uid' => $event'uid', + 'year' => intval($event'start'->format('Y')), + 'month' => intval($event'start'->format('n')), + 'day' => intval($event'start'->format('j')), + 'now' => $fileTime, + ); + + // TODO handle other methods + if ($event'_method' == 'REQUEST') { + $meeting'meetingMessageType' = Syncroton_Model_EmailMeetingRequest::MESSAGE_TYPE_REQUEST; + } else { + $meeting'meetingMessageType' = Syncroton_Model_EmailMeetingRequest::MESSAGE_TYPE_NORMAL; + } + + // New time proposals aren't supported by Kolab. + // This disables the UI elements related to this on the client side + $meeting'disallowNewTimeProposal' = 1; + + $result'meetingRequest' = new Syncroton_Model_EmailMeetingRequest($meeting); + } // Categories (Tags) - if ($this->tag_categories) { + if (isset($this->tag_categories) && $this->tag_categories) { // convert kolab tags into categories $result'categories' = $this->getKolabTags($message); } + $is_ios = preg_match('/(iphone|ipad)/i', $this->device->devicetype); + // attachments $attachments = array_merge($message->attachments, $message->inline_parts); if (!empty($attachments)) { @@ -347,6 +478,10 @@ foreach ($attachments as $attachment) { $att = array(); + if ($is_ios && !empty($event) && $attachment->mime_id == $event'_mime_id') { + continue; + } + $filename = rcube_charset::clean($attachment->filename); if (empty($filename) && $attachment->mimetype == 'text/html') { $filename = 'HTML Part'; @@ -551,7 +686,7 @@ } // Use COPYUID feature (RFC2359) to get the new UID of the copied message - $copyuid = $this->storage->conn->data'COPYUID'; + $copyuid = isset($this->storage->conn->data'COPYUID') ? $this->storage->conn->data'COPYUID' : null; if (is_array($copyuid) && ($uid = $copyuid1)) { return $this->createMessageId($dest_id, $uid); @@ -564,13 +699,22 @@ * @param string $folderId Folder identifier * @param Syncroton_Model_IEntry $entry Entry * - * @return array + * @return string ID of the created entry */ public function createEntry($folderId, Syncroton_Model_IEntry $entry) { - // Throw exception here for better handling of unsupported - // entry creation, it can be object of class Email or SMS here - throw new Syncroton_Exception_Status_Sync(Syncroton_Exception_Status_Sync::INVALID_ITEM); + // Creating emails is not normally supported like this, but is implemented for testing purposes + $foldername = $this->backend->folder_id2name($folderId, $this->device->deviceid); + + $flag = !empty($entry->read) ? 'SEEN' : 'UNSEEN'; + $uid = $this->storage->save_message($foldername, $entry->body->data, '', false, $flag); + + if (!$uid) { + $this->logger->error("Error while storing the message " . $this->storage->get_error_str()); + throw new Syncroton_Exception_Status_Sync(Syncroton_Exception_Status_Sync::SYNC_SERVER_ERROR); + } + + return $this->createMessageId($folderId, $uid); } /** @@ -582,30 +726,34 @@ */ public function updateEntry($folderId, $serverId, Syncroton_Model_IEntry $entry) { - $msg = $this->parseMessageId($serverId); - $message = $this->getObject($serverId); + $msg = $this->parseMessageId($serverId); - if (empty($message)) { + if (empty($msg)) { throw new Syncroton_Exception_Status_Sync(Syncroton_Exception_Status_Sync::SYNC_SERVER_ERROR); } - $is_flagged = !empty($message->headers->flags'FLAGGED'); + if (isset($entry->categories)) { + // Read the message headers only when they are needed + $message = $this->getObject($serverId); + + if (empty($message)) { + throw new Syncroton_Exception_Status_Sync(Syncroton_Exception_Status_Sync::SYNC_SERVER_ERROR); + } + } // Read status change if (isset($entry->read)) { // here we update only Read flag - $flag = (((int)$entry->read != 1) ? 'UN' : '') . 'SEEN'; + $flag = !empty($entry->read) ? 'SEEN' : 'UNSEEN'; $this->storage->set_flag($msg'uid', $flag, $msg'foldername'); } // Flag change - if (isset($entry->flag) && (empty($entry->flag) || empty($entry->flag->flagType))) { - if ($is_flagged) { + if (isset($entry->flag)) { + if (empty($entry->flag) || empty($entry->flag->flagType)) { $this->storage->set_flag($msg'uid', 'UNFLAGGED', $msg'foldername'); } - } - else if (!$is_flagged && !empty($entry->flag)) { - if ($entry->flag->flagType && preg_match('/follow\s*up/i', $entry->flag->flagType)) { + else if (preg_match('/follow\s*up/i', $entry->flag->flagType)) { $this->storage->set_flag($msg'uid', 'FLAGGED', $msg'foldername'); } } @@ -617,7 +765,7 @@ } /** - * delete entry + * Delete an email (or move to Trash) * * @param string $folderId * @param string $serverId @@ -632,17 +780,26 @@ throw new Syncroton_Exception_Status_Sync(Syncroton_Exception_Status_Sync::SYNC_SERVER_ERROR); } + // Note: If DeletesAsMoves is not specified in the request, its default is 1 (true). + // move message to trash folder - if ($collection->deletesAsMoves + if ((!isset($collection->deletesAsMoves) || !empty($collection->deletesAsMoves)) && strlen($trash) && $trash != $msg'foldername' && $this->storage->folder_exists($trash) ) { $this->storage->move_message($msg'uid', $trash, $msg'foldername'); } - // set delete flag + // delete the message else { - $this->storage->set_flag($msg'uid', 'DELETED', $msg'foldername'); + // According to the ActiveSync spec. "If the DeletesAsMoves element is set to false, + // the deletion is PERMANENT.", therefore we delete the message, and not flag as deleted. + $this->storage->delete_message($msg'uid', $msg'foldername'); + + // FIXME: We could consider acting according to the 'flag_for_deletion' setting. + // Don't forget about 'read_when_deleted' setting then. + // $this->storage->set_flag($msg'uid', 'DELETED', $msg'foldername'); + // $this->storage->set_flag($msg'uid', 'SEEN', $msg'foldername'); } } @@ -851,13 +1008,14 @@ // If previous HIGHESTMODSEQ doesn't exist we can't get changes // We can only get folder's HIGHESTMODSEQ value and store it for the next try // Skip search if HIGHESTMODSEQ didn't change - if ($folder_data'HIGHESTMODSEQ') { + if (!empty($folder_data'HIGHESTMODSEQ')) { $modseq_data$foldername = $folder_data'HIGHESTMODSEQ'; - if ($modseq_data$foldername != $modseq$foldername) { + $modseq_old = isset($modseq$foldername) ? $modseq$foldername : null; + if ($modseq_data$foldername != $modseq_old) { $modseq_update = true; - if ($modseq && $modseq$foldername) { + if ($modseq && $modseq_old) { $modified = true; - $filter_str .= " MODSEQ " . ($modseq$foldername + 1); + $filter_str .= " MODSEQ " . ($modseq_old + 1); } } } @@ -1009,7 +1167,7 @@ } } - $search_str .= ' ' . str_repeat(' OR', $search_count-1) . $search; + $search_str = str_repeat(' OR', $search_count-1) . $search; // search messages in current folder $search = $storage->search_once($foldername, $search_str); @@ -1171,9 +1329,11 @@ if (empty($folders)) { $folders = $this->listFolders(); - if (is_array($folders)) { - $folders = array_keys($folders); + if (!is_array($folders)) { + throw new Syncroton_Exception_Status(Syncroton_Exception_Status::SERVER_ERROR); } + + $folders = array_keys($folders); } return array($folders, $search_str); @@ -1229,12 +1389,17 @@ $entryid = $entryid'itemId'; } + if (!is_string($entryid) || !strpos($entryid, '::')) { + return; + } + + // Note: the id might be in a form of <folder>::<uid>::<part_id> list($folderid, $uid) = explode('::', $entryid); + $foldername = $this->backend->folder_id2name($folderid, $this->device->deviceid); if ($foldername === null || $foldername === false) { - // @TODO exception? - return null; + return; } return array( @@ -1280,21 +1445,27 @@ */ protected function getMessagePartBody($message, $part, $html = false) { - // Check if we have enough memory to handle the message in it - // @FIXME: we need up to 5x more memory than the body - if (!rcube_utils::mem_check($part->size * 5)) { + if (empty($part->size) || !isset($part->mime_id)) { + // TODO: Throw an exception? return ''; } - $body = $message->get_part_body($part->mime_id, true); + // Check if we have enough memory to handle the message in it + $body = $this->message_mem_check($message, $part->size, false); + + if ($body !== false) { + $body = $message->get_part_body($part->mime_id, true); + } // message is cached but not exists, or other error if ($body === false) { return ''; } + $ctype_secondary = !empty($part->ctype_secondary) ? $part->ctype_secondary : null; + if ($html) { - if ($part->ctype_secondary == 'html') { + if ($ctype_secondary == 'html') { // charset was converted to UTF-8 in rcube_storage::get_message_part(), // change/add charset specification in HTML accordingly $meta = '<meta http-equiv="Content-Type" content="text/html; charset='.RCUBE_CHARSET.'" />'; @@ -1307,14 +1478,14 @@ $body = '<head>' . $meta . '</head>' . $body; } } - else if ($part->ctype_secondary == 'enriched') { + else if ($ctype_secondary == 'enriched') { $body = rcube_enriched::to_html($body); } else { // Roundcube >= 1.2 if (class_exists('rcube_text2html')) { - $flowed = $part->ctype_parameters'format' == 'flowed'; - $delsp = $part->ctype_parameters'delsp' == 'yes'; + $flowed = isset($part->ctype_parameters'format') && $part->ctype_parameters'format' == 'flowed'; + $delsp = isset($part->ctype_parameters'delsp') && $part->ctype_parameters'delsp' == 'yes'; $options = array('flowed' => $flowed, 'wrap' => false, 'delsp' => $delsp); $text2html = new rcube_text2html($body, false, $options); $body = '<html><body>' . $text2html->get_html() . '</body></html>'; @@ -1325,17 +1496,20 @@ } } else { - if ($part->ctype_secondary == 'enriched') { + if ($ctype_secondary == 'enriched') { $body = rcube_enriched::to_html($body); $part->ctype_secondary = 'html'; } - if ($part->ctype_secondary == 'html') { + if ($ctype_secondary == 'html') { $txt = new rcube_html2text($body, false, true); $body = $txt->get_text(); } else { - if ($part->ctype_secondary == 'plain' && $part->ctype_parameters'format' == 'flowed') { + if ($ctype_secondary == 'plain' + && !empty($part->ctype_parameters'format') + && $part->ctype_parameters'format' == 'flowed' + ) { $body = rcube_mime::unfold_flowed($body); } } @@ -1422,7 +1596,7 @@ if ($force) { $config->resolve_members($relation, $force); - $this->tag_rts$tag'uid' = time(); + $this->tag_rts$relation'uid' = time(); } $selected = !empty($tags) && in_array($relation'name', $tags); @@ -1595,7 +1769,7 @@ foreach ($lines as $line) { // don't wrap already quoted lines - if ($line0 == '>') { + if (isset($line0) && $line0 == '>') { $line = '>' . rtrim($line); } else if (mb_strlen($line) > $max) { @@ -1624,21 +1798,103 @@ /** * Returns calendar event data from the iTip invitation attached to a mail message */ + public function get_invitation_event_from_message($message) + { + // Parse the message and find iTip attachments + $libcal = libcalendaring::get_instance(); + $libcal->mail_message_load(array('object' => $message)); + $ical_objects = $libcal->get_mail_ical_objects(); + + // We support only one event in the iTip + foreach ($ical_objects as $mime_id => $event) { + if ($event'_type' == 'event') { + $event'_method' = $ical_objects->method; + $event'_mime_id' = $ical_objects->mime_id; + + return $event; + } + } + + return null; + } + + /** + * Returns calendar event data from the iTip invitation attached to a mail message + */ public function get_invitation_event($messageId) { // Get the mail message object if ($message = $this->getObject($messageId)) { - // Parse the message and find iTip attachments - $libcal = libcalendaring::get_instance(); - $libcal->mail_message_load(array('object' => $message)); - $ical_objects = $libcal->get_mail_ical_objects(); - - // We support only one event in the iTip - foreach ($ical_objects as $mime_id => $event) { - if ($event'_type' == 'event') { - return $event; + return $this->get_invitation_event_from_message($message); + } + return null; + } + + + private function mem_check($need) + { + $mem_limit = parse_bytes(ini_get('memory_limit')); + $memory = static::$memory_accumulated; + return $mem_limit > 0 && $memory + $need > $mem_limit ? false : true; + } + + /** + * Checks if the message can be processed, depending on its size and + * memory_limit, otherwise throws an exception or returns fake body. + */ + protected function message_mem_check($message, $size, $result = null) + { + static $memory_rised; + + // @FIXME: we need up to 5x more memory than the body + // Note: Biggest memory multiplication happens in recode_message() + // and the Syncroton engine (which also does not support passing bodies + // as streams). It also happens when parsing the plain/html text body + // in getMessagePartBody() though the footprint there is probably lower. + + if (!$this->mem_check($size * 5)) { + // If we already rised the memory we throw an exception, so the message + // will be synchronized in the next run (then we might have enough memory) + if ($memory_rised) { + throw new Syncroton_Exception_MemoryExhausted; + } + + $memory_rised = true; + $memory_max = 512; // maximum in MB + $memory_limit = round(parse_bytes(ini_get('memory_limit')) / 1024 / 1024); // current limit (in MB) + $memory_add = round($size * 5 / 1024 / 1024); // how much we need (in MB) + $memory_needed = min($memory_limit + $memory_add, $memory_max) . "M"; + + if ($memory_limit < $memory_max) { + $this->logger->debug("Setting memory_limit=$memory_needed"); + + if (ini_set('memory_limit', $memory_needed) !== false) { + // Memory has been rised, check again + if ($this->mem_check($size * 5)) { + return; + } } } + + $this->logger->warn("Not enough memory. Using fake email body."); + + if ($result !== null) { + return $result; + } + + // Let's return a fake message. If we return an empty body Outlook + // will not list the message at all. This way user can do something + // with the message (flag, delete, move) and see the reason why it's fake + // and importantly see its subject, sender, etc. + // TODO: Localization? + $msg = "This message is too large for ActiveSync."; + // $msg .= "See https://kb.kolabenterprise.com/documentation/some-place for more information."; + + // Get original message headers + $headers = $this->storage->get_raw_headers($message->uid); + + // Build a fake message with original headers, but changed body + return kolab_sync_message::fake_message($headers, $msg); } } }
View file
kolab-syncroton-2.3.10.tar.gz/lib/kolab_sync_data_gal.php -> kolab-syncroton-2.4.2.tar.gz/lib/kolab_sync_data_gal.php
Changed
@@ -153,7 +153,7 @@ } if (strlen($value) > $maxsize) { - continue; + continue 2; } $value = new Syncroton_Model_GALPicture(array(
View file
kolab-syncroton-2.3.10.tar.gz/lib/kolab_sync_data_tasks.php -> kolab-syncroton-2.4.2.tar.gz/lib/kolab_sync_data_tasks.php
Changed
@@ -117,7 +117,7 @@ $result = array(); // Completion status (required) - $result'complete' = intval($task'status' == 'COMPLETED' || $task'complete' == 100); + $result'complete' = intval($task'status' ?? null == 'COMPLETED' || $task'complete' ?? null == 100); // Calendar namespace fields foreach ($this->mapping as $key => $name) { @@ -140,7 +140,9 @@ break; case 'sensitivity': - $value = intval($this->sensitivityMap$value); + if (!empty($value)) { + $value = intval($this->sensitivityMap$value); + } break; case 'priority': @@ -156,7 +158,9 @@ } // convert kolab tags into categories - $result'categories' = $this->getKolabTags($task'uid', $result'categories'); + if (!empty($result'categories')) { + $result'categories' = $this->getKolabTags($task'uid', $result'categories'); + } // Recurrence $this->recurrence_from_kolab($collection, $task, $result, 'Task'); @@ -164,6 +168,23 @@ return $as_array ? $result : new Syncroton_Model_Task($result); } + + + /** + * Apply a timezone matching the utc offset. + */ + private static function applyTimezone($value, $utcValue) + { + $tzc = kolab_sync_timezone_converter::getInstance(); + $tz = $tzc->getOffsetTimezone($value, $utcValue); + if ($tz) { + //Setting the timezone will change the time, so we set it on the utc variant instead to end up with the same time in the new timezone. + $value = $utcValue; + $value->setTimezone($tz); + } + return $value; + } + /** * convert contact from xml to libkolab array * @@ -186,9 +207,26 @@ $value = $data->$key; switch ($name) { + + case 'due': + case 'start': + // We expect to always get regular and utc variants, so we only need to take one into account. + if ($key == 'utcStartDate' || $key == 'utcDueDate') { + continue 2; + } + if ($value) { + if ($name =='due' && $data->utcDueDate) { + $value = static::applyTimezone($value, $data->utcDueDate); + } + if ($name =='start' && $data->utcStartDate) { + $value = static::applyTimezone($value, $data->utcStartDate); + } + } + break; + case 'sensitivity': $map = array_flip($this->sensitivityMap); - $value = $map$value; + $value = $map$value ?? 'none' ?? self::SENSITIVITY_NORMAL; break; case 'description': @@ -211,9 +249,13 @@ $task'status' = 'COMPLETED'; $task'complete' = 100; } - else if (isset($data->complete) && ($task'status' == 'COMPLETED' || $task'complete' == 100)) { - $task'status' = ''; - $task'complete' = 0; + else if (isset($data->complete)) { + if ((!empty($task'status') && $task'status' == 'COMPLETED') + || (!empty($task'complete') && $task'complete' == 100) + ) { + $task'status' = ''; + $task'complete' = 0; + } } // recurrence
View file
kolab-syncroton-2.3.10.tar.gz/lib/kolab_sync_logger.php -> kolab-syncroton-2.4.2.tar.gz/lib/kolab_sync_logger.php
Changed
@@ -29,12 +29,22 @@ { public $mode; + protected $logfile; + protected $format; + protected $log_dir; + protected $username; + /** * Constructor */ function __construct($mode = null) { - $this->mode = intval($mode); + $rcube = rcube::get_instance(); + + $this->mode = intval($mode); + $this->logfile = $rcube->config->get('activesync_log_file'); + $this->format = $rcube->config->get('log_date_format', 'd-M-Y H:i:s O'); + $this->log_dir = $rcube->config->get('log_dir'); $r = new ReflectionClass($this); $this->_priorities = $r->getConstants(); @@ -49,6 +59,17 @@ } /** + * Check whether debug logging is enabled + * + * @return bool + */ + public function hasDebug() + { + // This is what we check in self::log() below + return !empty($this->log_dir) && $this->mode >= self::NOTICE; + } + + /** * Message logger * * @param string $message Log message @@ -56,11 +77,6 @@ */ public function log($message, $method, $extras = null) { - $rcube = rcube::get_instance(); - $logfile = $rcube->config->get('activesync_log_file'); - $format = $rcube->config->get('log_date_format', 'd-M-Y H:i:s O'); - $log_dir = $rcube->get_user_log_dir() ?: $rcube->config->get('log_dir'); - if (is_numeric($method)) { $mode = $method; $method = array_search($method, $this->_priorities); @@ -69,10 +85,20 @@ $mode = $this->_priorities$method; } + // Don't log messages with lower prio than the configured one if ($mode > $this->mode) { return; } + // Don't log debug messages if it's disabled e.g. by per_user_logging + if (empty($this->log_dir) && $mode >= self::NOTICE) { + return; + } + + $rcube = rcube::get_instance(); + $log_dir = $this->log_dir ?: $rcube->config->get('log_dir'); + $logfile = $this->logfile; + // if log_file is configured all logs will go to it // otherwise use separate file for info/debug and warning/error if (!$logfile) { @@ -121,7 +147,7 @@ } } - $date = rcube_utils::date_format($format); + $date = rcube_utils::date_format($this->format); $logline = sprintf("%s: %s %s\n", $date, $method, $message); if ($fp = @fopen($logfile, 'a')) { @@ -144,4 +170,12 @@ { $this->username = $username; } + + /** + * Set log directory + */ + public function set_log_dir($dir) + { + $this->log_dir = $dir; + } }
View file
kolab-syncroton-2.3.10.tar.gz/lib/kolab_sync_message.php -> kolab-syncroton-2.4.2.tar.gz/lib/kolab_sync_message.php
Changed
@@ -161,7 +161,7 @@ $headers = $this->headers; $mailto = $headers'To'; - $headers'User-Agent' .= $rcube->app_name . ' ' . kolab_sync::VERSION; + $headers'User-Agent' = $rcube->app_name . ' ' . kolab_sync::VERSION; if ($agent = $rcube->config->get('useragent')) { $headers'User-Agent' .= '/' . $agent; } @@ -193,75 +193,39 @@ // remove empty headers $headers = array_filter($headers); - // send thru SMTP server using custom SMTP library - if ($rcube->config->get('smtp_server')) { - $smtp_headers = $headers; - // generate list of recipients - $recipients = array(); - - if (!empty($headers'To')) - $recipients = $headers'To'; - if (!empty($headers'Cc')) - $recipients = $headers'Cc'; - if (!empty($headers'Bcc')) - $recipients = $headers'Bcc'; - - // remove Bcc header - unset($smtp_headers'Bcc'); - - // send message - if (!is_object($rcube->smtp)) { - $rcube->smtp_init(true); - } + $smtp_headers = $headers; - $sent = $rcube->smtp->send_mail($headers'From', $recipients, $smtp_headers, $this->body, $smtp_opts); - $smtp_response = $rcube->smtp->get_response(); - $smtp_error = $rcube->smtp->get_error(); + // generate list of recipients + $recipients = array(); - // log error - if (!$sent) { - rcube::raise_error(array('code' => 800, 'type' => 'smtp', - 'line' => __LINE__, 'file' => __FILE__, - 'message' => "SMTP error: ".join("\n", $smtp_response)), true, false); - } + if (!empty($headers'To')) + $recipients = $headers'To'; + if (!empty($headers'Cc')) + $recipients = $headers'Cc'; + if (!empty($headers'Bcc')) + $recipients = $headers'Bcc'; + + if (empty($headers'To') && empty($headers'Cc')) { + $headers'To' = 'undisclosed-recipients:;'; } - // send mail using PHP's mail() function - else { - $mail_headers = $headers; - $delim = $rcube->config->header_delimiter(); - $subject = $headers'Subject'; - $to = $headers'To'; - - // unset some headers because they will be added by the mail() function - unset($mail_headers'To', $mail_headers'Subject'); - - // #1485779 - if (strtoupper(substr(PHP_OS, 0, 3)) === 'WIN') { - if (preg_match_all('/<(^@+@^>+)>/', $to, $m)) { - $to = implode(', ', $m1); - } - } - foreach ($mail_headers as $header => $header_value) { - $mail_headers$header = $header . ': ' . $header_value; - } - $header_str = rtrim(implode("\r\n", $mail_headers)); + // remove Bcc header + unset($smtp_headers'Bcc'); - if ($delim != "\r\n") { - $header_str = str_replace("\r\n", $delim, $header_str); - $msg_body = str_replace("\r\n", $delim, $this->body); - $to = str_replace("\r\n", $delim, $to); - $subject = str_replace("\r\n", $delim, $subject); - } + // send message + if (!is_object($rcube->smtp)) { + $rcube->smtp_init(true); + } - if (ini_get('safe_mode')) { - $sent = mail($to, $subject, $msg_body, $header_str); - } - else { - $from = rcube_mime::decode_address_list($headers'From', 1, false, null, true); - $from = $from0; - $sent = mail($to, $subject, $msg_body, $header_str, "-f$from"); - } + $sent = $rcube->smtp->send_mail($headers'From', $recipients, $smtp_headers, $this->body, $smtp_opts); + $smtp_response = $rcube->smtp->get_response(); + $smtp_error = $rcube->smtp->get_error(); + + // log error + if (!$sent) { + rcube::raise_error(array('code' => 800, 'type' => 'smtp', + 'line' => __LINE__, 'file' => __FILE__, + 'message' => "SMTP error: ".join("\n", $smtp_response)), true, false); } if ($sent) { @@ -270,25 +234,20 @@ // remove MDN headers after sending unset($headers'Return-Receipt-To', $headers'Disposition-Notification-To'); - // get all recipients - if ($headers'Cc') - $mailto .= ' ' . $headers'Cc'; - if ($headers'Bcc') - $mailto .= ' ' . $headers'Bcc'; - if (preg_match_all('/<(^@+@^>+)>/', $mailto, $m)) - $mailto = implode(', ', array_unique($m1)); - if ($rcube->config->get('smtp_log')) { - rcube::write_log('sendmail', sprintf("User %s %s; Message for %s; %s", + // get all recipient addresses + $mailto = rcube_mime::decode_address_list(implode(',', $recipients), null, false, null, true); + + rcube::write_log('sendmail', sprintf("User %s %s; Message %s for %s; %s", $rcube->get_user_name(), - $_SERVER'REMOTE_ADDR', - $mailto, - !empty($smtp_response) ? join('; ', $smtp_response) : '')); + rcube_utils::remote_addr(), + $headers'Message-ID', + implode(', ', $mailto), + !empty($smtp_response) ? implode('; ', $smtp_response) : '') + ); } } - unset($headers'Bcc'); - $this->headers = $headers; return $sent; @@ -310,12 +269,12 @@ $message = stream_get_contents($message); } - list($headers, $message) = preg_split('/\r?\n\r?\n/', $message, 2, PREG_SPLIT_NO_EMPTY); + list($headers, $message) = array_pad(preg_split('/\r?\n\r?\n/', $message, 2, PREG_SPLIT_NO_EMPTY), 2, ''); $hdrs = self::parse_headers($headers); // multipart message - if (preg_match('/boundary="?(a-z0-9-\'\(\)+_\,\.\/:=\? +)"?/i', $hdrs'Content-Type', $matches)) { + if (preg_match('/boundary="?(a-z0-9-\'\(\)+_\,\.\/:=\? +)"?/i', $hdrs'Content-Type' ?? '', $matches)) { $boundary = '--' . $matches1; $message = explode($boundary, $message); @@ -327,7 +286,7 @@ } // single part - $enc = strtolower($hdrs'Content-Transfer-Encoding'); + $enc = !empty($hdrs'Content-Transfer-Encoding') ? strtolower($hdrs'Content-Transfer-Encoding') : null; // do nothing if already encoded if ($enc != 'quoted-printable' && $enc != 'base64') { @@ -347,6 +306,29 @@ } /** + * Creates a fake plain text message source with predefined headers and body + * + * @param string $headers Message headers + * @param string $body Plain text body + * + * @return string Message source + */ + public static function fake_message($headers, $body = '') + { + $hdrs = self::parse_headers($headers); + $result = ''; + + $hdrs'Content-Type' = 'text/plain; charset=UTF-8'; + $hdrs'Content-Transfer-Encoding' = 'quoted-printable'; + + foreach ($hdrs as $header => $header_value) { + $result .= $header . ': ' . $header_value . "\r\n"; + } + + return $result . "\r\n" . self::encode($body, 'quoted-printable'); + } + + /** * MIME message parser * * @param string|resource $message MIME message source @@ -406,9 +388,11 @@ // Unify char-case of header names $headers = array(); foreach ($lines as $line) { - list($field, $string) = explode(':', $line, 2); - if ($field = self::normalize_header_name($field)) { - $headers$field = trim($string); + if (strpos($line, ':') !== false) { + list($field, $string) = explode(':', $line, 2); + if ($field = self::normalize_header_name($field)) { + $headers$field = trim($string); + } } }
View file
kolab-syncroton-2.3.10.tar.gz/lib/kolab_sync_timezone_converter.php -> kolab-syncroton-2.4.2.tar.gz/lib/kolab_sync_timezone_converter.php
Changed
@@ -57,6 +57,14 @@ ) ); + + protected $_legacyTimezones = array( + // This is an outdated timezone that outlook keeps sending because of an outdate timezone database on windows + 'Lv///0kAcgBhAG4AIABTAHQAYQBuAGQAYQByAGQAIABUAGkAbQBlAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAkABAADABcAOwA7AOcDAAAAAEkAcgBhAG4AIABEAGEAeQBsAGkAZwBoAHQAIABUAGkAbQBlAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAMAAwAEAAAAAAAAAAAAxP///w==' => array( + 'Asia/Tehran' => '+0330' + ) + ); + /** * don't use the constructor. Use the singleton. * @@ -87,6 +95,43 @@ return self::$_instance; } + + /** + * Returns a timezone with an offset matching the time difference + * of $dt from $referenceDt. + * + * If set and matching the offset, kolab_format::$timezone is preferred. + * + * @param DateTime $dt The date time value for which we + * calculate the offset. + * @param DateTime $referenceDt The reference value, for instance in UTC. + * + * @return DateTimeZone|null + */ + public function getOffsetTimezone($dt, $referenceDt) + { + $interval = $referenceDt->diff($dt); + $tz = new DateTimeZone($interval->format('%R%H%I')); //e.g. +0200 + $utcOffset = $tz->getOffset($dt); + + //Prefer the configured timezone if it matches the offset. + if (kolab_format::$timezone) { + if (kolab_format::$timezone->getOffset($dt) == $utcOffset) { + return kolab_format::$timezone; + } + } + + //Look for any timezone with a matching offset. + foreach (DateTimeZone::listIdentifiers() as $timezoneIdentifier) { + $timezone = new DateTimeZone($timezoneIdentifier); + if ($timezone->getOffset($dt) == $utcOffset) { + return $timezone; + } + } + return null; + } + + /** * Returns a list of timezones that match to the {@param $_offsets} * @@ -103,6 +148,9 @@ if (is_string($_offsets) && isset($this->_knownTimezones$_offsets)) { $timezones = $this->_knownTimezones$_offsets; } + elseif (is_string($_offsets) && isset($this->_legacyTimezones$_offsets)) { + $timezones = $this->_legacyTimezones$_offsets; + } else { if (is_string($_offsets)) { // unpack timezone info to array @@ -114,18 +162,12 @@ } $this->_setDefaultStartDateIfEmpty($_offsets); - $cacheId = $this->_getCacheId('timezones', $_offsets); - $timezones = $this->_loadFromCache($cacheId); - - if (!is_array($timezones)) { - $timezones = array(); - foreach (DateTimeZone::listIdentifiers() as $timezoneIdentifier) { - $timezone = new DateTimeZone($timezoneIdentifier); - if (false !== ($matchingTransition = $this->_checkTimezone($timezone, $_offsets))) { - $timezones$timezoneIdentifier = $matchingTransition'abbr'; - } + $timezones = array(); + foreach (DateTimeZone::listIdentifiers() as $timezoneIdentifier) { + $timezone = new DateTimeZone($timezoneIdentifier); + if (false !== ($matchingTransition = $this->_checkTimezone($timezone, $_offsets))) { + $timezones$timezoneIdentifier = $matchingTransition'abbr'; } - $this->_saveInCache($timezones, $cacheId); } } @@ -175,6 +217,29 @@ return $this->_packTimezoneInfo($offsets); } + + /** + * Returns an encoded timezone representation from $date + * + * @param DateTime $date The date with the timezone to encode + * + * @return string encoded timezone + */ + public static function encodeTimezoneFromDate($date) + { + if ($date instanceof DateTime) { + $timezone = $date->getTimezone(); + + if ($timezone && ($tz_name = $timezone->getName()) != 'UTC') { + $tzc = self::getInstance(); + if ($tz_name = $tzc->encodeTimezone($tz_name, $date->format('Y-m-d'))) { + return $tz_name; + } + } + } + return null; + } + /** * Get offsets for given timezone * @@ -187,36 +252,30 @@ { $this->_setStartDate($_startDate); - $cacheId = $this->_getCacheId('offsets', array($_timezone)); - - if (false === ($offsets = $this->_loadFromCache($cacheId))) { - $offsets = $this->_getOffsetsTemplate(); + $offsets = $this->_getOffsetsTemplate(); - try { - $timezone = new DateTimeZone($_timezone); - } - catch (Exception $e) { - return null; - } + try { + $timezone = new DateTimeZone($_timezone); + } + catch (Exception $e) { + return null; + } - list($standardTransition, $daylightTransition) = $this->_getTransitionsForTimezoneAndYear($timezone, $this->_startDate'year'); + list($standardTransition, $daylightTransition) = $this->_getTransitionsForTimezoneAndYear($timezone, $this->_startDate'year'); - if ($standardTransition) { - $offsets'bias' = $standardTransition'offset'/60*-1; - if ($daylightTransition) { - $offsets = $this->_generateOffsetsForTransition($offsets, $standardTransition, 'standard'); - $offsets = $this->_generateOffsetsForTransition($offsets, $daylightTransition, 'daylight'); + if ($standardTransition) { + $offsets'bias' = $standardTransition'offset'/60*-1; + if ($daylightTransition) { + $offsets = $this->_generateOffsetsForTransition($offsets, $standardTransition, 'standard', $timezone); + $offsets = $this->_generateOffsetsForTransition($offsets, $daylightTransition, 'daylight', $timezone); - //@todo how do we get the standardBias (is usually 0)? - //$offsets'standardBias' = ... + //@todo how do we get the standardBias (is usually 0)? + //$offsets'standardBias' = ... - $offsets'daylightBias' = ($daylightTransition'offset' - $standardTransition'offset')/60*-1; - $offsets'standardHour' -= $offsets'daylightBias' / 60; - $offsets'daylightHour' += $offsets'daylightBias' / 60; - } + $offsets'daylightBias' = ($daylightTransition'offset' - $standardTransition'offset')/60*-1; + $offsets'standardHour' -= $offsets'daylightBias' / 60; + $offsets'daylightHour' += $offsets'daylightBias' / 60; } - - $this->_saveInCache($offsets, $cacheId); } return $offsets; @@ -225,28 +284,29 @@ /** * Get offsets for timezone transition * - * @param array $_offsets Timezone offsets - * @param array $_transition Timezone transition information - * @param string $_type 'standard' or 'daylight' + * @param array $_offsets Timezone offsets + * @param array $_transition Timezone transition information + * @param string $_type Transition type: 'standard' or 'daylight' + * @param DateTimeZone $_timezone Timezone of the transition * * @return array */ - protected function _generateOffsetsForTransition(array $_offsets, array $_transition, $_type) + protected function _generateOffsetsForTransition(array $_offsets, array $_transition, $_type, $_timezone) { - $transitionDateParsed = new DateTime($_transition'time'); + $transitionDate = new DateTime($_transition'time', $_timezone); if ($_transition'offset') { - $transitionDateParsed->modify($_transition'offset' . ' seconds'); + $transitionDate->modify($_transition'offset' . ' seconds'); } - $_offsets$_type . 'Month' = (int) $transitionDateParsed->format('n'); - $_offsets$_type . 'DayOfWeek' = (int) $transitionDateParsed->format('w'); - $_offsets$_type . 'Minute' = (int) $transitionDateParsed->format('i'); - $_offsets$_type . 'Hour' = (int) $transitionDateParsed->format('G'); + $_offsets$_type . 'Month' = (int) $transitionDate->format('n'); + $_offsets$_type . 'DayOfWeek' = (int) $transitionDate->format('w'); + $_offsets$_type . 'Minute' = (int) $transitionDate->format('i'); + $_offsets$_type . 'Hour' = (int) $transitionDate->format('G'); for ($i=5; $i>0; $i--) { - if ($this->_isNthOcurrenceOfWeekdayInMonth($_transition'ts', $i)) { - $_offsets$_type . 'Day' = $i; + if ($this->_isNthOcurrenceOfWeekdayInMonth($transitionDate, $i)) { + $_offsets$_type . 'Week' = $i; break; }; } @@ -257,22 +317,21 @@ /** * Test if the weekday of the given {@param $_timestamp} is the {@param $_occurence}th occurence of this weekday within its month. * - * @param int $_timestamp + * @param DateTime $_datetime * @param int $_occurence 1 to 5, where 5 indicates the final occurrence during the month if that day of the week does not occur 5 times * * @return bool */ - protected function _isNthOcurrenceOfWeekdayInMonth($_timestamp, $_occurence) + protected function _isNthOcurrenceOfWeekdayInMonth($_datetime, $_occurence) { if ($_occurence <= 1) { return true; } - $original = new DateTime('@'.$_timestamp); - $orig = $original->format('n'); + $orig = $_datetime->format('n'); if ($_occurence == 5) { - $modified = clone($original); + $modified = clone($_datetime); $modified->modify('1 week'); $mod = $modified->format('n'); @@ -280,7 +339,7 @@ return $mod > $orig || ($mod == 1 && $orig == 12); } - $modified = clone($original); + $modified = clone($_datetime); $modified->modify(sprintf('-%d weeks', $_occurence - 1)); $mod = $modified->format('n'); @@ -288,7 +347,7 @@ return false; } - $modified = clone($original); + $modified = clone($_datetime); $modified->modify(sprintf('-%d weeks', $_occurence)); $mod = $modified->format('n'); @@ -305,7 +364,7 @@ * * @return bool */ - protected function _checkTransition($_standardTransition, $_daylightTransition, $_offsets) + protected function _checkTransition($_standardTransition, $_daylightTransition, $_offsets, $tz) { if (empty($_standardTransition) || empty($_offsets)) { return false; @@ -314,7 +373,7 @@ $standardOffset = ($_offsets'bias' + $_offsets'standardBias') * 60 * -1; // check each condition in a single if statement and break the chain when one condition is not met - for performance reasons - if ($standardOffset == $_standardTransition'offset' ) { + if ($standardOffset == $_standardTransition'offset') { if (empty($_offsets'daylightMonth') && (empty($_daylightTransition) || empty($_daylightTransition'isdst'))) { // No DST @@ -326,17 +385,24 @@ // the milestone is sending a positive value for daylightBias while it should send a negative value $daylightOffsetMilestone = ($_offsets'bias' + ($_offsets'daylightBias' * -1) ) * 60 * -1; - if ($daylightOffset == $_daylightTransition'offset' || $daylightOffsetMilestone == $_daylightTransition'offset') { - $standardParsed = getdate($_standardTransition'ts'); - $daylightParsed = getdate($_daylightTransition'ts'); - - if ($standardParsed'mon' == $_offsets'standardMonth' && - $daylightParsed'mon' == $_offsets'daylightMonth' && - $standardParsed'wday' == $_offsets'standardDayOfWeek' && - $daylightParsed'wday' == $_offsets'daylightDayOfWeek' + if ( + !empty($_daylightTransition) + && ($daylightOffset == $_daylightTransition'offset' || $daylightOffsetMilestone == $_daylightTransition'offset') + ) { + // date-time input here contains UTC timezone specifier (+0000), + // we have to convert the date to the requested timezone afterwards. + $standardDate = new DateTime($_standardTransition'time'); + $daylightDate = new DateTime($_daylightTransition'time'); + $standardDate->setTimezone($tz); + $daylightDate->setTimezone($tz); + + if ($standardDate->format('n') == $_offsets'standardMonth' && + $daylightDate->format('n') == $_offsets'daylightMonth' && + $standardDate->format('w') == $_offsets'standardDayOfWeek' && + $daylightDate->format('w') == $_offsets'daylightDayOfWeek' ) { - return $this->_isNthOcurrenceOfWeekdayInMonth($_daylightTransition'ts', $_offsets'daylightDay') && - $this->_isNthOcurrenceOfWeekdayInMonth($_standardTransition'ts', $_offsets'standardDay'); + return $this->_isNthOcurrenceOfWeekdayInMonth($daylightDate, $_offsets'daylightWeek') && + $this->_isNthOcurrenceOfWeekdayInMonth($standardDate, $_offsets'standardWeek'); } } } @@ -352,10 +418,20 @@ */ protected function _unpackTimezoneInfo($_packedTimezoneInfo) { - $timezoneUnpackString = 'lbias/a64standardName/vstandardYear/vstandardMonth/vstandardDayOfWeek/vstandardDay/vstandardHour/vstandardMinute/vstandardSecond/vstandardMilliseconds/lstandardBias/a64daylightName/vdaylightYear/vdaylightMonth/vdaylightDayOfWeek/vdaylightDay/vdaylightHour/vdaylightMinute/vdaylightSecond/vdaylightMilliseconds/ldaylightBias'; + $timezoneUnpackString = 'lbias/a64standardName/vstandardYear/vstandardMonth/vstandardDayOfWeek/vstandardWeek/vstandardHour/vstandardMinute/vstandardSecond/vstandardMilliseconds/lstandardBias' + . '/a64daylightName/vdaylightYear/vdaylightMonth/vdaylightDayOfWeek/vdaylightWeek/vdaylightHour/vdaylightMinute/vdaylightSecond/vdaylightMilliseconds/ldaylightBias'; $timezoneInfo = unpack($timezoneUnpackString, base64_decode($_packedTimezoneInfo)); + if ($timezoneInfo'standardHour' == 23 && $timezoneInfo'standardMilliseconds' == 999 + && $timezoneInfo'standardMinute' == 59 && $timezoneInfo'standardSecond' == 59 + ) { + $timezoneInfo'standardHour' = 24; + $timezoneInfo'standardMinute' = 0; + $timezoneInfo'standardSecond' = 0; + $timezoneInfo'standardMilliseconds' = 0; + } + return $timezoneInfo; } @@ -371,6 +447,16 @@ return null; } + // According to e.g. https://docs.microsoft.com/en-us/windows/win32/api/minwinbase/ns-minwinbase-systemtime, + // 24 is not allowed in the Hour field, and consequently Outlook can't deal with it. + // This is the same workaround that Outlook applies. + if ($_timezoneInfo'standardHour' == 24) { + $_timezoneInfo'standardHour' = 23; + $_timezoneInfo'standardMinute' = 59; + $_timezoneInfo'standardSecond' = 59; + $_timezoneInfo'standardMilliseconds' = 999; + } + $packed = pack( "la64vvvvvvvvla64vvvvvvvvl", $_timezoneInfo'bias', @@ -378,7 +464,7 @@ $_timezoneInfo'standardYear', $_timezoneInfo'standardMonth', $_timezoneInfo'standardDayOfWeek', - $_timezoneInfo'standardDay', + $_timezoneInfo'standardWeek', $_timezoneInfo'standardHour', $_timezoneInfo'standardMinute', $_timezoneInfo'standardSecond', @@ -388,7 +474,7 @@ $_timezoneInfo'daylightYear', $_timezoneInfo'daylightMonth', $_timezoneInfo'daylightDayOfWeek', - $_timezoneInfo'daylightDay', + $_timezoneInfo'daylightWeek', $_timezoneInfo'daylightHour', $_timezoneInfo'daylightMinute', $_timezoneInfo'daylightSecond', @@ -415,7 +501,7 @@ 'standardYear' => 0, 'standardMonth' => 0, 'standardDayOfWeek' => 0, - 'standardDay' => 0, + 'standardWeek' => 0, 'standardHour' => 0, 'standardMinute' => 0, 'standardSecond' => 0, @@ -425,7 +511,7 @@ 'daylightYear' => 0, 'daylightMonth' => 0, 'daylightDayOfWeek' => 0, - 'daylightDay' => 0, + 'daylightWeek' => 0, 'daylightHour' => 0, 'daylightMinute' => 0, 'daylightSecond' => 0, @@ -444,8 +530,8 @@ protected function _validateOffsets($value) { // validate $value - if ((!empty($value'standardMonth') || !empty($value'standardDay') || !empty($value'daylightMonth') || !empty($value'daylightDay')) && - (empty($value'standardMonth') || empty($value'standardDay') || empty($value'daylightMonth') || empty($value'daylightDay')) + if ((!empty($value'standardMonth') || !empty($value'standardWeek') || !empty($value'daylightMonth') || !empty($value'daylightWeek')) && + (empty($value'standardMonth') || empty($value'standardWeek') || empty($value'daylightMonth') || empty($value'daylightWeek')) ) { // It is not possible not set standard offsets without setting daylight offsets and vice versa return false; @@ -475,7 +561,7 @@ } else if (is_int($_startDate)) { $startDateParsed'ts' = $_startDate; - $startDateParsed'string' = strftime('%F', $_startDate); + $startDateParsed'string' = date('Y-m-d', $_startDate); } else { $this->_setDefaultStartDateIfEmpty(); @@ -524,7 +610,7 @@ { list($standardTransition, $daylightTransition) = $this->_getTransitionsForTimezoneAndYear($timezone, $this->_startDate'year'); - if ($this->_checkTransition($standardTransition, $daylightTransition, $offsets)) { + if ($this->_checkTransition($standardTransition, $daylightTransition, $offsets, $timezone)) { return $standardTransition; } @@ -554,8 +640,8 @@ } foreach ($transitions as $index => $transition) { - if (strftime('%Y', $transition'ts') == $_year) { - if (isset($transitions$index+1) && strftime('%Y', $transitions$index'ts') == strftime('%Y', $transitions$index+1'ts')) { + if (date('Y', $transition'ts') == $_year) { + if (isset($transitions$index+1) && date('Y', $transitions$index'ts') == date('Y', $transitions$index+1'ts')) { $daylightTransition = $transition'isdst' ? $transition : $transitions$index+1; $standardTransition = $transition'isdst' ? $transitions$index+1 : $transition; } @@ -572,39 +658,4 @@ return array($standardTransition, $daylightTransition); } - - protected function _getCacheId($_prefix, $_offsets) - { - return $_prefix . md5(serialize($_offsets)); - } - - protected function _loadFromCache($key) - { - if ($cache = $this->getCache) { - return $cache->get($key); - } - - return false; - } - - protected function _saveInCache($value, $key) - { - if ($cache = $this->getCache) { - $cache->set($key, $value); - } - } - - /** - * Getter for the cache engine object - */ - protected function getCache() - { - if ($this->cache === null) { - $rcube = rcube::get_instance(); - $cache = $rcube->get_cache_shared('activesync'); - $this->cache = $cache ? $cache : false; - } - - return $this->cache; - } }
View file
kolab-syncroton-2.3.10.tar.gz/tests/body_converter.php -> kolab-syncroton-2.4.2.tar.gz/tests/body_converter.php
Changed
@@ -1,12 +1,7 @@ <?php -class body_converter extends PHPUnit_Framework_TestCase +class body_converter extends PHPUnit\Framework\TestCase { - function setUp() - { - } - - function data_html_to_text() { return array(
View file
kolab-syncroton-2.3.10.tar.gz/tests/data.php -> kolab-syncroton-2.4.2.tar.gz/tests/data.php
Changed
@@ -1,6 +1,6 @@ <?php -class data extends PHPUnit_Framework_TestCase +class data extends PHPUnit\Framework\TestCase { /** * Test for kolab_sync_data::recurrence_to_kolab()
View file
kolab-syncroton-2.3.10.tar.gz/tests/data_calendar.php -> kolab-syncroton-2.4.2.tar.gz/tests/data_calendar.php
Changed
@@ -1,6 +1,6 @@ <?php -class data_calendar extends PHPUnit_Framework_TestCase +class data_calendar extends PHPUnit\Framework\TestCase { /** * Test for kolab_sync_data_calendar::from_kolab_alarm()
View file
kolab-syncroton-2.3.10.tar.gz/tests/data_tasks.php -> kolab-syncroton-2.4.2.tar.gz/tests/data_tasks.php
Changed
@@ -1,6 +1,6 @@ <?php -class data_tasks extends PHPUnit_Framework_TestCase +class data_tasks extends PHPUnit\Framework\TestCase { function data_prio() {
View file
kolab-syncroton-2.4.2.tar.gz/tests/globalid_converter.php
Added
@@ -0,0 +1,37 @@ +<?php + +class globalid_converter extends PHPUnit\Framework\TestCase +{ + /** + * Test GlobalObjId encoding/decoding + */ + function test_globalobjid() + { + // https://learn.microsoft.com/en-us/openspecs/exchange_server_protocols/ms-asemail/e7424ddc-dd10-431e-a0b7-5c794863370e + $input = 'BAAAAIIA4AB0xbcQGoLgCAAAAAAAAAAAAAAAAAAAAAAAAAAAMwAAAHZDYWwtVWlkAQAAAHs4MTQxMkQzQy0yQTI0LTRFOUQtQjIwRS0xMUY3QkJFOTI3OTl9AA=='; + $output = kolab_sync_data_email::decodeGlobalObjId($input); + + $this->assertSame(51, $output'bytecount'); + $this->assertSame('{81412D3C-2A24-4E9D-B20E-11F7BBE92799}', $output'uid'); + + $encoded = kolab_sync_data_email::encodeGlobalObjId($output); + $this->assertSame($encoded, $input); + + $input = 'BAAAAIIA4AB0xbcQGoLgCAfUCRDgQMnBJoXEAQAAAAAAAAAAEAAAAAvw7UtuTulOnjnjhns3jvM='; + $output = kolab_sync_data_email::decodeGlobalObjId($input); + + $this->assertSame(16, $output'bytecount'); + $this->assertSame(2004, $output'year'); + $this->assertSame(9, $output'month'); + $this->assertSame(16, $output'day'); + $this->assertSame(127373090979660000, $output'now'); + + // This is how the "now" value is interpreted + // $winSecs = (int)($output'now' / 10000000); // convert microseconds to seconds + // $unixTimestamp = ($winSecs - 11644473600); // subtract 1.1.1600 - 1.1.1970 difference in seconds + // print(date(DateTime::RFC822, $unixTimestamp)); + + $encoded = kolab_sync_data_email::encodeGlobalObjId($output); + $this->assertSame($encoded, $input); + } +}
View file
kolab-syncroton-2.3.10.tar.gz/tests/message.php -> kolab-syncroton-2.4.2.tar.gz/tests/message.php
Changed
@@ -1,12 +1,7 @@ <?php -class message extends PHPUnit_Framework_TestCase +class message extends PHPUnit\Framework\TestCase { - function setUp() - { - } - - /** * Test message parsing and headers setting */
View file
kolab-syncroton-2.3.10.tar.gz/tests/phpunit.xml -> kolab-syncroton-2.4.2.tar.gz/tests/phpunit.xml
Changed
@@ -7,8 +7,10 @@ <file>data.php</file> <file>data_calendar.php</file> <file>data_tasks.php</file> + <file>globalid_converter.php</file> <file>message.php</file> <file>timezone_converter.php</file> + <file>wbxml.php</file> </testsuite> </testsuites> </phpunit>
View file
kolab-syncroton-2.3.10.tar.gz/tests/timezone_converter.php -> kolab-syncroton-2.4.2.tar.gz/tests/timezone_converter.php
Changed
@@ -1,47 +1,75 @@ <?php -class timezone_converter extends PHPUnit_Framework_TestCase +class timezone_converter extends PHPUnit\Framework\TestCase { - function setUp() + function test_list_timezones() { - } +// date_default_timezone_set('America/Los_Angeles'); + $converter = kolab_sync_timezone_converter::getInstance(); + $output = $converter->getListOfTimezones('AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAoAAAAEAAIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAMAAAAFAAEAAAAAAAAAxP///w=='); - function test_list_timezones() - { - $converter = timezone_converter_test::getInstance(); + $this->assertTrue(is_array($output)); + $this->assertSame(array(), $output); - $input = 'AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAoAAAAEAAIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAMAAAAFAAEAAAAAAAAAxP///w=='; - $output = $converter->getListOfTimezones($input); + $converter = kolab_sync_timezone_converter::getInstance(); + $output = $converter->getListOfTimezones('xP///0MAZQBuAHQAcgBhAGwAIABFAHUAcgBvAHAAZQAgAFMAdABhAG4AZABhAHIAZAAgAFQAaQBtAGUAAAAAAAAAAAAAAAoAAAAFAAMAAAAAAAAAAAAAAEMAZQBuAHQAcgBhAGwAIABFAHUAcgBvAHAAZQAgAEQAYQB5AGwAaQBnAGgAdAAgAFQAaQBtAGUAAAAAAAAAAAAAAAMAAAAFAAIAAAAAAAAAxP///w=='); $this->assertTrue(is_array($output)); - } + $this->assertTrue(isset($output'Europe/Warsaw')); - function test_get_timezone() - { - $converter = timezone_converter_test::getInstance(); - $datetime = new DateTime('2017-01-01T12:00:00Z'); + $converter = kolab_sync_timezone_converter::getInstance(); + $output = $converter->getListOfTimezones('4AEAAFAAYQBjAGkAZgBpAGMAIABTAHQAYQBuAGQAYQByAGQAIABUAGkAbQBlAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAsAAAABAAIAAAAAAAAAAAAAAFAAYQBjAGkAZgBpAGMAIABEAGEAeQBsAGkAZwBoAHQAIABUAGkAbQBlAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAMAAAACAAIAAAAAAAAAxP///w=='); + + $this->assertTrue(is_array($output)); + $this->assertTrue(isset($output'America/Los_Angeles')); + + $converter = kolab_sync_timezone_converter::getInstance(); + $output = $converter->getListOfTimezones('Lv///wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=='); - $offsets = $converter->getOffsetsForTimezone('UTC', $datetime); - $output = $converter->getTimezone($offsets, 'UTC'); + $this->assertTrue(is_array($output)); + $this->assertTrue(isset($output'Asia/Tehran')); - $this->assertSame('UTC', $output); + // As seen in outlook + $converter = kolab_sync_timezone_converter::getInstance(); + $output = $converter->getListOfTimezones('Lv///0kAcgBhAG4AIABTAHQAYQBuAGQAYQByAGQAIABUAGkAbQBlAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAkABAADABcAOwA7AOcDAAAAAEkAcgBhAG4AIABEAGEAeQBsAGkAZwBoAHQAIABUAGkAbQBlAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAMAAwAEAAAAAAAAAAAAxP///w=='); - $offsets = $converter->getOffsetsForTimezone('Europe/Warsaw', $datetime); - $output = $converter->getTimezone($offsets, 'Europe/Warsaw'); + $this->assertTrue(is_array($output)); + $this->assertTrue(isset($output'Asia/Tehran')); + } + + function data_get_timezone() + { + return + 'UTC', + 'Europe/Warsaw', + 'Europe/Zurich', + 'America/Los_Angeles', + 'Asia/Tehran', + ; + } - $this->assertSame('Europe/Warsaw', $output); + /** + * @dataProvider data_get_timezone + */ + function test_get_timezone($tzName) + { + date_default_timezone_set('America/Los_Angeles'); - $offsets = $converter->getOffsetsForTimezone('America/Los_angeles', $datetime); - $output = $converter->getTimezone($offsets, 'America/Los_Angeles'); + $converter = kolab_sync_timezone_converter::getInstance(); + $datetime = '2017-01-01T12:00:00Z'; - $this->assertSame('America/Los_Angeles', $output); + $offsets = $converter->getOffsetsForTimezone($tzName, $datetime); + $output = $converter->getTimezone($offsets, $tzName); + $this->assertSame($tzName, $output); } function test_get_offsets_for_timezone() { - $converter = timezone_converter_test::getInstance(); - $datetime = new DateTime('2017-01-01T12:00:00Z'); + date_default_timezone_set('America/Los_Angeles'); + + $converter = kolab_sync_timezone_converter::getInstance(); + $datetime = '2017-01-01T12:00:00Z'; $output = $converter->getOffsetsForTimezone('UTC', $datetime); @@ -55,11 +83,11 @@ $this->assertSame($output'standardBias', 0); $this->assertSame($output'standardMonth', 10); - $this->assertSame($output'standardDay', 5); + $this->assertSame($output'standardWeek', 5); $this->assertSame($output'standardHour', 3); $this->assertSame($output'daylightBias', -60); $this->assertSame($output'daylightMonth', 3); - $this->assertSame($output'daylightDay', 5); + $this->assertSame($output'daylightWeek', 5); $this->assertSame($output'daylightHour', 2); $output = $converter->getOffsetsForTimezone('America/Los_Angeles', $datetime); @@ -67,11 +95,11 @@ $this->assertSame($output'bias', 480); $this->assertSame($output'standardBias', 0); $this->assertSame($output'standardMonth', 11); - $this->assertSame($output'standardDay', 1); + $this->assertSame($output'standardWeek', 1); $this->assertSame($output'standardHour', 2); $this->assertSame($output'daylightBias', -60); $this->assertSame($output'daylightMonth', 3); - $this->assertSame($output'daylightDay', 2); + $this->assertSame($output'daylightWeek', 2); $this->assertSame($output'daylightHour', 2); $output = $converter->getOffsetsForTimezone('Atlantic/Azores', $datetime); @@ -79,20 +107,69 @@ $this->assertSame($output'bias', 60); $this->assertSame($output'standardBias', 0); $this->assertSame($output'standardMonth', 10); - $this->assertSame($output'standardDay', 5); + $this->assertSame($output'standardWeek', 5); $this->assertSame($output'standardHour', 1); $this->assertSame($output'daylightBias', -60); $this->assertSame($output'daylightMonth', 3); - $this->assertSame($output'daylightDay', 5); + $this->assertSame($output'daylightWeek', 5); + $this->assertSame($output'daylightHour', 0); + + //Check before dst change + $output = $converter->getOffsetsForTimezone('Asia/Tehran', $datetime); + + $this->assertSame($output'bias', -210); + $this->assertSame($output'standardBias', 0); + $this->assertSame($output'standardMonth', 9); + $this->assertSame($output'standardWeek', 3); + $this->assertSame($output'standardDayOfWeek', 4); + $this->assertSame($output'standardHour', 24); + $this->assertSame($output'daylightBias', -60); + $this->assertSame($output'daylightMonth', 3); + $this->assertSame($output'daylightWeek', 4); + $this->assertSame($output'daylightDayOfWeek', 3); + $this->assertSame($output'daylightHour', 0); + + //Check after dst change + $output = $converter->getOffsetsForTimezone('Asia/Tehran', '2023-01-01T12:00:00Z'); + + $this->assertSame($output'bias', -210); + $this->assertSame($output'standardBias', 0); + $this->assertSame($output'standardMonth', 0); + $this->assertSame($output'standardWeek', 0); + $this->assertSame($output'standardDayOfWeek', 0); + $this->assertSame($output'standardHour', 0); + $this->assertSame($output'daylightBias', 0); + $this->assertSame($output'daylightMonth', 0); + $this->assertSame($output'daylightWeek', 0); + $this->assertSame($output'daylightDayOfWeek', 0); $this->assertSame($output'daylightHour', 0); } -} -class timezone_converter_test extends kolab_sync_timezone_converter -{ - // disable cache - function getCache() + function data_timezone_conversion() + { + return array( + //Pre dst change + array('Asia/Tehran', 'Lv///wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAkAAgADABcAOwA7AOcDAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAMAAQAEAAAAAAAAAAAAxP///w==', '2021-07-01T12:00:00Z'), + //Post dst change + array('Asia/Tehran', 'Lv///wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA==', '2023-04-01T12:00:00Z'), + array('Pacific/Pago_Pago', 'lAIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA==', '2021-07-01T12:00:00Z'), + array('Europe/Warsaw', 'xP///wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAoAAAAFAAMAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAMAAAAFAAIAAAAAAAAAxP///w==', '2021-07-01T12:00:00Z'), + ); + } + + /** + * @dataProvider data_timezone_conversion + */ + function test_timezone_conversion($tz, $expected, $datetime) { - return null; + $converter = kolab_sync_timezone_converter::getInstance(); + $output = $converter->encodeTimezone($tz, $datetime); + + $this->assertSame($expected, $output); + + $output = $converter->getListOfTimezones($output); + + $this->assertTrue(is_array($output)); + $this->assertTrue(isset($output$tz)); } }
View file
kolab-syncroton-2.4.2.tar.gz/tests/wbxml.php
Added
@@ -0,0 +1,892 @@ +<?php + +class wbxml extends PHPUnit\Framework\TestCase +{ + //function testDecode() + //{ + // //TODO input some wbxml document + // // + // $dom = new DOMDocument(); + // $dom->loadXML($lastSyncCollection'lastXML'); + // // + // try { + // $decoder = new Syncroton_Wbxml_Decoder($dom); + // $requestBody = $decoder->decode(); + // if ($this->_logger instanceof Zend_Log) { + // $requestBody->formatOutput = true; + // $this->_logger->debug(__METHOD__ . '::' . __LINE__ . " xml request:\n" . $requestBody->saveXML()); + // } + // } catch(Syncroton_Wbxml_Exception_UnexpectedEndOfFile $e) { + // $requestBody = NULL; + // } + // //TODO validate output + //} + + + + public function testEncode() + { + $outputStream = fopen("php://temp", 'r+'); + + $encoder = new Syncroton_Wbxml_Encoder($outputStream, 'UTF-8', 3); + + $xml = <<<EOF + <?xml version="1.0" encoding="utf-8"?> + <!DOCTYPE AirSync PUBLIC "-//AIRSYNC//DTD AirSync//EN" "http://www.microsoft.com/"> + <Sync xmlns="uri:AirSync" xmlns:AirSyncBase="uri:AirSyncBase" xmlns:Tasks="uri:Tasks"> + <Collections> + <Collection> + <SyncKey>2</SyncKey> + <CollectionId>tasksId</CollectionId> + <Commands> + <Add> + <ClientId>clientId2</ClientId> + <ApplicationData> + <Subject xmlns="uri:Tasks">task2</Subject> + <Complete xmlns="uri:Tasks">0</Complete> + <DueDate xmlns="uri:Tasks">2020-11-04T00:00:00.000Z</DueDate> + <UtcDueDate xmlns="uri:Tasks">2020-11-03T23:00:00.000Z</UtcDueDate> + </ApplicationData> + </Add> + <Add> + <ClientId>clientId3</ClientId> + <ApplicationData> + <Subject xmlns="uri:Tasks">task3</Subject> + <Complete xmlns="uri:Tasks">0</Complete> + <DueDate xmlns="uri:Tasks">2020-11-04T00:00:00.000Z</DueDate> + <UtcDueDate xmlns="uri:Tasks">2020-11-03T23:00:00.000Z</UtcDueDate> + </ApplicationData> + </Add> + </Commands> + </Collection> + </Collections> + <WindowSize>16</WindowSize> + </Sync> + EOF; + + + $dom = new DOMDocument(); + $dom->loadXML($xml); + + $encoder->encode($dom); + + rewind($outputStream); + $output = stream_get_contents($outputStream); + // print("----"); + // print(var_export($output, true)); + // print("----"); + $this->assertEquals( + base64_decode('AwFqAEVcT0sDMgABUgN0YXNrc0lkAAFWR0wDY2xpZW50SWQyAAFdAAlgA3Rhc2syAAFKAzAAAUwDMjAyMC0xMS0wNFQwMDowMDowMC4wMDBaAAFNAzIwMjAtMTEtMDNUMjM6MDA6MDAuMDAwWgABAQEAAEdMA2NsaWVudElkMwABXQAJYAN0YXNrMwABSgMwAAFMAzIwMjAtMTEtMDRUMDA6MDA6MDAuMDAwWgABTQMyMDIwLTExLTAzVDIzOjAwOjAwLjAwMFoAAQEBAQEBAABVAzE2AAEB'), + $output + ); + } + + public function testEncodeFolderSync() + { + $outputStream = fopen("php://temp", 'r+'); + + $encoder = new Syncroton_Wbxml_Encoder($outputStream, 'UTF-8', 3); + + $xml = <<<EOF + <?xml version="1.0" encoding="utf-8"?> + <!DOCTYPE AirSync PUBLIC "-//AIRSYNC//DTD AirSync//EN" "http://www.microsoft.com/"> + <FolderSync xmlns="uri:FolderHierarchy" xmlns:Syncroton="uri:Syncroton" xmlns:Internal="uri:Internal"> + <Status>1</Status> + <SyncKey>1</SyncKey> + <Changes> + <Count>18</Count> + <Add> + <ServerId>2685b302b79f58d2753199545e3cb8be</ServerId> + <ParentId>0</ParentId> + <DisplayName>Test2</DisplayName> + <Type>13</Type> + </Add> + <Add> + <ServerId>9770b083c68e8584f396d15a116d6608</ServerId> + <ParentId>0</ParentId> + <DisplayName>DavidCalendar</DisplayName> + <Type>13</Type> + </Add> + <Add> + <ServerId>0f66388806743c514b8063bf0dc87486</ServerId> + <ParentId>0</ParentId> + <DisplayName>SergeyCalendar</DisplayName> + <Type>13</Type> + </Add> + <Add> + <ServerId>cca1b81c734abbcd669bea90d23e08ae</ServerId> + <ParentId>0</ParentId> + <DisplayName>Calendar</DisplayName> + <Type>8</Type> + </Add> + <Add> + <ServerId>ab1ddb4ef8e8f8fcc2c9f5a7f9062452</ServerId> + <ParentId>0</ParentId> + <DisplayName>PubCal</DisplayName> + <Type>13</Type> + </Add> + <Add> + <ServerId>d98bd8721371544ed095841ead941893</ServerId> + <ParentId>0</ParentId> + <DisplayName>(david) Test2</DisplayName> + <Type>13</Type> + </Add> + <Add> + <ServerId>9e7b9656ef61d4af2fb2fdcabe600079</ServerId> + <ParentId>0</ParentId> + <DisplayName>(david) DavidCalendar</DisplayName> + <Type>13</Type> + </Add> + <Add> + <ServerId>384cf2d877c39a622fdc2a16898052e2</ServerId> + <ParentId>0</ParentId> + <DisplayName>(david) Calendar</DisplayName> + <Type>13</Type> + </Add> + <Add> + <ServerId>Contacts::Syncroton</ServerId> + <ParentId>0</ParentId> + <DisplayName>Contacts</DisplayName> + <Type>9</Type> + </Add> + <Add> + <ServerId>1bb8c55fe84d52c6968db2571f7dc124</ServerId> + <ParentId>0</ParentId> + <DisplayName>Archive</DisplayName> + <Type>12</Type> + </Add> + <Add> + <ServerId>b51abe73e9e98fe200a4afe409050502</ServerId> + <ParentId>38b950ebd62cd9a66929c89615d0fc04</ParentId> + <DisplayName>Spam</DisplayName> + <Type>12</Type> + </Add> + <Add> + <ServerId>cf529c792fc87d1f207435b3921bb02e</ServerId> + <ParentId>0</ParentId> + <DisplayName>Sent</DisplayName> + <Type>5</Type> + </Add> + <Add> + <ServerId>715ed9ea29b8a5377a69c1f758037c65</ServerId> + <ParentId>0</ParentId> + <DisplayName>Spam</DisplayName> + <Type>12</Type> + </Add> + <Add> + <ServerId>db0d959a3aeb21757f8849a830947a7a</ServerId> + <ParentId>0</ParentId> + <DisplayName>Trash</DisplayName> + <Type>4</Type> + </Add> + <Add> + <ServerId>5ac9ec2e1a9d99e2e10cabe4abf26729</ServerId> + <ParentId>0</ParentId> + <DisplayName>Drafts</DisplayName> + <Type>3</Type> + </Add> + <Add> + <ServerId>38b950ebd62cd9a66929c89615d0fc04</ServerId> + <ParentId>0</ParentId> + <DisplayName>INBOX</DisplayName> + <Type>2</Type> + </Add> + <Add> + <ServerId>fc56f4c7ffe0aefa622db9f8d9186c4a</ServerId> + <ParentId>0</ParentId> + <DisplayName>Notes</DisplayName> + <Type>10</Type> + </Add> + <Add> + <ServerId>90335880f65deff6e521acea2b71a773</ServerId> + <ParentId>0</ParentId> + <DisplayName>Tasks</DisplayName> + <Type>7</Type> + </Add> + </Changes> + </FolderSync> + EOF; + + + $dom = new DOMDocument(); + $dom->loadXML($xml); + + $encoder->encode($dom); + + rewind($outputStream); + $output = stream_get_contents($outputStream); + // print("----"); + // print(var_export(base64_encode($output), true)); + // print("----"); + $this->assertEquals( + base64_decode('AwFqAAAHVkwDMQABUgMxAAFOVwMxOAABT0gDMjY4NWIzMDJiNzlmNThkMjc1MzE5OTU0NWUzY2I4YmUAAUkDMAABRwNUZXN0MgABSgMxMwABAU9IAzk3NzBiMDgzYzY4ZTg1ODRmMzk2ZDE1YTExNmQ2NjA4AAFJAzAAAUcDRGF2aWRDYWxlbmRhcgABSgMxMwABAU9IAzBmNjYzODg4MDY3NDNjNTE0YjgwNjNiZjBkYzg3NDg2AAFJAzAAAUcDU2VyZ2V5Q2FsZW5kYXIAAUoDMTMAAQFPSANjY2ExYjgxYzczNGFiYmNkNjY5YmVhOTBkMjNlMDhhZQABSQMwAAFHA0NhbGVuZGFyAAFKAzgAAQFPSANhYjFkZGI0ZWY4ZThmOGZjYzJjOWY1YTdmOTA2MjQ1MgABSQMwAAFHA1B1YkNhbAABSgMxMwABAU9IA2Q5OGJkODcyMTM3MTU0NGVkMDk1ODQxZWFkOTQxODkzAAFJAzAAAUcDKGRhdmlkKSBUZXN0MgABSgMxMwABAU9IAzllN2I5NjU2ZWY2MWQ0YWYyZmIyZmRjYWJlNjAwMDc5AAFJAzAAAUcDKGRhdmlkKSBEYXZpZENhbGVuZGFyAAFKAzEzAAEBT0gDMzg0Y2YyZDg3N2MzOWE2MjJmZGMyYTE2ODk4MDUyZTIAAUkDMAABRwMoZGF2aWQpIENhbGVuZGFyAAFKAzEzAAEBT0gDQ29udGFjdHM6OlN5bmNyb3RvbgABSQMwAAFHA0NvbnRhY3RzAAFKAzkAAQFPSAMxYmI4YzU1ZmU4NGQ1MmM2OTY4ZGIyNTcxZjdkYzEyNAABSQMwAAFHA0FyY2hpdmUAAUoDMTIAAQFPSANiNTFhYmU3M2U5ZTk4ZmUyMDBhNGFmZTQwOTA1MDUwMgABSQMzOGI5NTBlYmQ2MmNkOWE2NjkyOWM4OTYxNWQwZmMwNAABRwNTcGFtAAFKAzEyAAEBT0gDY2Y1MjljNzkyZmM4N2QxZjIwNzQzNWIzOTIxYmIwMmUAAUkDMAABRwNTZW50AAFKAzUAAQFPSAM3MTVlZDllYTI5YjhhNTM3N2E2OWMxZjc1ODAzN2M2NQABSQMwAAFHA1NwYW0AAUoDMTIAAQFPSANkYjBkOTU5YTNhZWIyMTc1N2Y4ODQ5YTgzMDk0N2E3YQABSQMwAAFHA1RyYXNoAAFKAzQAAQFPSAM1YWM5ZWMyZTFhOWQ5OWUyZTEwY2FiZTRhYmYyNjcyOQABSQMwAAFHA0RyYWZ0cwABSgMzAAEBT0gDMzhiOTUwZWJkNjJjZDlhNjY5MjljODk2MTVkMGZjMDQAAUkDMAABRwNJTkJPWAABSgMyAAEBT0gDZmM1NmY0YzdmZmUwYWVmYTYyMmRiOWY4ZDkxODZjNGEAAUkDMAABRwNOb3RlcwABSgMxMAABAU9IAzkwMzM1ODgwZjY1ZGVmZjZlNTIxYWNlYTJiNzFhNzczAAFJAzAAAUcDVGFza3MAAUoDNwABAQEB'), + $output + ); + } + + public function testEncodeCalendar() + { + $outputStream = fopen("php://temp", 'r+'); + + $encoder = new Syncroton_Wbxml_Encoder($outputStream, 'UTF-8', 3); + + $xml = <<<EOF + <?xml version="1.0" encoding="utf-8"?> + <!DOCTYPE AirSync PUBLIC "-//AIRSYNC//DTD AirSync//EN" "http://www.microsoft.com/"> + <Sync xmlns="uri:AirSync" xmlns:AirSyncBase="uri:AirSyncBase" xmlns:Calendar="uri:Calendar"> + <Collections> + <Collection> + <SyncKey>0</SyncKey> + <CollectionId>38b950ebd62cd9a66929c89615d0fc04</CollectionId> + <DeletesAsMoves>0</DeletesAsMoves> + <GetChanges>0</GetChanges> + <WindowSize>512</WindowSize> + <Options> + <FilterType>0</FilterType> + <MIMESupport>2</MIMESupport> + <MIMETruncation>8</MIMETruncation> + <BodyPreference xmlns="uri:AirSyncBase"> + <Type>4</Type> + <AllOrNone>1</AllOrNone> + </BodyPreference> + </Options> + </Collection> + <Collection> + <SyncKey>0</SyncKey> + <CollectionId>cca1b81c734abbcd669bea90d23e08ae</CollectionId> + <Supported> + <DtStamp xmlns="uri:Calendar"/> + <Categories xmlns="uri:Calendar"/> + <Sensitivity xmlns="uri:Calendar"/> + <BusyStatus xmlns="uri:Calendar"/> + <UID xmlns="uri:Calendar"/> + <Timezone xmlns="uri:Calendar"/> + <StartTime xmlns="uri:Calendar"/> + <Subject xmlns="uri:Calendar"/> + <Location xmlns="uri:Calendar"/> + <EndTime xmlns="uri:Calendar"/> + <Recurrence xmlns="uri:Calendar"/> + <AllDayEvent xmlns="uri:Calendar"/> + <Reminder xmlns="uri:Calendar"/> + <Exceptions xmlns="uri:Calendar"/> + <Attendees xmlns="uri:Calendar"/> + <MeetingStatus xmlns="uri:Calendar"/> + <ResponseRequested xmlns="uri:Calendar"/> + <DisallowNewTimeProposal xmlns="uri:Calendar"/> + </Supported> + <DeletesAsMoves>0</DeletesAsMoves> + <GetChanges>0</GetChanges> + <WindowSize>512</WindowSize> + <Options> + <FilterType>0</FilterType> + <BodyPreference xmlns="uri:AirSyncBase"> + <Type>1</Type> + <AllOrNone>1</AllOrNone> + </BodyPreference> + </Options> + </Collection> + <Collection> + <SyncKey>0</SyncKey> + <CollectionId>Contacts::Syncroton</CollectionId> + <DeletesAsMoves>0</DeletesAsMoves> + <GetChanges>0</GetChanges> + <WindowSize>512</WindowSize> + <Options> + <FilterType>0</FilterType> + <BodyPreference xmlns="uri:AirSyncBase"> + <Type>1</Type> + <AllOrNone>1</AllOrNone> + </BodyPreference> + </Options> + </Collection> + <Collection> + <SyncKey>0</SyncKey> + <CollectionId>db0d959a3aeb21757f8849a830947a7a</CollectionId> + <DeletesAsMoves>0</DeletesAsMoves> + <GetChanges>0</GetChanges> + <WindowSize>512</WindowSize> + <Options> + <FilterType>0</FilterType> + <MIMESupport>2</MIMESupport> + <MIMETruncation>8</MIMETruncation> + <BodyPreference xmlns="uri:AirSyncBase"> + <Type>4</Type> + <AllOrNone>1</AllOrNone> + </BodyPreference> + </Options> + </Collection> + <Collection> + <SyncKey>0</SyncKey> + <CollectionId>cf529c792fc87d1f207435b3921bb02e</CollectionId> + <DeletesAsMoves>0</DeletesAsMoves> + <GetChanges>0</GetChanges> + <WindowSize>512</WindowSize> + <Options> + <FilterType>0</FilterType> + <MIMESupport>2</MIMESupport> + <MIMETruncation>8</MIMETruncation> + <BodyPreference xmlns="uri:AirSyncBase"> + <Type>4</Type> + <AllOrNone>1</AllOrNone> + </BodyPreference> + </Options> + </Collection> + <Collection> + <SyncKey>0</SyncKey> + <CollectionId>90335880f65deff6e521acea2b71a773</CollectionId> + <DeletesAsMoves>0</DeletesAsMoves> + <GetChanges>0</GetChanges> + <WindowSize>512</WindowSize> + <Options> + <FilterType>0</FilterType> + <BodyPreference xmlns="uri:AirSyncBase"> + <Type>1</Type> + <AllOrNone>1</AllOrNone> + </BodyPreference> + </Options> + </Collection> + <Collection> + <SyncKey>0</SyncKey> + <CollectionId>1bb8c55fe84d52c6968db2571f7dc124</CollectionId> + <DeletesAsMoves>0</DeletesAsMoves> + <GetChanges>0</GetChanges> + <WindowSize>512</WindowSize> + <Options> + <FilterType>0</FilterType> + <MIMESupport>2</MIMESupport> + <MIMETruncation>8</MIMETruncation> + <BodyPreference xmlns="uri:AirSyncBase"> + <Type>4</Type> + <AllOrNone>1</AllOrNone> + </BodyPreference> + </Options> + </Collection> + <Collection> + <SyncKey>0</SyncKey> + <CollectionId>715ed9ea29b8a5377a69c1f758037c65</CollectionId> + <DeletesAsMoves>0</DeletesAsMoves> + <GetChanges>0</GetChanges> + <WindowSize>512</WindowSize> + <Options> + <FilterType>0</FilterType> + <MIMESupport>2</MIMESupport> + <MIMETruncation>8</MIMETruncation> + <BodyPreference xmlns="uri:AirSyncBase"> + <Type>4</Type> + <AllOrNone>1</AllOrNone> + </BodyPreference> + </Options> + </Collection> + <Collection> + <SyncKey>0</SyncKey> + <CollectionId>b51abe73e9e98fe200a4afe409050502</CollectionId> + <DeletesAsMoves>0</DeletesAsMoves> + <GetChanges>0</GetChanges> + <WindowSize>512</WindowSize> + <Options> + <FilterType>0</FilterType> + <MIMESupport>2</MIMESupport> + <MIMETruncation>8</MIMETruncation> + <BodyPreference xmlns="uri:AirSyncBase"> + <Type>4</Type> + <AllOrNone>1</AllOrNone> + </BodyPreference> + </Options> + </Collection> + <Collection> + <SyncKey>0</SyncKey> + <CollectionId>0f66388806743c514b8063bf0dc87486</CollectionId> + <Supported> + <DtStamp xmlns="uri:Calendar"/> + <Categories xmlns="uri:Calendar"/> + <Sensitivity xmlns="uri:Calendar"/> + <BusyStatus xmlns="uri:Calendar"/> + <UID xmlns="uri:Calendar"/> + <Timezone xmlns="uri:Calendar"/> + <StartTime xmlns="uri:Calendar"/> + <Subject xmlns="uri:Calendar"/> + <Location xmlns="uri:Calendar"/> + <EndTime xmlns="uri:Calendar"/> + <Recurrence xmlns="uri:Calendar"/> + <AllDayEvent xmlns="uri:Calendar"/> + <Reminder xmlns="uri:Calendar"/> + <Exceptions xmlns="uri:Calendar"/> + <Attendees xmlns="uri:Calendar"/> + <MeetingStatus xmlns="uri:Calendar"/> + <ResponseRequested xmlns="uri:Calendar"/> + <DisallowNewTimeProposal xmlns="uri:Calendar"/> + </Supported> + <DeletesAsMoves>0</DeletesAsMoves> + <GetChanges>0</GetChanges> + <WindowSize>512</WindowSize> + <Options> + <FilterType>0</FilterType> + <BodyPreference xmlns="uri:AirSyncBase"> + <Type>1</Type> + <AllOrNone>1</AllOrNone> + </BodyPreference> + </Options> + </Collection> + <Collection> + <SyncKey>0</SyncKey> + <CollectionId>2685b302b79f58d2753199545e3cb8be</CollectionId> + <Supported> + <DtStamp xmlns="uri:Calendar"/> + <Categories xmlns="uri:Calendar"/> + <Sensitivity xmlns="uri:Calendar"/> + <BusyStatus xmlns="uri:Calendar"/> + <UID xmlns="uri:Calendar"/> + <Timezone xmlns="uri:Calendar"/> + <StartTime xmlns="uri:Calendar"/> + <Subject xmlns="uri:Calendar"/> + <Location xmlns="uri:Calendar"/> + <EndTime xmlns="uri:Calendar"/> + <Recurrence xmlns="uri:Calendar"/> + <AllDayEvent xmlns="uri:Calendar"/> + <Reminder xmlns="uri:Calendar"/> + <Exceptions xmlns="uri:Calendar"/> + <Attendees xmlns="uri:Calendar"/> + <MeetingStatus xmlns="uri:Calendar"/> + <ResponseRequested xmlns="uri:Calendar"/> + <DisallowNewTimeProposal xmlns="uri:Calendar"/> + </Supported> + <DeletesAsMoves>0</DeletesAsMoves> + <GetChanges>0</GetChanges> + <WindowSize>512</WindowSize> + <Options> + <FilterType>0</FilterType> + <BodyPreference xmlns="uri:AirSyncBase"> + <Type>1</Type> + <AllOrNone>1</AllOrNone> + </BodyPreference> + </Options> + </Collection> + <Collection> + <SyncKey>0</SyncKey> + <CollectionId>384cf2d877c39a622fdc2a16898052e2</CollectionId> + <Supported> + <DtStamp xmlns="uri:Calendar"/> + <Categories xmlns="uri:Calendar"/> + <Sensitivity xmlns="uri:Calendar"/> + <BusyStatus xmlns="uri:Calendar"/> + <UID xmlns="uri:Calendar"/> + <Timezone xmlns="uri:Calendar"/> + <StartTime xmlns="uri:Calendar"/> + <Subject xmlns="uri:Calendar"/> + <Location xmlns="uri:Calendar"/> + <EndTime xmlns="uri:Calendar"/> + <Recurrence xmlns="uri:Calendar"/> + <AllDayEvent xmlns="uri:Calendar"/> + <Reminder xmlns="uri:Calendar"/> + <Exceptions xmlns="uri:Calendar"/> + <Attendees xmlns="uri:Calendar"/> + <MeetingStatus xmlns="uri:Calendar"/> + <ResponseRequested xmlns="uri:Calendar"/> + <DisallowNewTimeProposal xmlns="uri:Calendar"/> + </Supported> + <DeletesAsMoves>0</DeletesAsMoves> + <GetChanges>0</GetChanges> + <WindowSize>512</WindowSize> + <Options> + <FilterType>0</FilterType> + <BodyPreference xmlns="uri:AirSyncBase"> + <Type>1</Type> + <AllOrNone>1</AllOrNone> + </BodyPreference> + </Options> + </Collection> + <Collection> + <SyncKey>0</SyncKey> + <CollectionId>9770b083c68e8584f396d15a116d6608</CollectionId> + <Supported> + <DtStamp xmlns="uri:Calendar"/> + <Categories xmlns="uri:Calendar"/> + <Sensitivity xmlns="uri:Calendar"/> + <BusyStatus xmlns="uri:Calendar"/> + <UID xmlns="uri:Calendar"/> + <Timezone xmlns="uri:Calendar"/> + <StartTime xmlns="uri:Calendar"/> + <Subject xmlns="uri:Calendar"/> + <Location xmlns="uri:Calendar"/> + <EndTime xmlns="uri:Calendar"/> + <Recurrence xmlns="uri:Calendar"/> + <AllDayEvent xmlns="uri:Calendar"/> + <Reminder xmlns="uri:Calendar"/> + <Exceptions xmlns="uri:Calendar"/> + <Attendees xmlns="uri:Calendar"/> + <MeetingStatus xmlns="uri:Calendar"/> + <ResponseRequested xmlns="uri:Calendar"/> + <DisallowNewTimeProposal xmlns="uri:Calendar"/> + </Supported> + <DeletesAsMoves>0</DeletesAsMoves> + <GetChanges>0</GetChanges> + <WindowSize>512</WindowSize> + <Options> + <FilterType>0</FilterType> + <BodyPreference xmlns="uri:AirSyncBase"> + <Type>1</Type> + <AllOrNone>1</AllOrNone> + </BodyPreference> + </Options> + </Collection> + <Collection> + <SyncKey>0</SyncKey> + <CollectionId>9e7b9656ef61d4af2fb2fdcabe600079</CollectionId> + <Supported> + <DtStamp xmlns="uri:Calendar"/> + <Categories xmlns="uri:Calendar"/> + <Sensitivity xmlns="uri:Calendar"/> + <BusyStatus xmlns="uri:Calendar"/> + <UID xmlns="uri:Calendar"/> + <Timezone xmlns="uri:Calendar"/> + <StartTime xmlns="uri:Calendar"/> + <Subject xmlns="uri:Calendar"/> + <Location xmlns="uri:Calendar"/> + <EndTime xmlns="uri:Calendar"/> + <Recurrence xmlns="uri:Calendar"/> + <AllDayEvent xmlns="uri:Calendar"/> + <Reminder xmlns="uri:Calendar"/> + <Exceptions xmlns="uri:Calendar"/> + <Attendees xmlns="uri:Calendar"/> + <MeetingStatus xmlns="uri:Calendar"/> + <ResponseRequested xmlns="uri:Calendar"/> + <DisallowNewTimeProposal xmlns="uri:Calendar"/> + </Supported> + <DeletesAsMoves>0</DeletesAsMoves> + <GetChanges>0</GetChanges> + <WindowSize>512</WindowSize> + <Options> + <FilterType>0</FilterType> + <BodyPreference xmlns="uri:AirSyncBase"> + <Type>1</Type> + <AllOrNone>1</AllOrNone> + </BodyPreference> + </Options> + </Collection> + <Collection> + <SyncKey>0</SyncKey> + <CollectionId>ab1ddb4ef8e8f8fcc2c9f5a7f9062452</CollectionId> + <Supported> + <DtStamp xmlns="uri:Calendar"/> + <Categories xmlns="uri:Calendar"/> + <Sensitivity xmlns="uri:Calendar"/> + <BusyStatus xmlns="uri:Calendar"/> + <UID xmlns="uri:Calendar"/> + <Timezone xmlns="uri:Calendar"/> + <StartTime xmlns="uri:Calendar"/> + <Subject xmlns="uri:Calendar"/> + <Location xmlns="uri:Calendar"/> + <EndTime xmlns="uri:Calendar"/> + <Recurrence xmlns="uri:Calendar"/> + <AllDayEvent xmlns="uri:Calendar"/> + <Reminder xmlns="uri:Calendar"/> + <Exceptions xmlns="uri:Calendar"/> + <Attendees xmlns="uri:Calendar"/> + <MeetingStatus xmlns="uri:Calendar"/> + <ResponseRequested xmlns="uri:Calendar"/> + <DisallowNewTimeProposal xmlns="uri:Calendar"/> + </Supported> + <DeletesAsMoves>0</DeletesAsMoves> + <GetChanges>0</GetChanges> + <WindowSize>512</WindowSize> + <Options> + <FilterType>0</FilterType> + <BodyPreference xmlns="uri:AirSyncBase"> + <Type>1</Type> + <AllOrNone>1</AllOrNone> + </BodyPreference> + </Options> + </Collection> + <Collection> + <SyncKey>0</SyncKey> + <CollectionId>d98bd8721371544ed095841ead941893</CollectionId> + <Supported> + <DtStamp xmlns="uri:Calendar"/> + <Categories xmlns="uri:Calendar"/> + <Sensitivity xmlns="uri:Calendar"/> + <BusyStatus xmlns="uri:Calendar"/> + <UID xmlns="uri:Calendar"/> + <Timezone xmlns="uri:Calendar"/> + <StartTime xmlns="uri:Calendar"/> + <Subject xmlns="uri:Calendar"/> + <Location xmlns="uri:Calendar"/> + <EndTime xmlns="uri:Calendar"/> + <Recurrence xmlns="uri:Calendar"/> + <AllDayEvent xmlns="uri:Calendar"/> + <Reminder xmlns="uri:Calendar"/> + <Exceptions xmlns="uri:Calendar"/> + <Attendees xmlns="uri:Calendar"/> + <MeetingStatus xmlns="uri:Calendar"/> + <ResponseRequested xmlns="uri:Calendar"/> + <DisallowNewTimeProposal xmlns="uri:Calendar"/> + </Supported> + <DeletesAsMoves>0</DeletesAsMoves> + <GetChanges>0</GetChanges> + <WindowSize>512</WindowSize> + <Options> + <FilterType>0</FilterType> + <BodyPreference xmlns="uri:AirSyncBase"> + <Type>1</Type> + <AllOrNone>1</AllOrNone> + </BodyPreference> + </Options> + </Collection> + </Collections> + <WindowSize>16</WindowSize> + </Sync> + EOF; + + + $dom = new DOMDocument(); + $dom->loadXML($xml); + + $encoder->encode($dom); + + rewind($outputStream); + $output = stream_get_contents($outputStream); + // print("----"); + // print(var_export(base64_encode($output), true)); + // print("----"); + + $this->assertEquals( + base64_decode('AwFqAEVcT0sDMAABUgMzOGI5NTBlYmQ2MmNkOWE2NjkyOWM4OTYxNWQwZmMwNAABXgMwAAFTAzAAAVUDNTEyAAFXWAMwAAFiAzIAAWMDOAABABFFRgM0AAFIAzEAAQEBAQAAT0sDMAABUgNjY2ExYjgxYzczNGFiYmNkNjY5YmVhOTBkMjNlMDhhZQABYAAEEQ4lDSgFJyYXEhsGJBQHGDQzAQAAXgMwAAFTAzAAAVUDNTEyAAFXWAMwAAEAEUVGAzEAAUgDMQABAQEBAABPSwMwAAFSA0NvbnRhY3RzOjpTeW5jcm90b24AAV4DMAABUwMwAAFVAzUxMgABV1gDMAABABFFRgMxAAFIAzEAAQEBAQAAT0sDMAABUgNkYjBkOTU5YTNhZWIyMTc1N2Y4ODQ5YTgzMDk0N2E3YQABXgMwAAFTAzAAAVUDNTEyAAFXWAMwAAFiAzIAAWMDOAABABFFRgM0AAFIAzEAAQEBAQAAT0sDMAABUgNjZjUyOWM3OTJmYzg3ZDFmMjA3NDM1YjM5MjFiYjAyZQABXgMwAAFTAzAAAVUDNTEyAAFXWAMwAAFiAzIAAWMDOAABABFFRgM0AAFIAzEAAQEBAQAAT0sDMAABUgM5MDMzNTg4MGY2NWRlZmY2ZTUyMWFjZWEyYjcxYTc3MwABXgMwAAFTAzAAAVUDNTEyAAFXWAMwAAEAEUVGAzEAAUgDMQABAQEBAABPSwMwAAFSAzFiYjhjNTVmZTg0ZDUyYzY5NjhkYjI1NzFmN2RjMTI0AAFeAzAAAVMDMAABVQM1MTIAAVdYAzAAAWIDMgABYwM4AAEAEUVGAzQAAUgDMQABAQEBAABPSwMwAAFSAzcxNWVkOWVhMjliOGE1Mzc3YTY5YzFmNzU4MDM3YzY1AAFeAzAAAVMDMAABVQM1MTIAAVdYAzAAAWIDMgABYwM4AAEAEUVGAzQAAUgDMQABAQEBAABPSwMwAAFSA2I1MWFiZTczZTllOThmZTIwMGE0YWZlNDA5MDUwNTAyAAFeAzAAAVMDMAABVQM1MTIAAVdYAzAAAWIDMgABYwM4AAEAEUVGAzQAAUgDMQABAQEBAABPSwMwAAFSAzBmNjYzODg4MDY3NDNjNTE0YjgwNjNiZjBkYzg3NDg2AAFgAAQRDiUNKAUnJhcSGwYkFAcYNDMBAABeAzAAAVMDMAABVQM1MTIAAVdYAzAAAQARRUYDMQABSAMxAAEBAQEAAE9LAzAAAVIDMjY4NWIzMDJiNzlmNThkMjc1MzE5OTU0NWUzY2I4YmUAAWAABBEOJQ0oBScmFxIbBiQUBxg0MwEAAF4DMAABUwMwAAFVAzUxMgABV1gDMAABABFFRgMxAAFIAzEAAQEBAQAAT0sDMAABUgMzODRjZjJkODc3YzM5YTYyMmZkYzJhMTY4OTgwNTJlMgABYAAEEQ4lDSgFJyYXEhsGJBQHGDQzAQAAXgMwAAFTAzAAAVUDNTEyAAFXWAMwAAEAEUVGAzEAAUgDMQABAQEBAABPSwMwAAFSAzk3NzBiMDgzYzY4ZTg1ODRmMzk2ZDE1YTExNmQ2NjA4AAFgAAQRDiUNKAUnJhcSGwYkFAcYNDMBAABeAzAAAVMDMAABVQM1MTIAAVdYAzAAAQARRUYDMQABSAMxAAEBAQEAAE9LAzAAAVIDOWU3Yjk2NTZlZjYxZDRhZjJmYjJmZGNhYmU2MDAwNzkAAWAABBEOJQ0oBScmFxIbBiQUBxg0MwEAAF4DMAABUwMwAAFVAzUxMgABV1gDMAABABFFRgMxAAFIAzEAAQEBAQAAT0sDMAABUgNhYjFkZGI0ZWY4ZThmOGZjYzJjOWY1YTdmOTA2MjQ1MgABYAAEEQ4lDSgFJyYXEhsGJBQHGDQzAQAAXgMwAAFTAzAAAVUDNTEyAAFXWAMwAAEAEUVGAzEAAUgDMQABAQEBAABPSwMwAAFSA2Q5OGJkODcyMTM3MTU0NGVkMDk1ODQxZWFkOTQxODkzAAFgAAQRDiUNKAUnJhcSGwYkFAcYNDMBAABeAzAAAVMDMAABVQM1MTIAAVdYAzAAAQARRUYDMQABSAMxAAEBAQEBAABVAzE2AAEB'), + $output + ); + } + + public function testEncodeEmail() + { + $outputStream = fopen("php://temp", 'r+'); + + $encoder = new Syncroton_Wbxml_Encoder($outputStream, 'UTF-8', 3); + + $xml = <<<EOF + <?xml version="1.0" encoding="utf-8"?> + <!DOCTYPE AirSync PUBLIC "-//AIRSYNC//DTD AirSync//EN" "http://www.microsoft.com/"> + <Sync xmlns="uri:AirSync" xmlns:Syncroton="uri:Syncroton" xmlns:AirSyncBase="uri:AirSyncBase" xmlns:Email="uri:Email" xmlns:Email2="uri:Email2" xmlns:Tasks="uri:Tasks"> + <Collections> + <Collection xmlns:default="uri:Email" xmlns:default1="uri:AirSyncBase"> + <Class>Email</Class> + <SyncKey>2</SyncKey> + <CollectionId>38b950ebd62cd9a66929c89615d0fc04</CollectionId> + <Status>1</Status> + <MoreAvailable/> + <Commands xmlns:default="uri:Email" xmlns:default1="uri:AirSyncBase"> + <Add xmlns:default="uri:Email" xmlns:default1="uri:AirSyncBase"> + <ServerId>38b950ebd62cd9a66929c89615d0fc04::1</ServerId> + <ApplicationData> + <Email:DateReceived xmlns="uri:Email">2023-05-06T14:51:40.000Z</Email:DateReceived> + <Email:From xmlns="uri:Email">"Mollekopf, Christian" <christian@example.ch></Email:From> + <Email:InternetCPID xmlns="uri:Email">65001</Email:InternetCPID> + <Email:Subject xmlns="uri:Email">Foobar 1</Email:Subject> + <Email:To xmlns="uri:Email">christian@example.ch</Email:To> + <Email:Read xmlns="uri:Email">0</Email:Read> + <Email:Flag xmlns="uri:Email"/> + <AirSyncBase:Body xmlns="uri:AirSyncBase"> + <AirSyncBase:Type>4</AirSyncBase:Type> + <AirSyncBase:Data>Return-Path: <christian@example.ch> + Received: from imapb010.mykolab.com (unix socket) + by imapb010.mykolab.com (Cyrus 2.5.10-49-g2e214b4-Kolab-2.5.10-8.1.el7.kolab_14) with LMTPA; + Wed, 09 Aug 2017 18:37:01 +0200 + X-Sieve: CMU Sieve 2.4 + Received: from int-mx002.mykolab.com (unknown 10.9.13.2) + by imapb010.mykolab.com (Postfix) with ESMTPS id 0A93910A25047 + for <christian@example.ch>; Wed, 9 Aug 2017 18:37:01 +0200 (CEST) + Received: from int-subm002.mykolab.com (unknown 10.9.37.2) + by int-mx002.mykolab.com (Postfix) with ESMTPS id EC06AF6E + for <christian@example.ch>; Wed, 9 Aug 2017 18:37:00 +0200 (CEST) + MIME-Version: 1.0 + Content-Type: multipart/mixed; + boundary="=_291b8e96564265636432c6d494e02322" + Date: Sat, 06 May 2023 14:41:40 + From: "Mollekopf, Christian" <christian@example.ch> + To: christian@example.ch + Subject: Foobar 1 + Message-ID: <foobar1@example.org> + + --=_291b8e96564265636432c6d494e02322 + Content-Type: multipart/alternative; + boundary="=_ceff0fd19756f45ed1295ee2069ff8e0" + + --=_ceff0fd19756f45ed1295ee2069ff8e0 + Content-Transfer-Encoding: 7bit + Content-Type: text/plain; charset=US-ASCII + + sdlkjsdjf + --=_ceff0fd19756f45ed1295ee2069ff8e0 + Content-Transfer-Encoding: quoted-printable + Content-Type: text/html; charset=UTF-8 + + <html><head><meta http-equiv=3D"Content-Type" content=3D"text/html; charset= + =3DUTF-8" /></head><body style=3D'font-size: 10pt; font-family: Verdana,Gen= + eva,sans-serif'> + <p>sdlkjsdjf</p> + + </body></html> + + --=_ceff0fd19756f45ed1295ee2069ff8e0-- + + --=_291b8e96564265636432c6d494e02322 + Content-Transfer-Encoding: base64 + Content-Type: text/plain; + name=xorg.conf + Content-Disposition: attachment; + filename=xorg.conf; + size=211 + + U2VjdGlvbiAiRGV2aWNlIgogICAgSWRlbnRpZmllciAgICAgIkRldmljZTAiCiAgICBEcml2ZXIg + ICAgIEJvYXJkTmFtZSAgICAgICJOVlMgNDIwME0iCiAgICBPcHRpb24gIk5vTG9nbyIgInRydWUi + CiAgICBPcHRpb24gIlVzZUVESUQiICJ0cnVlIgpFbmRTZWN0aW9uCg== + --=_291b8e96564265636432c6d494e02322--</AirSyncBase:Data> + </AirSyncBase:Body> + <AirSyncBase:NativeBodyType xmlns="uri:AirSyncBase">2</AirSyncBase:NativeBodyType> + <Email:MessageClass xmlns="uri:Email">IPM.Note</Email:MessageClass> + <Email:ContentClass xmlns="uri:Email">urn:content-classes:message</Email:ContentClass> + <AirSyncBase:Attachments xmlns="uri:AirSyncBase"> + <AirSyncBase:Attachment> + <AirSyncBase:DisplayName>xorg.conf</AirSyncBase:DisplayName> + <AirSyncBase:FileReference>38b950ebd62cd9a66929c89615d0fc04::5::2</AirSyncBase:FileReference> + <AirSyncBase:Method>1</AirSyncBase:Method> + <AirSyncBase:EstimatedDataSize>35100212</AirSyncBase:EstimatedDataSize> + </AirSyncBase:Attachment> + </AirSyncBase:Attachments> + </ApplicationData> + </Add> + </Commands> + </Collection> + </Collections> + </Sync> + EOF; + + $dom = new DOMDocument(); + $dom->loadXML($xml); + + $encoder->encode($dom); + + rewind($outputStream); + $output = stream_get_contents($outputStream); + // print("----"); + // print(var_export(base64_encode($output), true)); + // print("----"); + + $this->assertEquals( + base64_decode('AwFqAEVcT1ADRW1haWwAAUsDMgABUgMzOGI5NTBlYmQ2MmNkOWE2NjkyOWM4OTYxNWQwZmMwNAABTgMxAAEUVkdNAzM4Yjk1MGViZDYyY2Q5YTY2OTI5Yzg5NjE1ZDBmYzA0OjoxAAFdAAJPAzIwMjMtMDUtMDZUMTQ6NTE6NDAuMDAwWgABWAMiTW9sbGVrb3BmLCBDaHJpc3RpYW4iIDxjaHJpc3RpYW5AZXhhbXBsZS5jaD4AAXkDNjUwMDEAAVQDRm9vYmFyIDEAAVYDY2hyaXN0aWFuQGV4YW1wbGUuY2gAAVUDMAABOgARSkYDNAABSwNSZXR1cm4tUGF0aDogPGNocmlzdGlhbkBleGFtcGxlLmNoPg0KUmVjZWl2ZWQ6IGZyb20gaW1hcGIwMTAubXlrb2xhYi5jb20gKFt1bml4IHNvY2tldF0pDQogICAgICAgIGJ5IGltYXBiMDEwLm15a29sYWIuY29tIChDeXJ1cyAyLjUuMTAtNDktZzJlMjE0YjQtS29sYWItMi41LjEwLTguMS5lbDcua29sYWJfMTQpIHdpdGggTE1UUEE7DQogICAgICAgIFdlZCwgMDkgQXVnIDIwMTcgMTg6Mzc6MDEgKzAyMDANClgtU2lldmU6IENNVSBTaWV2ZSAyLjQNClJlY2VpdmVkOiBmcm9tIGludC1teDAwMi5teWtvbGFiLmNvbSAodW5rbm93biBbMTAuOS4xMy4yXSkNCiAgICAgICAgYnkgaW1hcGIwMTAubXlrb2xhYi5jb20gKFBvc3RmaXgpIHdpdGggRVNNVFBTIGlkIDBBOTM5MTBBMjUwNDcNCiAgICAgICAgZm9yIDxjaHJpc3RpYW5AZXhhbXBsZS5jaD47IFdlZCwgIDkgQXVnIDIwMTcgMTg6Mzc6MDEgKzAyMDAgKENFU1QpDQpSZWNlaXZlZDogZnJvbSBpbnQtc3VibTAwMi5teWtvbGFiLmNvbSAodW5rbm93biBbMTAuOS4zNy4yXSkNCiAgICAgICAgYnkgaW50LW14MDAyLm15a29sYWIuY29tIChQb3N0Zml4KSB3aXRoIEVTTVRQUyBpZCBFQzA2QUY2RQ0KICAgICAgICBmb3IgPGNocmlzdGlhbkBleGFtcGxlLmNoPjsgV2VkLCAgOSBBdWcgMjAxNyAxODozNzowMCArMDIwMCAoQ0VTVCkNCk1JTUUtVmVyc2lvbjogMS4wDQpDb250ZW50LVR5cGU6IG11bHRpcGFydC9taXhlZDsNCmJvdW5kYXJ5PSI9XzI5MWI4ZTk2NTY0MjY1NjM2NDMyYzZkNDk0ZTAyMzIyIg0KRGF0ZTogU2F0LCAwNiBNYXkgMjAyMyAxNDo0MTo0MCANCkZyb206ICJNb2xsZWtvcGYsIENocmlzdGlhbiIgPGNocmlzdGlhbkBleGFtcGxlLmNoPg0KVG86IGNocmlzdGlhbkBleGFtcGxlLmNoDQpTdWJqZWN0OiBGb29iYXIgMQ0KTWVzc2FnZS1JRDogPGZvb2JhcjFAZXhhbXBsZS5vcmc+DQoNCi0tPV8yOTFiOGU5NjU2NDI2NTYzNjQzMmM2ZDQ5NGUwMjMyMg0KQ29udGVudC1UeXBlOiBtdWx0aXBhcnQvYWx0ZXJuYXRpdmU7DQpib3VuZGFyeT0iPV9jZWZmMGZkMTk3NTZmNDVlZDEyOTVlZTIwNjlmZjhlMCINCg0KLS09X2NlZmYwZmQxOTc1NmY0NWVkMTI5NWVlMjA2OWZmOGUwDQpDb250ZW50LVRyYW5zZmVyLUVuY29kaW5nOiA3Yml0DQpDb250ZW50LVR5cGU6IHRleHQvcGxhaW47IGNoYXJzZXQ9VVMtQVNDSUkNCg0Kc2Rsa2pzZGpmDQotLT1fY2VmZjBmZDE5NzU2ZjQ1ZWQxMjk1ZWUyMDY5ZmY4ZTANCkNvbnRlbnQtVHJhbnNmZXItRW5jb2Rpbmc6IHF1b3RlZC1wcmludGFibGUNCkNvbnRlbnQtVHlwZTogdGV4dC9odG1sOyBjaGFyc2V0PVVURi04DQoNCjxodG1sPjxoZWFkPjxtZXRhIGh0dHAtZXF1aXY9M0QiQ29udGVudC1UeXBlIiBjb250ZW50PTNEInRleHQvaHRtbDsgY2hhcnNldD0NCj0zRFVURi04IiAvPjwvaGVhZD48Ym9keSBzdHlsZT0zRCdmb250LXNpemU6IDEwcHQ7IGZvbnQtZmFtaWx5OiBWZXJkYW5hLEdlbj0NCmV2YSxzYW5zLXNlcmlmJz4NCjxwPnNkbGtqc2RqZjwvcD4NCg0KPC9ib2R5PjwvaHRtbD4NCg0KLS09X2NlZmYwZmQxOTc1NmY0NWVkMTI5NWVlMjA2OWZmOGUwLS0NCg0KLS09XzI5MWI4ZTk2NTY0MjY1NjM2NDMyYzZkNDk0ZTAyMzIyDQpDb250ZW50LVRyYW5zZmVyLUVuY29kaW5nOiBiYXNlNjQNCkNvbnRlbnQtVHlwZTogdGV4dC9wbGFpbjsNCm5hbWU9eG9yZy5jb25mDQpDb250ZW50LURpc3Bvc2l0aW9uOiBhdHRhY2htZW50Ow0KZmlsZW5hbWU9eG9yZy5jb25mOw0Kc2l6ZT0yMTENCg0KVTJWamRHbHZiaUFpUkdWMmFXTmxJZ29nSUNBZ1NXUmxiblJwWm1sbGNpQWdJQ0FnSWtSbGRtbGpaVEFpQ2lBZ0lDQkVjbWwyWlhJZw0KSUNBZ0lFSnZZWEprVG1GdFpTQWdJQ0FnSUNKT1ZsTWdOREl3TUUwaUNpQWdJQ0JQY0hScGIyNGdJazV2VEc5bmJ5SWdJblJ5ZFdVaQ0KQ2lBZ0lDQlBjSFJwYjI0Z0lsVnpaVVZFU1VRaUlDSjBjblZsSWdwRmJtUlRaV04wYVc5dUNnPT0NCi0tPV8yOTFiOGU5NjU2NDI2NTYzNjQzMmM2ZDQ5NGUwMjMyMi0tAAEBVgMyAAEAAlMDSVBNLk5vdGUAAXwDdXJuOmNvbnRlbnQtY2xhc3NlczptZXNzYWdlAAEAEU5PUAN4b3JnLmNvbmYAAVEDMzhiOTUwZWJkNjJjZDlhNjY5MjljODk2MTVkMGZjMDQ6OjU6OjIAAVIDMQABTAMzNTEwMDIxMgABAQEBAQEBAQE='), + $output + ); + } + + public function testEncodeEmailPerformanceTest() + { + $outputStream = fopen("php://temp", 'r+'); + + $encoder = new Syncroton_Wbxml_Encoder($outputStream, 'UTF-8', 3); + $attachment = str_repeat("ICAgIEJvYXJkTmFtZSAgICAgICJOVlMgNDIwME0iCiAgICBPcHRpb24gIk5vTG9nbyIgInRydWUi \n", 100000); + $xml = <<<EOF + <?xml version="1.0" encoding="utf-8"?> + <!DOCTYPE AirSync PUBLIC "-//AIRSYNC//DTD AirSync//EN" "http://www.microsoft.com/"> + <Sync xmlns="uri:AirSync" xmlns:Syncroton="uri:Syncroton" xmlns:AirSyncBase="uri:AirSyncBase" xmlns:Email="uri:Email" xmlns:Email2="uri:Email2" xmlns:Tasks="uri:Tasks"> + <Collections> + <Collection xmlns:default="uri:Email" xmlns:default1="uri:AirSyncBase"> + <Class>Email</Class> + <SyncKey>2</SyncKey> + <CollectionId>38b950ebd62cd9a66929c89615d0fc04</CollectionId> + <Status>1</Status> + <MoreAvailable/> + <Commands xmlns:default="uri:Email" xmlns:default1="uri:AirSyncBase"> + <Add xmlns:default="uri:Email" xmlns:default1="uri:AirSyncBase"> + <ServerId>38b950ebd62cd9a66929c89615d0fc04::1</ServerId> + <ApplicationData> + <Email:DateReceived xmlns="uri:Email">2023-05-06T14:51:40.000Z</Email:DateReceived> + <Email:From xmlns="uri:Email">"Mollekopf, Christian" <christian@example.ch></Email:From> + <Email:InternetCPID xmlns="uri:Email">65001</Email:InternetCPID> + <Email:Subject xmlns="uri:Email">Foobar 1</Email:Subject> + <Email:To xmlns="uri:Email">christian@example.ch</Email:To> + <Email:Read xmlns="uri:Email">0</Email:Read> + <Email:Flag xmlns="uri:Email"/> + <AirSyncBase:Body xmlns="uri:AirSyncBase"> + <AirSyncBase:Type>4</AirSyncBase:Type> + <AirSyncBase:Data>Return-Path: <christian@example.ch> + Received: from imapb010.mykolab.com (unix socket) + by imapb010.mykolab.com (Cyrus 2.5.10-49-g2e214b4-Kolab-2.5.10-8.1.el7.kolab_14) with LMTPA; + Wed, 09 Aug 2017 18:37:01 +0200 + X-Sieve: CMU Sieve 2.4 + Received: from int-mx002.mykolab.com (unknown 10.9.13.2) + by imapb010.mykolab.com (Postfix) with ESMTPS id 0A93910A25047 + for <christian@example.ch>; Wed, 9 Aug 2017 18:37:01 +0200 (CEST) + Received: from int-subm002.mykolab.com (unknown 10.9.37.2) + by int-mx002.mykolab.com (Postfix) with ESMTPS id EC06AF6E + for <christian@example.ch>; Wed, 9 Aug 2017 18:37:00 +0200 (CEST) + MIME-Version: 1.0 + Content-Type: multipart/mixed; + boundary="=_291b8e96564265636432c6d494e02322" + Date: Sat, 06 May 2023 14:41:40 + From: "Mollekopf, Christian" <christian@example.ch> + To: christian@example.ch + Subject: Foobar 1 + Message-ID: <foobar1@example.org> + + --=_291b8e96564265636432c6d494e02322 + Content-Type: multipart/alternative; + boundary="=_ceff0fd19756f45ed1295ee2069ff8e0" + + --=_ceff0fd19756f45ed1295ee2069ff8e0 + Content-Transfer-Encoding: 7bit + Content-Type: text/plain; charset=US-ASCII + + sdlkjsdjf + --=_ceff0fd19756f45ed1295ee2069ff8e0 + Content-Transfer-Encoding: quoted-printable + Content-Type: text/html; charset=UTF-8 + + <html><head><meta http-equiv=3D"Content-Type" content=3D"text/html; charset= + =3DUTF-8" /></head><body style=3D'font-size: 10pt; font-family: Verdana,Gen= + eva,sans-serif'> + <p>sdlkjsdjf</p> + + </body></html> + + --=_ceff0fd19756f45ed1295ee2069ff8e0-- + + --=_291b8e96564265636432c6d494e02322 + Content-Transfer-Encoding: base64 + Content-Type: text/plain; + name=xorg.conf + Content-Disposition: attachment; + filename=xorg.conf; + size=211 + + U2VjdGlvbiAiRGV2aWNlIgogICAgSWRlbnRpZmllciAgICAgIkRldmljZTAiCiAgICBEcml2ZXIg + {$attachment} + CiAgICBPcHRpb24gIlVzZUVESUQiICJ0cnVlIgpFbmRTZWN0aW9uCg== + --=_291b8e96564265636432c6d494e02322--</AirSyncBase:Data> + </AirSyncBase:Body> + <AirSyncBase:NativeBodyType xmlns="uri:AirSyncBase">2</AirSyncBase:NativeBodyType> + <Email:MessageClass xmlns="uri:Email">IPM.Note</Email:MessageClass> + <Email:ContentClass xmlns="uri:Email">urn:content-classes:message</Email:ContentClass> + <AirSyncBase:Attachments xmlns="uri:AirSyncBase"> + <AirSyncBase:Attachment> + <AirSyncBase:DisplayName>xorg.conf</AirSyncBase:DisplayName> + <AirSyncBase:FileReference>38b950ebd62cd9a66929c89615d0fc04::5::2</AirSyncBase:FileReference> + <AirSyncBase:Method>1</AirSyncBase:Method> + <AirSyncBase:EstimatedDataSize>35100212</AirSyncBase:EstimatedDataSize> + </AirSyncBase:Attachment> + </AirSyncBase:Attachments> + </ApplicationData> + </Add> + </Commands> + </Collection> + </Collections> + </Sync> + EOF; + + $dom = new DOMDocument(); + $dom->loadXML($xml); + + $start = microtime(true); + $encoder->encode($dom); + $end = microtime(true); + + $this->assertTrue($end - $start < 0.05); + } +} +
View file
kolab-syncroton.dsc
Changed
@@ -2,15 +2,15 @@ Source: kolab-syncroton Binary: kolab-syncroton Architecture: all -Version: 2.3.10-0~kolab1 -Maintainer: Jeroen van Meeuwen (Kolab Systems) <vanmeeuwen@kolabsys.com> -Uploaders: Paul Klos <kolab@klos2day.nl> +Version: 1:2.4.2.10-1~kolab1 +Maintainer: Jeroen van Meeuwen <vanmeeuwen@kolabsys.com> +Uploaders: Jeroen van Meeuwen <vanmeeuwen@kolabsys.com> Homepage: http://www.kolab.org/ Standards-Version: 3.9.3 Vcs-Git: http://git.kolab.org/kolab-syncroton -Build-Depends: debhelper (>= 8), po-debconf +Build-Depends: debhelper (>= 8), po-debconf, psa | bash Package-List: kolab-syncroton deb utils extra Files: - 00000000000000000000000000000000 0 kolab-syncroton-2.3.10.tar.gz + 00000000000000000000000000000000 0 kolab-syncroton-2.4.0.tar.gz 00000000000000000000000000000000 0 debian.tar.gz
View file
kolab-syncroton.logrotate
Changed
@@ -1,4 +1,4 @@ -/var/log/kolab-syncroton/console /var/log/kolab-syncroton/errors /var/log/kolab-syncroton/imap /var/log/kolab-syncroton/ldap /var/log/kolab-syncroton/sendmail /var/log/kolab-syncroton/sieve /var/log/kolab-syncroton/smtp /var/log/kolab-syncroton/sql /var/log/kolab-syncroton/userlogins { +/var/log/kolab-syncroton/*.log { missingok compress notifempty
View file
plesk.kolab_syncroton.inc.php
Added
@@ -0,0 +1,123 @@ +<?php + +// This file lists all ActiveSync-related configuration options + +// Enables ActiveSync protocol debuging +$config'activesync_debug' = false; + +// If specified all ActiveSync-related logs will be saved to this file +// Note: This doesn't change Roundcube Framework log locations +$config'activesync_log_file' = null; + +// Type of ActiveSync cache. Supported values: 'db', 'apc' and 'memcache'. +// Note: This is only for some additional data like timezones mapping. +$config'activesync_cache' = 'db'; + +// lifetime of ActiveSync cache +// possible units: s, m, h, d, w +$config'activesync_cache_ttl' = '1d'; + +// Type of ActiveSync Auth cache. Supported values: 'db', 'apc' and 'memcache'. +// Note: This is only for username canonification map. +$config'activesync_auth_cache' = 'db'; + +// lifetime of ActiveSync Auth cache +// possible units: s, m, h, d, w +$config'activesync_auth_cache_ttl' = '1d'; + +// List of global addressbooks (GAL) +// Note: If empty 'autocomplete_addressbooks' setting will be used +$config'activesync_addressbooks' = array(); + +// ActiveSync => Roundcube contact fields map for GAL search +/* Default: array( + 'alias' => 'nickname', + 'company' => 'organization', + 'displayName' => 'name', + 'emailAddress' => 'email', + 'firstName' => 'firstname', + 'lastName' => 'surname', + 'mobilePhone' => 'phone.mobile', + 'office' => 'office', + 'picture' => 'photo', + 'phone' => 'phone', + 'title' => 'jobtitle', +); +*/ +$config'activesync_gal_fieldmap' = null; + +// List of device types that will sync the LDAP addressbook(s) as a normal folder. +// For devices that do not support GAL searching, e.g. Outlook. +// Note: To make the LDAP addressbook sources working we need two additional +// fields ('uid' and 'changed') specified in the fieldmap array +// of the LDAP configuration ('ldap_public' option). For example: +// 'uid' => 'nsuniqueid', +// 'changed' => 'modifytimestamp', +// Examples: +// array('windowsoutlook') # enable for Oultook only +// true # enable for all +$config'activesync_gal_sync' = false; + +// GAL cache. As reading all contacts from LDAP may be slow, caching is recommended. +$config'activesync_gal_cache' = 'db'; + +// TTL of GAL cache entries. Technically this causes that synchronized +// contacts will not be updated (queried) often than the specified interval. +$config'activesync_gal_cache_ttl' = '1d'; + +// List of Roundcube plugins +// WARNING: Not all plugins used in Roundcube can be listed here +$config'activesync_plugins' = array( + 'libcalendaring', + 'libkolab' +); + +// Defines for how many seconds we'll sleep between every +// action for detecting changes in folders. Default: 60 +$config'activesync_ping_timeout' = 60; + +// Defines maximum Ping interval in seconds. Default: 900 (15 minutes) +$config'activesync_ping_interval' = 900; + +// We start detecting changes n seconds since the last sync of a folder +// Default: 180 +$config'activesync_quiet_time' = 0; + +// Defines maximum number of folders in a single Sync/Ping request. Default: 100. +$config'activesync_max_folders' = 100; + +// When a device is reqistered, by default a set of folders are +// subscribed for syncronization, i.e. INBOX and personal folders with +// defined folder type: +// mail.drafts, mail.wastebasket, mail.sentitems, mail.outbox, +// event, event.default, +// contact, contact.default, +// task, task.default +// This default set can be extended by adding following values: +// 1 - all subscribed folders in personal namespace +// 2 - all folders in personal namespace +// 4 - all subscribed folders in other users namespace +// 8 - all folders in other users namespace +// 16 - all subscribed folders in shared namespace +// 32 - all folders in shared namespace +$config'activesync_init_subscriptions' = 21; + +// Defines blacklist of devices (device type strings) that do not support folder hierarchies. +// When set to an array folder hierarchies are used on all devices not listed here. +// When set to null an old whitelist approach will be used where we do opposite +// action and enable folder hierarchies only on device types known to support it. +$config'activesync_multifolder_blacklist' = array(); + +// Blacklist overwrites for specified object type. If set to an array +// it will have a precedence over 'activesync_multifolder_blacklist' list only for that type. +// Note: Outlook does not support multiple folders for contacts, +// in that case use $config'activesync_multifolder_blacklist_contact' = array('windowsoutlook'); +$config'activesync_multifolder_blacklist_mail' = null; +$config'activesync_multifolder_blacklist_event' = null; +$config'activesync_multifolder_blacklist_contact' = array('windowsoutlook'); +$config'activesync_multifolder_blacklist_note' = null; +$config'activesync_multifolder_blacklist_task' = null; + +// Enables adding sender name in the From: header of send email +// when a device uses email address only (e.g. iOS devices) +$config'activesync_fix_from' = true;
View file
release.sh
Added
@@ -0,0 +1,15 @@ +#!/bin/bash + +./buildtarball.sh + +# Autobump the version +CURRENT_VERSION=$(grep '^Version: ' ./*.spec | sed 's/Version: //') +NEW_VERSION=$(echo "$CURRENT_VERSION" | awk -F. '/0-9+\./{$NF++;print}' OFS=.) +echo "Bumping from $CURRENT_VERSION to $NEW_VERSION" + +sed -i "s/$CURRENT_VERSION/$NEW_VERSION/" debian.changelog +sed -i "s/^Version:.*/Version: 1:$NEW_VERSION-1~kolab1/" ./*.dsc +sed -i "s/^Version:.*/Version: $NEW_VERSION/" ./*.spec + +osc ci -m "New release $NEW_VERSION" +osc sr Kolab:16 --yes -m "New release $NEW_VERSION"
Locations
Projects
Search
Status Monitor
Help
Open Build Service
OBS Manuals
API Documentation
OBS Portal
Reporting a Bug
Contact
Mailing List
Forums
Chat (IRC)
Twitter
Open Build Service (OBS)
is an
openSUSE project
.