diff --git a/scripts/install b/scripts/install
old mode 100644
new mode 100755
index 51822c9..a18ad92
--- a/scripts/install
+++ b/scripts/install
@@ -96,7 +96,15 @@ ynh_app_setting_set "$app" final_phpconf "$final_phpconf"
# Copy files to the right place
sudo mkdir -p $final_path
-sudo cp -a ../sources/* $final_path
+
+# We download the sources and check the md5sum
+SHA1=`sudo cat ../sources/source_sha1`;
+sed -i "s@SHA1TOCHANGE@$SHA1@g" ../sources/source_url
+sed -i "s@SHA1TOCHANGE@$SHA1@g" ../sources/source_md5
+sudo wget -nv -i ../sources/source_url -O Z-Push-contrib-${SHA1}.zip
+sudo md5sum -c ../sources/source_md5 --status || (echo "Corrupt source" >&2 && false)
+sudo unzip Z-Push-contrib-${SHA1}.zip -d ../sources/
+sudo cp -R ../sources/Z-Push-contrib-${SHA1}/* $final_path
# Configuration
sed -i "s^define('TIMEZONE', .*^define('TIMEZONE', '$(cat /etc/timezone)');^" ../conf/config.php
@@ -140,7 +148,6 @@ fi
# Set permissions to z-push directory
sudo chown -R www-data: $final_path
-
# Create log directory
sudo mkdir -p $final_logpath
sudo chmod 750 $final_logpath
diff --git a/scripts/remove b/scripts/remove
old mode 100644
new mode 100755
diff --git a/scripts/upgrade b/scripts/upgrade
old mode 100644
new mode 100755
index c0bc626..0745fcc
--- a/scripts/upgrade
+++ b/scripts/upgrade
@@ -57,7 +57,15 @@ sudo rm -f $final_phpconf
# Copy files to the right place
sudo mkdir -p $final_path
-sudo cp -a ../sources/* $final_path
+
+# We download the sources and check the md5sum
+SHA1=`sudo cat ../sources/source_sha1`;
+sed -i "s@SHA1TOCHANGE@$SHA1@g" ../sources/source_url
+sed -i "s@SHA1TOCHANGE@$SHA1@g" ../sources/source_md5
+sudo wget -nv -i ../sources/source_url -O Z-Push-contrib-${SHA1}.zip
+sudo md5sum -c ../sources/source_md5 --status || (echo "Corrupt source" >&2 && false)
+sudo unzip Z-Push-contrib-${SHA1}.zip -d ../sources/
+sudo cp -R ../sources/Z-Push-contrib-${SHA1}/* $final_path
# Configuration
sed -i "s^define('TIMEZONE', .*^define('TIMEZONE', '$(cat /etc/timezone)');^" ../conf/config.php
diff --git a/sources/INSTALL b/sources/INSTALL
deleted file mode 100644
index f870974..0000000
--- a/sources/INSTALL
+++ /dev/null
@@ -1,290 +0,0 @@
-Installing Z-Push
-======================
-
-Requirements
-------------
-
-Z-Push 2 runs only on PHP 5.3 or later
-A PEAR dependency as in previous versions does not exist in Z-Push 2.
-
-The PHP version requirement is met in these distributions and versions (or later).
-
-Debian 6.0 (squeeze)
-Ubuntu 10.04 (LTS lucid)
-RHEL/CentOS 6 (You can use SCL for newer version without overwriting system files: https://www.softwarecollections.org)
-Fedora 12 (constantine)
-OpenSuse 11.2
-Slackware 13.37
-FreeBSD 7.4
-OpenBSD 5.0
-
-If your distribution is not listed here, you can check which PHP version
-is default for it at http://distrowatch.com/.
-
-Additional informations can be found in the Zarafa Administrator Manual:
-http://doc.zarafa.com/trunk/Administrator_Manual/en-US/html/_zpush.html
-
-
-Additional php packages
-----------------------
-To use the full featureset of Z-Push 2 and the z-push-top command line utility,
-additional php packages are required. These provide SOAP support, access to
-process control and shared memory.
-
-These packages vary in names between the distributions.
-- Generally install the packages: php-cli php-soap
-- On Suse (SLES & OpenSuse) install the packages: php53 php53-soap php53-pcntl php53-sysvshm php53-sysvsem php53-posix
-- On RHEL based systems install the package: php-cli php-soap php-process
- In order to install these packages you need to add an extra channel subscription
- from the RHEL Server Optional channel.
-
-
-Be aware that each backend can have their own requirements. Take a look to the
-REQUIREMENTS file inside its folder for more information.
-
-
-How to install
---------------
-
-To install Z-Push, simply untar the z-push archive, e.g. with:
- tar -xzvf z-push-[version]-{buildnr}.tar.gz
-
-The tar contains a folder which has the following structure:
- z-push-[version]-{buildnr}
-
-The contents of this folder should be copied to /usr/share/z-push.
-In a case that /usr/share/z-push does not exist yet, create it with:
- mkdir -p /usr/share/z-push
-
- cp -R z-push-[version]-{buildnr}/* /usr/share/z-push/
-
-Edit the config.php file in the Z-Push directory to fit your needs.
-If you intend to use Z-Push with Zarafa backend and Zarafa is installed
-on the same server, it should work out of the box without changing anything.
-Please also set your timezone in the config.php file.
-
-The parameters and their roles are also explained in the config.php file.
-
-By default the state directory is /var/lib/z-push, the log directory /var/log/z-push.
-Make sure that these directories exist and are writeable for your webserver
-process, so either change the owner of these directories to the UID of
-your apache process or make the directories world writeable:
-
- chmod 755 /var/lib/z-push /var/log/z-push
- chown apache:apache /var/lib/z-push /var/log/z-push
-
-For the default webserver user please refer to your distribution's manual.
-
-Now, you must configure Apache to redirect the URL
-'Microsoft-Server-ActiveSync' to the index.php file in the Z-Push
-directory. This can be done by adding the line:
-
- Alias /Microsoft-Server-ActiveSync /usr/share/z-push/index.php
-
-to your httpd.conf file. Make sure that you are adding the line to the
-correct part of your Apache configuration, taking care of virtual hosts and
-other Apache configurations.
-Another possibility is to add this line to z-push.conf file inside the directory
-which contents are automatically processed during the webserver start (by
-default it is conf.d inside the /etc/apache2 or /etc/httpd depending on your
-distribution).
-
-You have to reload your webserver after making these configurations.
-
-*WARNING* You CANNOT simply rename the z-push directory to
-Microsoft-Server-ActiveSync. This will cause Apache to send redirects to the
-mobile device, which will definitely break your mobile device synchronisation.
-
-Lastly, make sure that PHP has the following settings:
-
- php_flag magic_quotes_gpc off
- php_flag register_globals off
- php_flag magic_quotes_runtime off
- php_flag short_open_tag on
-
-You can set this in the httpd.conf, in php.ini or in an .htaccess file in
-the root of z-push.
-
-If you have several php applications on the same system, you could specify the
-z-push directory so these settings are considered only there.
-
- php_flag magic_quotes_gpc off
- php_flag register_globals off
- php_flag magic_quotes_runtime off
- php_flag short_open_tag on
-
-
-If you don't set this up correctly, you will not be
-able to login correctly via z-push.
-
-Please also set a memory_limit for php to 128M in php.ini.
-
-Z-Push writes files to your file system like logs or data from the
-FileStateMachine (which is default). In order to make this possible,
-you either need to disable the php-safe-mode in php.ini or .htaccess with
- php_admin_flag safe_mode off
-or configure it accordingly, so Z-Push is allowed to write to the
-log and state directories.
-
-After doing this, you should be able to synchronize with your mobile device.
-
-To use the command line tools, access the installation directory
-(usually /usr/share/z-push) and execute:
- ./z-push-top.php and/or
- ./z-push-admin.php
-
-To facilitate the access symbolic links can be created, by executing:
- ln -s /usr/share/z-push/z-push-admin.php /usr/sbin/z-push-admin
- ln -s /usr/share/z-push/z-push-top.php /usr/sbin/z-push-top
-
-With these symlinks in place the cli tools can be accessed from any
-directory and without the php file extension.
-
-
-Upgrade
--------
-Upgrading to a newer Z-Push version follows the same path as the
-initial installation.
-When upgrading to a new minor version e.g. from Z-Push 1.4 to
-Z-Push 1.4.1, the existing Z-Push directory can be overwritten
-when extracting the archive. When installing a new major version
-it is recommended to extract the tarball to another directory and
-to copy the state from the existing installation.
-
-*Important*
-It is crucial to always keep the data of the state directory in order
-to ensure data consistency on already synchronized mobiles.
-
-Without the state information mobile devices, which already have an
-ActiveSync profile, will receive duplicate items or the synchronization
-will break completely.
-
-*Important*
-Upgrading to Z-Push 2.X from 1.X it is not necessary to copy the state
-directory because states are not compatible. However Z-Push 2 implements
-a fully automatic resynchronizing of devices in the case states are
-missing or faulty.
-
-*Important*
-Downgrading from Z-Push 2.X to 1.X is not simple. As the states are not
-compatible you would have to follow the procedure for a new installation
-and re-create profiles on every device.
-
-*Important*
-States of Z-Push 2.0 and Z-Push 2.1 are not compatible. A state migration
-script called migrate-2.0.x-2.1.0.php is available in the tools folder.
-
-*Important*
-When running Z-Push seperately from your Zarafa installation you had in
-the past to configure MAPI_SERVER directly in the config.php of Z-Push.
-This setting has now moved to the config.php file of the Zarafa backend
-(backend/zarafa/config.php).
-
-Please also observe the published release notes of the new Z-Push version.
-For some releases it is necessary to e.g. resynchronize the mobile.
-
-
-S/MIME
-------
-Z-Push supports signing and en-/decrypting of emails on mobile devices
-since the version 2.0.7.
-
-*Important*
-Currently only Android 4.X and higher and iOS 5 and higher devices are
-known to support encryption/signing of emails.
-
-It might be possible that PHP functions require CA information in order
-to validate certs. Therefore the CAINFO parameter in the config.php
-must be configured properly.
-
-The major part of S/MIME deployment is the PKI setup. It includes the
-public-private key/certificate obtaining, their management in directory
-service and roll-out to the mobile devices. Individual certificates can
-either be obtained from a local (company intern) or a public CA. There
-are various public CAs offering certificates: commercial ones e.g.
-Symantec or Comodo or community-driven e.g. CAcert.org.
-
-Both most popular directory services Microsoft Active Directory (MS AD)
-and free open source solution OpenLDAP allow to save certificates. Private
-keys/certificates reside in user’s directory or on a smartcard. Public
-certificates are saved in directory. MS AD and OpenLDAP both use
-serCertificate attribute to save it.
-
-In Active Directory the public key for contacts from GAB is saved in
-PR_EMS_AB_TAGGED_X509_CERT (0x8C6A1102) property and if you save a key
-in a contact it’s PR_USER_X509_CERTIFICATE (0x3A701102).
-
-In LDAP public key for contacts from GAB is saved in userCertificate
-property. It should be mapped to 0x3A220102 in ldap.propmap.cfg
-(0x3A220102 = userCertificate). Make sure it looks like this in LDAP:
-
-userCertificate;binary
- MIIFGjCCBAKgAwIBAgIQbRnqpxlPa…
-
-*Important*
-It is strongly recommended to use MS AD or LDAP to manage certificates.
-Other user plugin options like db or unix might not work correctly and
-are not supported.
-
-For in-depth information please refer to:
-http://www.zarafa.com/blog/post/2013/05/smime-z-push-signing-and-en-decrypting-emails-mobile-devices
-
-Setting up your mobile device
------------------------------
-
-This is simply a case of adding an 'exchange server' to your activesync
-server list, specifying the IP address of the Z-Push's apache server,
-disabling SSL, unless you have already setup SSL on your Apache server,
-setting the correct username and password (the domain is ignored, you can
-simply specify 'domain' or some other random string), and then going through
-the standard activesync settings.
-
-Once you have done this, you should be able to synchronise your mobile
-simply by clicking the 'Sync' button in ActiveSync on your mobile.
-
-*NOTE* using the synchronisation without SSL is not recommended because
-your private data is transmitted in clear text over the net. Configuring
-SSL on Apache is beyond of the scope of this document. Please refer to
-Apache documention available at http://httpd.apache.org/docs/
-
-
-Troubleshooting
----------------
-
-Most problems will be caused by incorrect Apache settings. To test whether
-your Apache setup is working correctly, you can simply type the Z-Push URL
-in your browser, to see if apache is correctly redirecting your request to
-z-push. You can simply use:
-
- http:///Microsoft-Server-ActiveSync
-
-If correctly configured, you should see a username/password request and
-when you specify a valid username and password, you should see a Z-Push
-information page, saying that this kind of requests is not supported.
-Without authentication credentials Z-Push displays general information.
-
-If not then check your PHP and Apache settings and Apache error logs.
-
-If you have other synchronisation problems, you can increase the LOGLEVEL
-parameter in the config e.g. to LOGLEVEL_DEBUG or LOGLEVEL_WBXML.
-
-The z-push.log file will then collect detailed debug information from your
-synchronisation.
-
-*NOTE* This setting will set Z-Push to log the detailed information for
-*every* user on the system. You can set a different log level for particular
-users by adding them comma separated to $specialLogUsers in the config.php
- e.g. $specialLogUsers = array("user1", "user2", "user3");
-
-*NOTE* Be aware that if you are using LOGLEVEL_DEBUG and LOGLEVEL_WBXML
- Z-Push will be quite talkative, so it is advisable to use log-rotate
- on the log file.
-
-*Repeated incorrect password messages*
-If a password contains characters which are encoded differently in ISO-8859-1
-and Windows-1252 encodings (e.g. "§") the login might fail with Z-Push but
-it works fine with the WebApp/Webaccess. The solution is to add:
-
-setlocale(LC_CTYPE, "en_US.UTF-8");
-
-to the config.php file.
diff --git a/sources/INSTALL_UPDATE_FROM_GIT b/sources/INSTALL_UPDATE_FROM_GIT
deleted file mode 100644
index 01f3cd5..0000000
--- a/sources/INSTALL_UPDATE_FROM_GIT
+++ /dev/null
@@ -1,21 +0,0 @@
-Installing and updating from GIT can be time consuming, because we will have conflicts
-with the config.php files.
-Until upstream finds a good solution we can use this trick:
-
-
-Setup
-
- Clone the repository to your web server
- git checkout -b config to create the new config branch with the local configuration
-
-Updating
-
- Backup the local repository (or even better: also do this on a non-live copy)
- git checkout master
- git pull origin master to update the local master branch
- git checkout config
- git rebase master to rebase the offline config branch
- Solve the conflicts (which will occur if the configuration files have been changed in the master)
-
-
-Instructions provided by Martin Porcheron (@mporcheron)
diff --git a/sources/LICENSE b/sources/LICENSE
deleted file mode 100644
index 2ca933b..0000000
--- a/sources/LICENSE
+++ /dev/null
@@ -1,696 +0,0 @@
- GNU AFFERO GENERAL PUBLIC LICENSE
- Version 3, 19 November 2007
-
- Copyright (C) 2007 Free Software Foundation, Inc.
- Everyone is permitted to copy and distribute verbatim copies
- of this license document, but changing it is not allowed.
-
- Preamble
-
- The GNU Affero General Public License is a free, copyleft license for
-software and other kinds of works, specifically designed to ensure
-cooperation with the community in the case of network server software.
-
- The licenses for most software and other practical works are designed
-to take away your freedom to share and change the works. By contrast,
-our General Public Licenses are intended to guarantee your freedom to
-share and change all versions of a program--to make sure it remains free
-software for all its users.
-
- When we speak of free software, we are referring to freedom, not
-price. Our General Public Licenses are designed to make sure that you
-have the freedom to distribute copies of free software (and charge for
-them if you wish), that you receive source code or can get it if you
-want it, that you can change the software or use pieces of it in new
-free programs, and that you know you can do these things.
-
- Developers that use our General Public Licenses protect your rights
-with two steps: (1) assert copyright on the software, and (2) offer
-you this License which gives you legal permission to copy, distribute
-and/or modify the software.
-
- A secondary benefit of defending all users' freedom is that
-improvements made in alternate versions of the program, if they
-receive widespread use, become available for other developers to
-incorporate. Many developers of free software are heartened and
-encouraged by the resulting cooperation. However, in the case of
-software used on network servers, this result may fail to come about.
-The GNU General Public License permits making a modified version and
-letting the public access it on a server without ever releasing its
-source code to the public.
-
- The GNU Affero General Public License is designed specifically to
-ensure that, in such cases, the modified source code becomes available
-to the community. It requires the operator of a network server to
-provide the source code of the modified version running there to the
-users of that server. Therefore, public use of a modified version, on
-a publicly accessible server, gives the public access to the source
-code of the modified version.
-
- An older license, called the Affero General Public License and
-published by Affero, was designed to accomplish similar goals. This is
-a different license, not a version of the Affero GPL, but Affero has
-released a new version of the Affero GPL which permits relicensing under
-this license.
-
- The precise terms and conditions for copying, distribution and
-modification follow.
-
- TERMS AND CONDITIONS
-
- 0. Definitions.
-
- "This License" refers to version 3 of the GNU Affero General Public License.
-
- "Copyright" also means copyright-like laws that apply to other kinds of
-works, such as semiconductor masks.
-
- "The Program" refers to any copyrightable work licensed under this
-License. Each licensee is addressed as "you". "Licensees" and
-"recipients" may be individuals or organizations.
-
- To "modify" a work means to copy from or adapt all or part of the work
-in a fashion requiring copyright permission, other than the making of an
-exact copy. The resulting work is called a "modified version" of the
-earlier work or a work "based on" the earlier work.
-
- A "covered work" means either the unmodified Program or a work based
-on the Program.
-
- To "propagate" a work means to do anything with it that, without
-permission, would make you directly or secondarily liable for
-infringement under applicable copyright law, except executing it on a
-computer or modifying a private copy. Propagation includes copying,
-distribution (with or without modification), making available to the
-public, and in some countries other activities as well.
-
- To "convey" a work means any kind of propagation that enables other
-parties to make or receive copies. Mere interaction with a user through
-a computer network, with no transfer of a copy, is not conveying.
-
- An interactive user interface displays "Appropriate Legal Notices"
-to the extent that it includes a convenient and prominently visible
-feature that (1) displays an appropriate copyright notice, and (2)
-tells the user that there is no warranty for the work (except to the
-extent that warranties are provided), that licensees may convey the
-work under this License, and how to view a copy of this License. If
-the interface presents a list of user commands or options, such as a
-menu, a prominent item in the list meets this criterion.
-
- 1. Source Code.
-
- The "source code" for a work means the preferred form of the work
-for making modifications to it. "Object code" means any non-source
-form of a work.
-
- A "Standard Interface" means an interface that either is an official
-standard defined by a recognized standards body, or, in the case of
-interfaces specified for a particular programming language, one that
-is widely used among developers working in that language.
-
- The "System Libraries" of an executable work include anything, other
-than the work as a whole, that (a) is included in the normal form of
-packaging a Major Component, but which is not part of that Major
-Component, and (b) serves only to enable use of the work with that
-Major Component, or to implement a Standard Interface for which an
-implementation is available to the public in source code form. A
-"Major Component", in this context, means a major essential component
-(kernel, window system, and so on) of the specific operating system
-(if any) on which the executable work runs, or a compiler used to
-produce the work, or an object code interpreter used to run it.
-
- The "Corresponding Source" for a work in object code form means all
-the source code needed to generate, install, and (for an executable
-work) run the object code and to modify the work, including scripts to
-control those activities. However, it does not include the work's
-System Libraries, or general-purpose tools or generally available free
-programs which are used unmodified in performing those activities but
-which are not part of the work. For example, Corresponding Source
-includes interface definition files associated with source files for
-the work, and the source code for shared libraries and dynamically
-linked subprograms that the work is specifically designed to require,
-such as by intimate data communication or control flow between those
-subprograms and other parts of the work.
-
- The Corresponding Source need not include anything that users
-can regenerate automatically from other parts of the Corresponding
-Source.
-
- The Corresponding Source for a work in source code form is that
-same work.
-
- 2. Basic Permissions.
-
- All rights granted under this License are granted for the term of
-copyright on the Program, and are irrevocable provided the stated
-conditions are met. This License explicitly affirms your unlimited
-permission to run the unmodified Program. The output from running a
-covered work is covered by this License only if the output, given its
-content, constitutes a covered work. This License acknowledges your
-rights of fair use or other equivalent, as provided by copyright law.
-
- You may make, run and propagate covered works that you do not
-convey, without conditions so long as your license otherwise remains
-in force. You may convey covered works to others for the sole purpose
-of having them make modifications exclusively for you, or provide you
-with facilities for running those works, provided that you comply with
-the terms of this License in conveying all material for which you do
-not control copyright. Those thus making or running the covered works
-for you must do so exclusively on your behalf, under your direction
-and control, on terms that prohibit them from making any copies of
-your copyrighted material outside their relationship with you.
-
- Conveying under any other circumstances is permitted solely under
-the conditions stated below. Sublicensing is not allowed; section 10
-makes it unnecessary.
-
- 3. Protecting Users' Legal Rights From Anti-Circumvention Law.
-
- No covered work shall be deemed part of an effective technological
-measure under any applicable law fulfilling obligations under article
-11 of the WIPO copyright treaty adopted on 20 December 1996, or
-similar laws prohibiting or restricting circumvention of such
-measures.
-
- When you convey a covered work, you waive any legal power to forbid
-circumvention of technological measures to the extent such circumvention
-is effected by exercising rights under this License with respect to
-the covered work, and you disclaim any intention to limit operation or
-modification of the work as a means of enforcing, against the work's
-users, your or third parties' legal rights to forbid circumvention of
-technological measures.
-
- 4. Conveying Verbatim Copies.
-
- You may convey verbatim copies of the Program's source code as you
-receive it, in any medium, provided that you conspicuously and
-appropriately publish on each copy an appropriate copyright notice;
-keep intact all notices stating that this License and any
-non-permissive terms added in accord with section 7 apply to the code;
-keep intact all notices of the absence of any warranty; and give all
-recipients a copy of this License along with the Program.
-
- You may charge any price or no price for each copy that you convey,
-and you may offer support or warranty protection for a fee.
-
- 5. Conveying Modified Source Versions.
-
- You may convey a work based on the Program, or the modifications to
-produce it from the Program, in the form of source code under the
-terms of section 4, provided that you also meet all of these conditions:
-
- a) The work must carry prominent notices stating that you modified
- it, and giving a relevant date.
-
- b) The work must carry prominent notices stating that it is
- released under this License and any conditions added under section
- 7. This requirement modifies the requirement in section 4 to
- "keep intact all notices".
-
- c) You must license the entire work, as a whole, under this
- License to anyone who comes into possession of a copy. This
- License will therefore apply, along with any applicable section 7
- additional terms, to the whole of the work, and all its parts,
- regardless of how they are packaged. This License gives no
- permission to license the work in any other way, but it does not
- invalidate such permission if you have separately received it.
-
- d) If the work has interactive user interfaces, each must display
- Appropriate Legal Notices; however, if the Program has interactive
- interfaces that do not display Appropriate Legal Notices, your
- work need not make them do so.
-
- A compilation of a covered work with other separate and independent
-works, which are not by their nature extensions of the covered work,
-and which are not combined with it such as to form a larger program,
-in or on a volume of a storage or distribution medium, is called an
-"aggregate" if the compilation and its resulting copyright are not
-used to limit the access or legal rights of the compilation's users
-beyond what the individual works permit. Inclusion of a covered work
-in an aggregate does not cause this License to apply to the other
-parts of the aggregate.
-
- 6. Conveying Non-Source Forms.
-
- You may convey a covered work in object code form under the terms
-of sections 4 and 5, provided that you also convey the
-machine-readable Corresponding Source under the terms of this License,
-in one of these ways:
-
- a) Convey the object code in, or embodied in, a physical product
- (including a physical distribution medium), accompanied by the
- Corresponding Source fixed on a durable physical medium
- customarily used for software interchange.
-
- b) Convey the object code in, or embodied in, a physical product
- (including a physical distribution medium), accompanied by a
- written offer, valid for at least three years and valid for as
- long as you offer spare parts or customer support for that product
- model, to give anyone who possesses the object code either (1) a
- copy of the Corresponding Source for all the software in the
- product that is covered by this License, on a durable physical
- medium customarily used for software interchange, for a price no
- more than your reasonable cost of physically performing this
- conveying of source, or (2) access to copy the
- Corresponding Source from a network server at no charge.
-
- c) Convey individual copies of the object code with a copy of the
- written offer to provide the Corresponding Source. This
- alternative is allowed only occasionally and noncommercially, and
- only if you received the object code with such an offer, in accord
- with subsection 6b.
-
- d) Convey the object code by offering access from a designated
- place (gratis or for a charge), and offer equivalent access to the
- Corresponding Source in the same way through the same place at no
- further charge. You need not require recipients to copy the
- Corresponding Source along with the object code. If the place to
- copy the object code is a network server, the Corresponding Source
- may be on a different server (operated by you or a third party)
- that supports equivalent copying facilities, provided you maintain
- clear directions next to the object code saying where to find the
- Corresponding Source. Regardless of what server hosts the
- Corresponding Source, you remain obligated to ensure that it is
- available for as long as needed to satisfy these requirements.
-
- e) Convey the object code using peer-to-peer transmission, provided
- you inform other peers where the object code and Corresponding
- Source of the work are being offered to the general public at no
- charge under subsection 6d.
-
- A separable portion of the object code, whose source code is excluded
-from the Corresponding Source as a System Library, need not be
-included in conveying the object code work.
-
- A "User Product" is either (1) a "consumer product", which means any
-tangible personal property which is normally used for personal, family,
-or household purposes, or (2) anything designed or sold for incorporation
-into a dwelling. In determining whether a product is a consumer product,
-doubtful cases shall be resolved in favor of coverage. For a particular
-product received by a particular user, "normally used" refers to a
-typical or common use of that class of product, regardless of the status
-of the particular user or of the way in which the particular user
-actually uses, or expects or is expected to use, the product. A product
-is a consumer product regardless of whether the product has substantial
-commercial, industrial or non-consumer uses, unless such uses represent
-the only significant mode of use of the product.
-
- "Installation Information" for a User Product means any methods,
-procedures, authorization keys, or other information required to install
-and execute modified versions of a covered work in that User Product from
-a modified version of its Corresponding Source. The information must
-suffice to ensure that the continued functioning of the modified object
-code is in no case prevented or interfered with solely because
-modification has been made.
-
- If you convey an object code work under this section in, or with, or
-specifically for use in, a User Product, and the conveying occurs as
-part of a transaction in which the right of possession and use of the
-User Product is transferred to the recipient in perpetuity or for a
-fixed term (regardless of how the transaction is characterized), the
-Corresponding Source conveyed under this section must be accompanied
-by the Installation Information. But this requirement does not apply
-if neither you nor any third party retains the ability to install
-modified object code on the User Product (for example, the work has
-been installed in ROM).
-
- The requirement to provide Installation Information does not include a
-requirement to continue to provide support service, warranty, or updates
-for a work that has been modified or installed by the recipient, or for
-the User Product in which it has been modified or installed. Access to a
-network may be denied when the modification itself materially and
-adversely affects the operation of the network or violates the rules and
-protocols for communication across the network.
-
- Corresponding Source conveyed, and Installation Information provided,
-in accord with this section must be in a format that is publicly
-documented (and with an implementation available to the public in
-source code form), and must require no special password or key for
-unpacking, reading or copying.
-
- 7. Additional Terms.
-
- "Additional permissions" are terms that supplement the terms of this
-License by making exceptions from one or more of its conditions.
-Additional permissions that are applicable to the entire Program shall
-be treated as though they were included in this License, to the extent
-that they are valid under applicable law. If additional permissions
-apply only to part of the Program, that part may be used separately
-under those permissions, but the entire Program remains governed by
-this License without regard to the additional permissions.
-
- When you convey a copy of a covered work, you may at your option
-remove any additional permissions from that copy, or from any part of
-it. (Additional permissions may be written to require their own
-removal in certain cases when you modify the work.) You may place
-additional permissions on material, added by you to a covered work,
-for which you have or can give appropriate copyright permission.
-
- Notwithstanding any other provision of this License, for material you
-add to a covered work, you may (if authorized by the copyright holders of
-that material) supplement the terms of this License with terms:
-
- a) Disclaiming warranty or limiting liability differently from the
- terms of sections 15 and 16 of this License; or
-
- b) Requiring preservation of specified reasonable legal notices or
- author attributions in that material or in the Appropriate Legal
- Notices displayed by works containing it; or
-
- c) Prohibiting misrepresentation of the origin of that material, or
- requiring that modified versions of such material be marked in
- reasonable ways as different from the original version; or
-
- d) Limiting the use for publicity purposes of names of licensors or
- authors of the material; or
-
- e) Declining to grant rights under trademark law for use of some
- trade names, trademarks, or service marks; or
-
- f) Requiring indemnification of licensors and authors of that
- material by anyone who conveys the material (or modified versions of
- it) with contractual assumptions of liability to the recipient, for
- any liability that these contractual assumptions directly impose on
- those licensors and authors.
-
- All other non-permissive additional terms are considered "further
-restrictions" within the meaning of section 10. If the Program as you
-received it, or any part of it, contains a notice stating that it is
-governed by this License along with a term that is a further
-restriction, you may remove that term. If a license document contains
-a further restriction but permits relicensing or conveying under this
-License, you may add to a covered work material governed by the terms
-of that license document, provided that the further restriction does
-not survive such relicensing or conveying.
-
- If you add terms to a covered work in accord with this section, you
-must place, in the relevant source files, a statement of the
-additional terms that apply to those files, or a notice indicating
-where to find the applicable terms.
-
- Additional terms, permissive or non-permissive, may be stated in the
-form of a separately written license, or stated as exceptions;
-the above requirements apply either way.
-
- 8. Termination.
-
- You may not propagate or modify a covered work except as expressly
-provided under this License. Any attempt otherwise to propagate or
-modify it is void, and will automatically terminate your rights under
-this License (including any patent licenses granted under the third
-paragraph of section 11).
-
- However, if you cease all violation of this License, then your
-license from a particular copyright holder is reinstated (a)
-provisionally, unless and until the copyright holder explicitly and
-finally terminates your license, and (b) permanently, if the copyright
-holder fails to notify you of the violation by some reasonable means
-prior to 60 days after the cessation.
-
- Moreover, your license from a particular copyright holder is
-reinstated permanently if the copyright holder notifies you of the
-violation by some reasonable means, this is the first time you have
-received notice of violation of this License (for any work) from that
-copyright holder, and you cure the violation prior to 30 days after
-your receipt of the notice.
-
- Termination of your rights under this section does not terminate the
-licenses of parties who have received copies or rights from you under
-this License. If your rights have been terminated and not permanently
-reinstated, you do not qualify to receive new licenses for the same
-material under section 10.
-
- 9. Acceptance Not Required for Having Copies.
-
- You are not required to accept this License in order to receive or
-run a copy of the Program. Ancillary propagation of a covered work
-occurring solely as a consequence of using peer-to-peer transmission
-to receive a copy likewise does not require acceptance. However,
-nothing other than this License grants you permission to propagate or
-modify any covered work. These actions infringe copyright if you do
-not accept this License. Therefore, by modifying or propagating a
-covered work, you indicate your acceptance of this License to do so.
-
- 10. Automatic Licensing of Downstream Recipients.
-
- Each time you convey a covered work, the recipient automatically
-receives a license from the original licensors, to run, modify and
-propagate that work, subject to this License. You are not responsible
-for enforcing compliance by third parties with this License.
-
- An "entity transaction" is a transaction transferring control of an
-organization, or substantially all assets of one, or subdividing an
-organization, or merging organizations. If propagation of a covered
-work results from an entity transaction, each party to that
-transaction who receives a copy of the work also receives whatever
-licenses to the work the party's predecessor in interest had or could
-give under the previous paragraph, plus a right to possession of the
-Corresponding Source of the work from the predecessor in interest, if
-the predecessor has it or can get it with reasonable efforts.
-
- You may not impose any further restrictions on the exercise of the
-rights granted or affirmed under this License. For example, you may
-not impose a license fee, royalty, or other charge for exercise of
-rights granted under this License, and you may not initiate litigation
-(including a cross-claim or counterclaim in a lawsuit) alleging that
-any patent claim is infringed by making, using, selling, offering for
-sale, or importing the Program or any portion of it.
-
- 11. Patents.
-
- A "contributor" is a copyright holder who authorizes use under this
-License of the Program or a work on which the Program is based. The
-work thus licensed is called the contributor's "contributor version".
-
- A contributor's "essential patent claims" are all patent claims
-owned or controlled by the contributor, whether already acquired or
-hereafter acquired, that would be infringed by some manner, permitted
-by this License, of making, using, or selling its contributor version,
-but do not include claims that would be infringed only as a
-consequence of further modification of the contributor version. For
-purposes of this definition, "control" includes the right to grant
-patent sublicenses in a manner consistent with the requirements of
-this License.
-
- Each contributor grants you a non-exclusive, worldwide, royalty-free
-patent license under the contributor's essential patent claims, to
-make, use, sell, offer for sale, import and otherwise run, modify and
-propagate the contents of its contributor version.
-
- In the following three paragraphs, a "patent license" is any express
-agreement or commitment, however denominated, not to enforce a patent
-(such as an express permission to practice a patent or covenant not to
-sue for patent infringement). To "grant" such a patent license to a
-party means to make such an agreement or commitment not to enforce a
-patent against the party.
-
- If you convey a covered work, knowingly relying on a patent license,
-and the Corresponding Source of the work is not available for anyone
-to copy, free of charge and under the terms of this License, through a
-publicly available network server or other readily accessible means,
-then you must either (1) cause the Corresponding Source to be so
-available, or (2) arrange to deprive yourself of the benefit of the
-patent license for this particular work, or (3) arrange, in a manner
-consistent with the requirements of this License, to extend the patent
-license to downstream recipients. "Knowingly relying" means you have
-actual knowledge that, but for the patent license, your conveying the
-covered work in a country, or your recipient's use of the covered work
-in a country, would infringe one or more identifiable patents in that
-country that you have reason to believe are valid.
-
- If, pursuant to or in connection with a single transaction or
-arrangement, you convey, or propagate by procuring conveyance of, a
-covered work, and grant a patent license to some of the parties
-receiving the covered work authorizing them to use, propagate, modify
-or convey a specific copy of the covered work, then the patent license
-you grant is automatically extended to all recipients of the covered
-work and works based on it.
-
- A patent license is "discriminatory" if it does not include within
-the scope of its coverage, prohibits the exercise of, or is
-conditioned on the non-exercise of one or more of the rights that are
-specifically granted under this License. You may not convey a covered
-work if you are a party to an arrangement with a third party that is
-in the business of distributing software, under which you make payment
-to the third party based on the extent of your activity of conveying
-the work, and under which the third party grants, to any of the
-parties who would receive the covered work from you, a discriminatory
-patent license (a) in connection with copies of the covered work
-conveyed by you (or copies made from those copies), or (b) primarily
-for and in connection with specific products or compilations that
-contain the covered work, unless you entered into that arrangement,
-or that patent license was granted, prior to 28 March 2007.
-
- Nothing in this License shall be construed as excluding or limiting
-any implied license or other defenses to infringement that may
-otherwise be available to you under applicable patent law.
-
- 12. No Surrender of Others' Freedom.
-
- If conditions are imposed on you (whether by court order, agreement or
-otherwise) that contradict the conditions of this License, they do not
-excuse you from the conditions of this License. If you cannot convey a
-covered work so as to satisfy simultaneously your obligations under this
-License and any other pertinent obligations, then as a consequence you may
-not convey it at all. For example, if you agree to terms that obligate you
-to collect a royalty for further conveying from those to whom you convey
-the Program, the only way you could satisfy both those terms and this
-License would be to refrain entirely from conveying the Program.
-
- 13. Remote Network Interaction; Use with the GNU General Public License.
-
- Notwithstanding any other provision of this License, if you modify the
-Program, your modified version must prominently offer all users
-interacting with it remotely through a computer network (if your version
-supports such interaction) an opportunity to receive the Corresponding
-Source of your version by providing access to the Corresponding Source
-from a network server at no charge, through some standard or customary
-means of facilitating copying of software. This Corresponding Source
-shall include the Corresponding Source for any work covered by version 3
-of the GNU General Public License that is incorporated pursuant to the
-following paragraph.
-
- Notwithstanding any other provision of this License, you have
-permission to link or combine any covered work with a work licensed
-under version 3 of the GNU General Public License into a single
-combined work, and to convey the resulting work. The terms of this
-License will continue to apply to the part which is the covered work,
-but the work with which it is combined will remain governed by version
-3 of the GNU General Public License.
-
- 14. Revised Versions of this License.
-
- The Free Software Foundation may publish revised and/or new versions of
-the GNU Affero General Public License from time to time. Such new versions
-will be similar in spirit to the present version, but may differ in detail to
-address new problems or concerns.
-
- Each version is given a distinguishing version number. If the
-Program specifies that a certain numbered version of the GNU Affero General
-Public License "or any later version" applies to it, you have the
-option of following the terms and conditions either of that numbered
-version or of any later version published by the Free Software
-Foundation. If the Program does not specify a version number of the
-GNU Affero General Public License, you may choose any version ever published
-by the Free Software Foundation.
-
- If the Program specifies that a proxy can decide which future
-versions of the GNU Affero General Public License can be used, that proxy's
-public statement of acceptance of a version permanently authorizes you
-to choose that version for the Program.
-
- Later license versions may give you additional or different
-permissions. However, no additional obligations are imposed on any
-author or copyright holder as a result of your choosing to follow a
-later version.
-
- 15. Disclaimer of Warranty.
-
- THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY
-APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT
-HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY
-OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,
-THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
-PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM
-IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF
-ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
-
- 16. Limitation of Liability.
-
- IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
-WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS
-THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY
-GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE
-USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF
-DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD
-PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),
-EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
-SUCH DAMAGES.
-
- 17. Interpretation of Sections 15 and 16.
-
- If the disclaimer of warranty and limitation of liability provided
-above cannot be given local legal effect according to their terms,
-reviewing courts shall apply local law that most closely approximates
-an absolute waiver of all civil liability in connection with the
-Program, unless a warranty or assumption of liability accompanies a
-copy of the Program in return for a fee.
-
- END OF TERMS AND CONDITIONS
-
- How to Apply These Terms to Your New Programs
-
- If you develop a new program, and you want it to be of the greatest
-possible use to the public, the best way to achieve this is to make it
-free software which everyone can redistribute and change under these terms.
-
- To do so, attach the following notices to the program. It is safest
-to attach them to the start of each source file to most effectively
-state the exclusion of warranty; and each file should have at least
-the "copyright" line and a pointer to where the full notice is found.
-
-
- Copyright (C)
-
- This program is free software: you can redistribute it and/or modify
- it under the terms of the GNU Affero General Public License as published by
- the Free Software Foundation, either version 3 of the License, or
- (at your option) any later version.
-
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU Affero General Public License for more details.
-
- You should have received a copy of the GNU Affero General Public License
- along with this program. If not, see .
-
-Also add information on how to contact you by electronic and paper mail.
-
- If your software can interact with users remotely through a computer
-network, you should also make sure that it provides a way for users to
-get its source. For example, if your program is a web application, its
-interface could display a "Source" link that leads users to an archive
-of the code. There are many ways you could offer source, and different
-solutions will be better for different programs; see section 13 for the
-specific requirements.
-
- You should also get your employer (if you work as a programmer) or school,
-if any, to sign a "copyright disclaimer" for the program, if necessary.
-For more information on this, and how to apply and follow the GNU AGPL, see
-.
-
-
----
-
-Copyright 2007 - 2013 Zarafa Deutschland GmbH
-
-This program is free software: you can redistribute it and/or modify
-it under the terms of the GNU Affero General Public License, version 3,
-as published by the Free Software Foundation with the following additional
-term according to sec. 7:
-
-According to sec. 7 of the GNU Affero General Public License, version 3,
-the terms of the AGPL are supplemented with the following terms:
-
-"Zarafa" is a registered trademark of Zarafa B.V.
-"Z-Push" is a registered trademark of Zarafa Deutschland GmbH
-The licensing of the Program under the AGPL does not imply a trademark license.
-Therefore any rights, title and interest in our trademarks remain entirely with us.
-
-However, if you propagate an unmodified version of the Program you are
-allowed to use the term "Z-Push" to indicate that you distribute the Program.
-Furthermore you may use our trademarks where it is necessary to indicate
-the intended purpose of a product or service provided you use it in accordance
-with honest practices in industrial or commercial matters.
-If you want to propagate modified versions of the Program under the name "Z-Push",
-you may only do so if you have a written permission by Zarafa Deutschland GmbH
-(to acquire a permission please contact Zarafa at trademark@zarafa.com).
-
-This program is distributed in the hope that it will be useful,
-but WITHOUT ANY WARRANTY; without even the implied warranty of
-MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-GNU Affero General Public License for more details.
-
-You should have received a copy of the GNU Affero General Public License
-along with this program. If not, see .
\ No newline at end of file
diff --git a/sources/NOTES b/sources/NOTES
deleted file mode 100644
index cab93eb..0000000
--- a/sources/NOTES
+++ /dev/null
@@ -1,4 +0,0 @@
-Run composer to update autoinclude
-==================================
-curl -sS https://getcomposer.org/installer | php
-php composer.phar dump-autoload -o
diff --git a/sources/README.md b/sources/README.md
deleted file mode 100644
index b10db1a..0000000
--- a/sources/README.md
+++ /dev/null
@@ -1,114 +0,0 @@
-Z-Push-contrib
-==============
-
-[](https://gitter.im/fmbiete/Z-Push-contrib?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)
-
-This is a Z-Push fork with changes that I will try to put into the contrib branch, so they can get into the official Z-Push
-
-IMPORTANT:
-For them to get into the official Z-Push, you must release the code under AGPLv3. Add this text to your commits "Released under the Affero GNU General Public License (AGPL) version 3" before merging.
-
-If you see some changes here, and you are the author, I will not contrib them, as I have no rights over them.
-But I will try to reimplement with different code/approach so I can contribute them. When a sustitution is ready I will remove your changes from this repo.
-If you want to help the community, contribute them yourself.
-
-
-IMPORTANT 2:
-All the code is AGPL licensed (or compatible, like the "include" files). So you can get a copy, modify it, use... your only obligation it's to publish your changes somewhere.
-
-
-----------------------------------------------------
-Original Z-Push
-
-URL: http://www.zpush.org
-
-Z-Push is an implementation of the ActiveSync protocol, which is used 'over-the-air' for multi platform ActiveSync devices, including Windows Mobile, Ericsson and Nokia phones. With Z-Push any groupware can be connected and synced with these devices.
-
-License: GNU Affero Genaral Public License v3.0 (AGPLv3)
-
-
-Documentation
-=============
-You can find some configuration guidelines in the Wiki https://github.com/fmbiete/Z-Push-contrib/wiki
-
-Requisites
-==========
-- PHP 5.x (5.3 it's the minimum supported) using PHP-FPM or MOD_PHP
-- HHVM 3.6 or newer, instead of PHP
-- NGINX or APACHE
-
-Configuration
-=============
-
-NGINX, 1.4 at least or you will need to enable chunkin mode (Use google for Apache configuration)
-
- server {
- listen 443;
- server_name zpush.domain.com;
-
- ssl on;
- ssl_certificate /etc/ssl/certs/zpush.pem;
- ssl_certificate_key /etc/ssl/private/zpush.key;
-
- root /usr/share/www/z-push-contrib;
- index index.php;
-
- error_log /var/log/nginx/zpush-error.log;
- access_log /var/log/nginx/zpush-access.log;
-
- location / {
- try_files $uri $uri/ index.php;
- }
-
- location /Microsoft-Server-ActiveSync {
- rewrite ^(.*)$ /index.php last;
- }
-
- location ~ .php$ {
- include /etc/nginx/fastcgi_params;
- fastcgi_index index.php;
- fastcgi_param HTTPS on;
- fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
- fastcgi_pass unix:/var/run/php5-fpm.sock;
- # Z-Push Ping command will be alive for 470s, but be safe
- fastcgi_read_timeout 630;
- }
- }
-
-PHP-FPM
-
- max_execution_time=600
- short_open_tag=On
-
-And configure enough php-fpm processes, as a rough estimation you will need 1.5 x number users.
-
-
-Backends
-========
-Each backend has a README file, or comments in their config.php file. Look at them to configure correctly. Also you can look here https://github.com/fmbiete/Z-Push-contrib/wiki
-
-
-StateMachine
-============
-You have 2 StateMachine methods.
-
-- FILE - FileStateMachine : will store state info into files. You will use it in Active-Pasive setups
-- SQL - SqlStateMachine: will store state info into a database. It uses PHP-PDO, so you will need to install the required packages for your database flavour and create the database. You will use it in Active-Active setups.
-
-
-User-Device Permissions
-=======================
-Disabled by default, when enabled will limit what users and device can sync against your Z-Push installation.
-It can auto-accept users, users and device until a maximum number of devices is reached.
-
-If using with FileStateMachine, edit the file STATE_DIR/PreAuthUserDevices to modificate the behaivour. That file is JSON formatted and it's filled each time a new user connect.
-
-If using with SqlStateMachine, look at the zpush_preauth_users table.
-
-
-
-
-Links
-=====
-Microsoft ActiveSync Specification
-http://msdn.microsoft.com/en-us/library/cc425499%28v=exchg.80%29.aspx
diff --git a/sources/autodiscover/INSTALL b/sources/autodiscover/INSTALL
deleted file mode 100644
index 1128816..0000000
--- a/sources/autodiscover/INSTALL
+++ /dev/null
@@ -1,177 +0,0 @@
-Z-Push AutoDiscover manual
---------------------------
-This manual gives an introduction to the Z-Push AutoDiscover service, discusses
-technical details and explains the installation.
-
-Introduction
-------------
-AutoDiscover is the service used to simplify the configuration of collaboration
-accounts for clients, especially for mobile phones.
-While in the past the user was required to enter the server name, user name and
-password manually into his mobile phone in order to connect, with AutoDiscover
-the user is only required to fill in his email address and the password.
-AutoDiscover will try several methods to reach the correct server automatically.
-
-
-How does it work?
------------------
-When speaking about AutoDiscover, this includes two distinct realms:
-- AutoDiscover is a specification which defines the steps a client should take
- in order to contact a service to request additional data.
-- The AutoDiscover service is piece of software which accepts requests from the
- clients, authenticates them, requests some additional data from the
- collaboration server and sends this data back to the client.
-The specification suggests several ways for client to contact the responsible
-server to receive additional information. Tests have shown, that basically all
-mobile phones tested support only the most basic ways. These are sufficient for
-almost all types of scenarios and are the ones implemented by Z-Push AutoDiscover.
-Please refer to the Mobile Compatibility List (http://z-push.sf.net/compatibility)
-for an overview of supported and tested devices.
-The used email address is the key for the process. The client splits it up into
-the local and domain part (before and after the @-sign). The client then tries
-to connect to this domain in order to get in contact with the AutoDiscover
-service. The local part of the email address is used as "login" to the
-AutoDiscover service. There is also an option, to use the full email address as
-login name (see "Configuration" section below for details).
-
-
- ---------------
- | Client |
- | e.g. mobile |
- ---------------
- / \
- 1. Searches for / \ 2. Data access
- information / \
- / \
- V V
- ---------------- --------------
- | AutoDiscover | redirects to | Z-Push |
- | | --------------------> | ActiveSync |
- ---------------- --------------
- \ /
- Authen- \ / Synchronizes
- ticates \ /
- via Z-Push \ /
- Backend V V
- -----------------
- | Collaboration |
- | Platform |
- -----------------
-
-Requirements
-------------
-As described in the previous chapter, the local part of the email address or
-the email address is used in order to log in.
-Your configuration requires that this type of login is possible:
-- either the user name is used to login and must be used in the email address
- entered on the mobile, or
-- the entire email address is used to login.
-
-Which option is used has to be configured in the AutoDiscover configuration and
-in the underlying platform (e.g. ZCP (hosting mode)).
-Most companies use the user name as local part of the email by default. From the
-AutoDiscover point of view, it is not required that user is able to receive
-emails at the used email address. It is recommended allowing that in order not
-to confuse end users.
-
-AutoDiscover also requires a valid SSL certificate to work as expected. A very
-little percentage of mobiles support self-signed certificates (showing a
-pop-up alerting the user). Most mobiles silently ignore self-signed certificates
-and just declare the AutoDiscover process as failed in such cases.
-If AutoDiscover fails, the user is generally redirected to the
-"manual configuration" of the client.
-
-If you do not plan to acquire an official certificate, you will probably not be
-able to use the AutoDiscover service.
-Depending on your setup, it could be necessary to add new DNS entries for your
-mail domain.
-
-
-Domain setup
-------------
-There are two general ways the AutoDiscover process can be configured:
-1. Directly with "yourdomain.com" website ("www.yourdomain.com" will most
- probably not work)
-2. With the sub-domain "autodiscover.yourdomain.com"
-In both cases, an official SSL certificate is required. If you already have a
-certificate for your domain, the webserver answering for that domain could be
-reconfigured to allow AutoDiscover requests as well. In the case that you do
-not have direct access to this type of configuration (e.g. hosting provider),
-it's recommended to acquire a dedicated certificate for
-"autodiscover.yourdomain.com". Please note, that this sub-domain can NOT be
-renamed. In general, "wildcard" certificates can be used, as long they are
-valid for the required domain.
-
-
-Software requirements
----------------------
-Like Z-Push, AutoDiscover is written in PHP, where PHP 5.3 or newer is required.
-Please consult the Z-Push INSTALL file for further information about PHP versions.
-If only AutoDiscover is to be executed on a host, the Z-Push PHP dependencies do
-NOT need to be installed.
-AutoDiscover has one direct dependency, the php-xml parser library.
-
-These packages vary in names between the distributions.
-- Generally install the packages: php-xml
-- On Suse (SLES & OpenSuse) install the packages: php53-xml
-- On RHEL based systems install the package: php-xml
-
-
-Installation
-------------
-AutoDiscover is part of the Z-Push package and uses some of the functionality
-available in Z-Push.
-It is possible to install AutoDiscover on the same host as Z-Push, or to
-install them on different hosts.
-
-Currently, independently from the setup, it's recommended to extract the entire
-z-push tarball and configure the services as required.
-Please follow the install instructions from the Z-Push INSTALL file (section
-"How to install") to copy the files to your server.
-If you do not want to setup Z-Push on the host, do not add the "Alias" for
-ActiveSync.
-
-To setup the SSL certificate, please refer to one of the many setup guides
-available on the internet, like that one:
-http://www.apache.com/resources/how-to-setup-an-ssl-certificate-on-apache/
-
-The mobiles requests these URLs (where "yourdomain.com" corresponds to the
-domain part of the email used in the client):
- https://yourdomain.com/Autodiscover/Autodiscover.xml and/or
- https://autodiscover.yourdomain.com/Autodiscover/Autodiscover.xml
-
-Add the following line to the apache site configuration file.
- AliasMatch (?i)/Autodiscover/Autodiscover.xml$ "/usr/share/z-push/autodiscover/autodiscover.php"
-
-This line assumes that Z-Push is installed in /usr/share/z-push. If the path
-is different, please adjust it accordingly.
-
-Note: some mobiles use different casings, like "AutoDiscover" in the URL. The
-above statement is valid for these as well.
-
-Please restart Apache afterwards.
-
-
-Configuration
--------------
-You don't need extra configuration for the AutoDiscover Service. It will use
-the configuration already defined for the main Z-Push Service.
-
-
-Test installation
------------------
-If everything is correct, accessing with a browser the URL for your setup, you
-should see:
- 1. a pop-up asking for your username + password. Always use the email
- address which you would also enter on the mobile (independently from
- the configuration).
- 2. if the authentication was successful, you will see a Z-Push informational
- page (like when accessing the Z-Push location).
-
-Note: The same test can also be performed in the mobiles web browser to check
-if the access works correctly from the mobile network.
-
-If the authentication fails, please check the configuration options of AutoDiscover.
-Also check the logfiles for possible failures.
-
-If the manual method works, try setting up your mobile phone! :)
diff --git a/sources/autodiscover/autodiscover.php b/sources/autodiscover/autodiscover.php
deleted file mode 100644
index a22cb71..0000000
--- a/sources/autodiscover/autodiscover.php
+++ /dev/null
@@ -1,256 +0,0 @@
-.
-*
-* Consult LICENSE file for details
-************************************************/
-
-require_once '../vendor/autoload.php';
-require_once '../config.php';
-
-class ZPushAutodiscover {
- const ACCEPTABLERESPONSESCHEMA = 'http://schemas.microsoft.com/exchange/autodiscover/mobilesync/responseschema/2006';
- const MAXINPUTSIZE = 8192; // Bytes, the autodiscover request shouldn't exceed that value
-
- private static $instance;
-
- /**
- * Static method to start the autodiscover process.
- *
- * @access public
- *
- * @return void
- */
- public static function DoZPushAutodiscover() {
- ZLog::Write(LOGLEVEL_DEBUG, '-------- Start ZPushAutodiscover');
- // TODO use filterevilinput?
- if (stripos($_SERVER["REQUEST_METHOD"], "GET") !== false) {
- ZLog::Write(LOGLEVEL_WARN, "GET request for autodiscover. Exiting.");
- if (!headers_sent()) {
- ZPush::PrintZPushLegal('GET not supported');
- }
- ZLog::Write(LOGLEVEL_DEBUG, '-------- End ZPushAutodiscover');
- exit(1);
- }
- if (!isset(self::$instance)) {
- self::$instance = new ZPushAutodiscover();
- }
- self::$instance->DoAutodiscover();
- ZLog::Write(LOGLEVEL_DEBUG, '-------- End ZPushAutodiscover');
- }
-
- /**
- * Does the complete autodiscover.
- * @access public
- * @throws AuthenticationRequiredException if login to the backend failed.
- * @throws ZPushException if the incoming XML is invalid..
- *
- * @return void
- */
- public function DoAutodiscover() {
- if (!defined('REAL_BASE_PATH')) {
- define('REAL_BASE_PATH', str_replace('autodiscover/', '', BASE_PATH));
- }
- set_include_path(get_include_path() . PATH_SEPARATOR . REAL_BASE_PATH);
- $response = "";
-
- try {
- $incomingXml = $this->getIncomingXml();
- $backend = ZPush::GetBackend();
- $username = $this->login($backend, $incomingXml);
- $userDetails = $backend->GetUserDetails($username);
- $email = ($this->getAttribFromUserDetails($userDetails, 'emailaddress')) ? $this->getAttribFromUserDetails($userDetails, 'emailaddress') : $incomingXml->Request->EMailAddress;
- $userFullname = ($this->getAttribFromUserDetails($userDetails, 'fullname')) ? $this->getAttribFromUserDetails($userDetails, 'fullname') : $email;
- ZLog::Write(LOGLEVEL_WBXML, sprintf("Resolved user's '%s' fullname to '%s'", $username, $userFullname));
- $response = $this->createResponse($email, $userFullname);
- setcookie("membername", $username);
- }
-
- catch (AuthenticationRequiredException $ex) {
- if (isset($incomingXml)) {
- ZLog::Write(LOGLEVEL_ERROR, sprintf("Unable to complete autodiscover because login failed for user with email '%s'", $incomingXml->Request->EMailAddress));
- }
- else {
- ZLog::Write(LOGLEVEL_ERROR, sprintf("Unable to complete autodiscover incorrect request: '%s'", $ex->getMessage()));
- }
- http_response_code(401);
- header('WWW-Authenticate: Basic realm="ZPush"');
- }
- catch (ZPushException $ex) {
- ZLog::Write(LOGLEVEL_ERROR, sprintf("Unable to complete autodiscover because of ZPushException. Error: %s", $ex->getMessage()));
- if(!headers_sent()) {
- header('HTTP/1.1 '. $ex->getHTTPCodeString());
- foreach ($ex->getHTTPHeaders() as $h) {
- header($h);
- }
- }
- }
- $this->sendResponse($response);
- }
-
- /**
- * Processes the incoming XML request and parses it to a SimpleXMLElement.
- *
- * @access private
- * @throws ZPushException if the XML is invalid.
- * @throws AuthenticationRequiredException if no login data was sent.
- *
- * @return SimpleXMLElement
- */
- private function getIncomingXml() {
- if ($_SERVER['CONTENT_LENGTH'] > ZPushAutodiscover::MAXINPUTSIZE) {
- throw new ZPushException('The request input size exceeds 8kb.');
- }
-
- if (!isset($_SERVER['PHP_AUTH_USER']) || !isset($_SERVER['PHP_AUTH_PW'])) {
- throw new AuthenticationRequiredException();
- }
-
- $input = @file_get_contents('php://input');
- $xml = simplexml_load_string($input);
-
- if (LOGLEVEL >= LOGLEVEL_WBXML) {
- ZLog::Write(LOGLEVEL_WBXML, sprintf("ZPushAutodiscover->getIncomingXml() incoming XML data:%s%s", PHP_EOL, $xml->asXML()));
- }
-
- if (!isset($xml->Request->EMailAddress)) {
- throw new FatalException('Invalid input XML: no email address.');
- }
-
- if (Utils::GetLocalPartFromEmail($xml->Request->EMailAddress) != Utils::GetLocalPartFromEmail($_SERVER['PHP_AUTH_USER'])) {
- ZLog::Write(LOGLEVEL_WARN, sprintf("The local part of the server auth user is different from the local part in the XML request ('%s' != '%s')",
- Utils::GetLocalPartFromEmail($xml->Request->EMailAddress), Utils::GetLocalPartFromEmail($_SERVER['PHP_AUTH_USER'])));
- }
-
- if (!isset($xml->Request->AcceptableResponseSchema)) {
- throw new FatalException('Invalid input XML: no AcceptableResponseSchema.');
- }
-
- if ($xml->Request->AcceptableResponseSchema != ZPushAutodiscover::ACCEPTABLERESPONSESCHEMA) {
- throw new FatalException('Invalid input XML: not a mobilesync responseschema.');
- }
-
- return $xml;
- }
-
- /**
- * Logins using the backend's Logon function.
- *
- * @param IBackend $backend
- * @param String $incomingXml
- * @access private
- * @throws AuthenticationRequiredException if no login data was sent.
- *
- * @return string $username
- */
- private function login($backend, $incomingXml) {
- // Determine the login name depending on the configuration: complete email address or
- // the local part only.
- if (USE_FULLEMAIL_FOR_LOGIN) {
- ZLog::Write(LOGLEVEL_DEBUG, sprintf("Using the complete email address for login."));
- $username = $incomingXml->Request->EMailAddress;
- }
- else{
- ZLog::Write(LOGLEVEL_DEBUG, sprintf("Using the username only for login."));
- $username = Utils::GetLocalPartFromEmail($incomingXml->Request->EMailAddress);
- }
-
- if($backend->Logon($username, "", $_SERVER['PHP_AUTH_PW']) == false) {
- throw new AuthenticationRequiredException("Access denied. Username or password incorrect.");
- }
- ZLog::Write(LOGLEVEL_DEBUG, sprintf("ZPushAutodiscover->login() Using '%s' as the username.", $username));
- return $username;
- }
-
- /**
- * Creates the XML response.
- *
- * @param string $email
- * @param string $userFullname
- * @access private
- *
- * @return string
- */
- private function createResponse($email, $userFullname) {
- $server_url = 'https://' . $_SERVER['SERVER_NAME'] . '/Microsoft-Server-ActiveSync';
- $xml = file_get_contents('response.xml');
- $response = new SimpleXMLElement($xml);
- $response->Response->User->DisplayName = $userFullname;
- $response->Response->User->EMailAddress = $email;
- $response->Response->Action->Settings->Server->Url = $server_url;
- $response->Response->Action->Settings->Server->Name = $server_url;
- $response = $response->asXML();
- ZLog::Write(LOGLEVEL_WBXML, sprintf("ZPushAutodiscover->createResponse() XML response:%s%s", PHP_EOL, $response));
- return $response;
- }
-
- /**
- * Sends the response to the device.
- * @param string $response
- * @access private
- *
- * @return void
- */
- private function sendResponse($response) {
- ZLog::Write(LOGLEVEL_DEBUG, "ZPushAutodiscover->sendResponse() sending response...");
- header("Content-type: text/html");
- $output = fopen("php://output", "w+");
- fwrite($output, $response);
- fclose($output);
- ZLog::Write(LOGLEVEL_DEBUG, "ZPushAutodiscover->sendResponse() response sent.");
- }
-
- /**
- * Gets an attribute from user details.
- * @param Array $userDetails
- * @param String $attrib
- * @access private
- *
- * @return String or false on error.
- */
- private function getAttribFromUserDetails($userDetails, $attrib) {
- if (isset($userDetails[$attrib]) && $userDetails[$attrib]) {
- return $userDetails[$attrib];
- }
- ZLog::Write(LOGLEVEL_WARN, sprintf("The backend was not able to find attribute '%s' of the user. Fall back to the default value.", $attrib));
- return false;
- }
-}
-
-ZPushAutodiscover::DoZPushAutodiscover();
diff --git a/sources/autodiscover/response.xml b/sources/autodiscover/response.xml
deleted file mode 100644
index 9fa473d..0000000
--- a/sources/autodiscover/response.xml
+++ /dev/null
@@ -1,19 +0,0 @@
-
-
-
-en:us
-
-
-
-
-
-
-
-MobileSync
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/sources/backend/caldav/AUTHOR b/sources/backend/caldav/AUTHOR
deleted file mode 100644
index af8002c..0000000
--- a/sources/backend/caldav/AUTHOR
+++ /dev/null
@@ -1,3 +0,0 @@
-The Author of this backend is dupondje, I could have modified it.
-You can found the original code here:
-https://github.com/dupondje/PHP-Push-2
diff --git a/sources/backend/caldav/REQUIREMENTS b/sources/backend/caldav/REQUIREMENTS
deleted file mode 100644
index cd00f71..0000000
--- a/sources/backend/caldav/REQUIREMENTS
+++ /dev/null
@@ -1,8 +0,0 @@
-REQUIREMENTS:
-php-curl
-libawl-php
-
-INSTALL:
-Add your awl folder to the include_path variable (/etc/php.ini or similar)
-
-CalDAV server (DAViCal, Sabredav, Sogo, Owncloud...)
diff --git a/sources/backend/caldav/caldav.php b/sources/backend/caldav/caldav.php
deleted file mode 100644
index 811c798..0000000
--- a/sources/backend/caldav/caldav.php
+++ /dev/null
@@ -1,1528 +0,0 @@
-.
-*
-* Consult LICENSE file for details
-************************************************/
-
-// config file
-require_once("backend/caldav/config.php");
-
-class BackendCalDAV extends BackendDiff {
- /**
- * @var CalDAVClient
- */
- private $_caldav;
- private $_caldav_path;
- private $_collection = array();
-
- private $changessinkinit;
- private $sinkdata;
- private $sinkmax;
-
- /**
- * Constructor
- */
- public function BackendCalDAV() {
- if (!function_exists("curl_init")) {
- throw new FatalException("BackendCalDAV(): php-curl is not found", 0, null, LOGLEVEL_FATAL);
- }
-
- $this->changessinkinit = false;
- $this->sinkdata = array();
- $this->sinkmax = array();
- }
-
- /**
- * Login to the CalDAV backend
- * @see IBackend::Logon()
- */
- public function Logon($username, $domain, $password) {
- $this->_caldav_path = str_replace('%u', $username, CALDAV_PATH);
- $url = sprintf("%s://%s:%d%s", CALDAV_PROTOCOL, CALDAV_SERVER, CALDAV_PORT, $this->_caldav_path);
- $this->_caldav = new CalDAVClient($url, $username, $password);
- if ($connected = $this->_caldav->CheckConnection()) {
- ZLog::Write(LOGLEVEL_DEBUG, sprintf("BackendCalDAV->Logon(): User '%s' is authenticated on CalDAV '%s'", $username, $url));
- }
- else {
- ZLog::Write(LOGLEVEL_WARN, sprintf("BackendCalDAV->Logon(): User '%s' is not authenticated on CalDAV '%s'", $username, $url));
- }
-
- return $connected;
- }
-
- /**
- * The connections to CalDAV are always directly closed. So nothing special needs to happen here.
- * @see IBackend::Logoff()
- */
- public function Logoff() {
- if ($this->_caldav != null) {
- $this->_caldav->Disconnect();
- unset($this->_caldav);
- }
-
- $this->SaveStorages();
-
- unset($this->sinkdata);
- unset($this->sinkmax);
-
- ZLog::Write(LOGLEVEL_DEBUG, "BackendCalDAV->Logoff(): disconnected from CALDAV server");
-
- return true;
- }
-
- /**
- * CalDAV doesn't need to handle SendMail
- * @see IBackend::SendMail()
- */
- public function SendMail($sm) {
- return false;
- }
-
- /**
- * No attachments in CalDAV
- * @see IBackend::GetAttachmentData()
- */
- public function GetAttachmentData($attname) {
- return false;
- }
-
- /**
- * Deletes are always permanent deletes. Messages doesn't get moved.
- * @see IBackend::GetWasteBasket()
- */
- public function GetWasteBasket() {
- return false;
- }
-
- /**
- * Get a list of all the folders we are going to sync.
- * Each caldav calendar can contain tasks (prefix T) and events (prefix C), so duplicate each calendar found.
- * @see BackendDiff::GetFolderList()
- */
- public function GetFolderList() {
- ZLog::Write(LOGLEVEL_DEBUG, sprintf("BackendCalDAV->GetFolderList(): Getting all folders."));
- $folders = array();
- $calendars = $this->_caldav->FindCalendars();
- foreach ($calendars as $val) {
- $folder = array();
- $fpath = explode("/", $val->url, -1);
- if (is_array($fpath)) {
- $folderid = array_pop($fpath);
- $id = "C" . $folderid;
- $folders[] = $this->StatFolder($id);
- $id = "T" . $folderid;
- $folders[] = $this->StatFolder($id);
- }
- }
- return $folders;
- }
-
- /**
- * Returning a SyncFolder
- * @see BackendDiff::GetFolder()
- */
- public function GetFolder($id) {
- ZLog::Write(LOGLEVEL_DEBUG, sprintf("BackendCalDAV->GetFolder('%s')", $id));
- $val = $this->_caldav->GetCalendarDetails($this->_caldav_path . substr($id, 1) . "/");
- $folder = new SyncFolder();
- $folder->parentid = "0";
- $folder->displayname = $val->displayname;
- $folder->serverid = $id;
- if ($id[0] == "C") {
- if (defined('CALDAV_PERSONAL') && strtolower(substr($id, 1)) == CALDAV_PERSONAL) {
- $folder->type = SYNC_FOLDER_TYPE_APPOINTMENT;
- }
- else {
- $folder->type = SYNC_FOLDER_TYPE_USER_APPOINTMENT;
- }
- }
- else {
- if (defined('CALDAV_PERSONAL') && strtolower(substr($id, 1)) == CALDAV_PERSONAL) {
- $folder->type = SYNC_FOLDER_TYPE_TASK;
- }
- else {
- $folder->type = SYNC_FOLDER_TYPE_USER_TASK;
- }
- }
- return $folder;
- }
-
- /**
- * Returns information on the folder.
- * @see BackendDiff::StatFolder()
- */
- public function StatFolder($id) {
- ZLog::Write(LOGLEVEL_DEBUG, sprintf("BackendCalDAV->StatFolder('%s')", $id));
- $val = $this->GetFolder($id);
- $folder = array();
- $folder["id"] = $id;
- $folder["parent"] = $val->parentid;
- $folder["mod"] = $val->serverid;
- return $folder;
- }
-
- /**
- * ChangeFolder is not supported under CalDAV
- * @see BackendDiff::ChangeFolder()
- */
- public function ChangeFolder($folderid, $oldid, $displayname, $type) {
- ZLog::Write(LOGLEVEL_DEBUG, sprintf("BackendCalDAV->ChangeFolder('%s','%s','%s','%s')", $folderid, $oldid, $displayname, $type));
- return false;
- }
-
- /**
- * DeleteFolder is not supported under CalDAV
- * @see BackendDiff::DeleteFolder()
- */
- public function DeleteFolder($id, $parentid) {
- ZLog::Write(LOGLEVEL_DEBUG, sprintf("BackendCalDAV->DeleteFolder('%s','%s')", $id, $parentid));
- return false;
- }
-
- /**
- * Get a list of all the messages.
- * @see BackendDiff::GetMessageList()
- */
- public function GetMessageList($folderid, $cutoffdate) {
- ZLog::Write(LOGLEVEL_DEBUG, sprintf("BackendCalDAV->GetMessageList('%s','%s')", $folderid, $cutoffdate));
-
- /* Calculating the range of events we want to sync */
- $begin = gmdate("Ymd\THis\Z", $cutoffdate);
- $finish = gmdate("Ymd\THis\Z", CALDAV_MAX_SYNC_PERIOD);
-
- $path = $this->_caldav_path . substr($folderid, 1) . "/";
- if ($folderid[0] == "C") {
- $msgs = $this->_caldav->GetEvents($begin, $finish, $path);
- }
- else {
- $msgs = $this->_caldav->GetTodos($begin, $finish, false, false, $path);
- }
-
- $messages = array();
- foreach ($msgs as $e) {
- $id = $e['href'];
- $this->_collection[$id] = $e;
- $messages[] = $this->StatMessage($folderid, $id);
- }
- return $messages;
- }
-
- /**
- * Get a SyncObject by its ID
- * @see BackendDiff::GetMessage()
- */
- public function GetMessage($folderid, $id, $contentparameters) {
- ZLog::Write(LOGLEVEL_DEBUG, sprintf("BackendCalDAV->GetMessage('%s','%s')", $folderid, $id));
- $data = $this->_collection[$id]['data'];
-
- if ($folderid[0] == "C") {
- return $this->_ParseVEventToAS($data, $contentparameters);
- }
- if ($folderid[0] == "T") {
- return $this->_ParseVTodoToAS($data, $contentparameters);
- }
- return false;
- }
-
- /**
- * Return id, flags and mod of a messageid
- * @see BackendDiff::StatMessage()
- */
- public function StatMessage($folderid, $id) {
- ZLog::Write(LOGLEVEL_DEBUG, sprintf("BackendCalDAV->StatMessage('%s','%s')", $folderid, $id));
- $type = "VEVENT";
- if ($folderid[0] == "T") {
- $type = "VTODO";
- }
- $data = null;
- if (array_key_exists($id, $this->_collection)) {
- $data = $this->_collection[$id];
- }
- else {
- $path = $this->_caldav_path . substr($folderid, 1) . "/";
- $e = $this->_caldav->GetEntryByUid(substr($id, 0, strlen($id)-4), $path, $type);
- if ($e == null && count($e) <= 0)
- return;
- $data = $e[0];
- }
- $message = array();
- $message['id'] = $data['href'];
- $message['flags'] = "1";
- $message['mod'] = $data['etag'];
- return $message;
- }
-
- /**
- * Change/Add a message with contents received from ActiveSync
- * @see BackendDiff::ChangeMessage()
- */
- public function ChangeMessage($folderid, $id, $message, $contentParameters) {
- ZLog::Write(LOGLEVEL_DEBUG, sprintf("BackendCalDAV->ChangeMessage('%s','%s')", $folderid, $id));
-
- if ($id) {
- $mod = $this->StatMessage($folderid, $id);
- $etag = $mod['mod'];
- }
- else {
- $etag = "*";
- $id = sprintf("%s-%s.ics", gmdate("Ymd\THis\Z"), hash("md5", microtime()));
- }
-
- $url = $this->_caldav_path . substr($folderid, 1) . "/" . $id;
-
- $data = $this->_ParseASToVCalendar($message, $folderid, substr($id, 0, strlen($id) - 4));
-
- $etag_new = $this->CreateUpdateCalendar($data, $url, $etag);
-
- $item = array();
- $item['href'] = $id;
- $item['etag'] = $etag_new;
- $item['data'] = $data;
- $this->_collection[$id] = $item;
-
- return $this->StatMessage($folderid, $id);
- }
-
- /**
- * Change the read flag is not supported.
- * @see BackendDiff::SetReadFlag()
- */
- public function SetReadFlag($folderid, $id, $flags, $contentParameters) {
- return false;
- }
-
- /**
- * Delete a message from the CalDAV server.
- * @see BackendDiff::DeleteMessage()
- */
- public function DeleteMessage($folderid, $id, $contentParameters) {
- ZLog::Write(LOGLEVEL_DEBUG, sprintf("BackendCalDAV->DeleteMessage('%s','%s')", $folderid, $id));
- $url = $this->_caldav_path . substr($folderid, 1) . "/" . $id;
- $http_status_code = $this->_caldav->DoDELETERequest($url);
- return $http_status_code == "204";
- }
-
- /**
- * Move a message is not supported by CalDAV.
- * @see BackendDiff::MoveMessage()
- */
- public function MoveMessage($folderid, $id, $newfolderid, $contentParameters) {
- return false;
- }
-
- /**
- * Create or Update one event
- *
- * @access public
- * @param $data string VCALENDAR text
- * @param $url string URL for the calendar, if false a new calendar object is created
- * @param $etag string ETAG for the calendar, if '*' is a new object
- * @return array
- */
- public function CreateUpdateCalendar($data, $url = false, $etag = "*") {
- if ($url === false) {
- $url = sprintf("%s%s/%s-%s.ics", $this->_caldav_path, CALDAV_PERSONAL, gmdate("Ymd\THis\Z"), hash("md5", microtime()));
- $etag = "*";
- }
-
- return $this->_caldav->DoPUTRequest($url, $data, $etag);
- }
-
- /**
- * Deletes one VCALENDAR
- *
- * @access public
- * @param $id string ID of the VCALENDAR
- * @return boolean
- */
- public function DeleteCalendar($id) {
- $http_status_code = $this->_caldav->DoDELETERequest(sprintf("%s%s/%s", $this->_caldav_path, CALDAV_PERSONAL, $id));
- return $http_status_code == "204";
- }
-
- /**
- * Finds one VCALENDAR
- *
- * @access public
- * @param $uid string UID attribute
- * @return array
- */
- public function FindCalendar($uid) {
- $filter = sprintf("%s", $uid);
-
- $events = $this->_caldav->DoCalendarQuery($filter, sprintf("%s%s", $this->_caldav_path, CALDAV_PERSONAL));
-
- return $events;
- }
-
- /**
- * Resolves recipients
- *
- * @param SyncObject $resolveRecipients
- *
- * @access public
- * @return SyncObject $resolveRecipients
- */
- public function ResolveRecipients($resolveRecipients) {
- // TODO:
- return false;
- }
-
- /**
- * Indicates which AS version is supported by the backend.
- *
- * @access public
- * @return string AS version constant
- */
- public function GetSupportedASVersion() {
- return ZPush::ASV_14;
- }
-
- /**
- * Indicates if the backend has a ChangesSink.
- * A sink is an active notification mechanism which does not need polling.
- * The CalDAV backend simulates a sink by polling revision dates from the events or use the native sync-collection.
- *
- * @access public
- * @return boolean
- */
- public function HasChangesSink() {
- return true;
- }
-
- /**
- * The folder should be considered by the sink.
- * Folders which were not initialized should not result in a notification
- * of IBackend->ChangesSink().
- *
- * @param string $folderid
- *
- * @access public
- * @return boolean false if found can not be found
- */
- public function ChangesSinkInitialize($folderid) {
- ZLog::Write(LOGLEVEL_DEBUG, sprintf("BackendCalDAV->ChangesSinkInitialize(): folderid '%s'", $folderid));
-
- // We don't need the actual events, we only need to get the changes since this moment
- $init_ok = true;
- $url = $this->_caldav_path . substr($folderid, 1) . "/";
- $this->sinkdata[$folderid] = $this->_caldav->GetSync($url, true, CALDAV_SUPPORTS_SYNC);
- if (CALDAV_SUPPORTS_SYNC) {
- // we don't need to store the sinkdata if the caldav server supports native sync
- unset($this->sinkdata[$url]);
- $this->sinkdata[$folderid] = array();
- }
-
- $this->changessinkinit = $init_ok;
- $this->sinkmax = array();
-
- return $this->changessinkinit;
- }
-
- /**
- * The actual ChangesSink.
- * For max. the $timeout value this method should block and if no changes
- * are available return an empty array.
- * If changes are available a list of folderids is expected.
- *
- * @param int $timeout max. amount of seconds to block
- *
- * @access public
- * @return array
- */
- public function ChangesSink($timeout = 30) {
- $notifications = array();
- $stopat = time() + $timeout - 1;
-
- //We can get here and the ChangesSink not be initialized yet
- if (!$this->changessinkinit) {
- ZLog::Write(LOGLEVEL_DEBUG, sprintf("BackendCalDAV->ChangesSink - Not initialized ChangesSink, sleep and exit"));
- // We sleep and do nothing else
- sleep($timeout);
- return $notifications;
- }
-
- // only check once to reduce pressure in the DAV server
- foreach ($this->sinkdata as $k => $v) {
- $changed = false;
-
- $url = $this->_caldav_path . substr($k, 1) . "/";
- $response = $this->_caldav->GetSync($url, false, CALDAV_SUPPORTS_SYNC);
-
- if (CALDAV_SUPPORTS_SYNC) {
- if (count($response) > 0) {
- $changed = true;
- ZLog::Write(LOGLEVEL_DEBUG, sprintf("BackendCalDAV->ChangesSink - Changes detected"));
- }
- }
- else {
- // If the numbers of events are different, we know for sure, there are changes
- if (count($response) != count($v)) {
- $changed = true;
- ZLog::Write(LOGLEVEL_DEBUG, sprintf("BackendCalDAV->ChangesSink - Changes detected"));
- }
- else {
- // If the numbers of events are equals, we compare the biggest date
- // FIXME: we are comparing strings no dates
- if (!isset($this->sinkmax[$k])) {
- $this->sinkmax[$k] = '';
- for ($i = 0; $i < count($v); $i++) {
- if ($v[$i]['getlastmodified'] > $this->sinkmax[$k]) {
- $this->sinkmax[$k] = $v[$i]['getlastmodified'];
- }
- }
- }
-
- for ($i = 0; $i < count($response); $i++) {
- if ($response[$i]['getlastmodified'] > $this->sinkmax[$k]) {
- $changed = true;
- }
- }
-
- if ($changed) {
- ZLog::Write(LOGLEVEL_DEBUG, sprintf("BackendCalDAV->ChangesSink - Changes detected"));
- }
- }
- }
-
- if ($changed) {
- $notifications[] = $k;
- }
- }
-
- // Wait to timeout
- if (empty($notifications)) {
- while ($stopat > time()) {
- sleep(1);
- }
- }
-
- return $notifications;
- }
-
-
- /**
- * Convert a iCAL VEvent to ActiveSync format
- * @param ical_vevent $data
- * @param ContentParameters $contentparameters
- * @return SyncAppointment
- */
- private function _ParseVEventToAS($data, $contentparameters) {
- ZLog::Write(LOGLEVEL_DEBUG, "BackendCalDAV->_ParseVEventToAS(): Parsing VEvent");
-
- $truncsize = Utils::GetTruncSize($contentparameters->GetTruncation());
- $message = new SyncAppointment();
-
- $ical = new iCalComponent($data);
- $timezones = $ical->GetComponents("VTIMEZONE");
- $timezone = "";
- if (count($timezones) > 0) {
- $timezone = Utils::ParseTimezone($timezones[0]->GetPValue("TZID"));
- }
- if (!$timezone) {
- $timezone = date_default_timezone_get();
- }
- $message->timezone = $this->_GetTimezoneString($timezone);
-
- $vevents = $ical->GetComponents("VTIMEZONE", false);
- foreach ($vevents as $event) {
- $rec = $event->GetProperties("RECURRENCE-ID");
- if (count($rec) > 0) {
- $recurrence_id = reset($rec);
- $exception = new SyncAppointmentException();
- $tzid = Utils::ParseTimezone($recurrence_id->GetParameterValue("TZID"));
- if (!$tzid) {
- $tzid = $timezone;
- }
- $exception->exceptionstarttime = Utils::MakeUTCDate($recurrence_id->Value(), $tzid);
- $exception->deleted = "0";
- $exception = $this->_ParseVEventToSyncObject($event, $exception, $truncsize);
- if (!isset($message->exceptions)) {
- $message->exceptions = array();
- }
- $message->exceptions[] = $exception;
- }
- else {
- $message = $this->_ParseVEventToSyncObject($event, $message, $truncsize);
- }
- }
- return $message;
- }
-
- /**
- * Parse 1 VEvent
- * @param ical_vevent $event
- * @param SyncAppointment(Exception) $message
- * @param int $truncsize
- */
- private function _ParseVEventToSyncObject($event, $message, $truncsize) {
- //Defaults
- $message->busystatus = "2";
-
- $properties = $event->GetProperties();
- foreach ($properties as $property) {
- switch ($property->Name()) {
- case "LAST-MODIFIED":
- $message->dtstamp = Utils::MakeUTCDate($property->Value());
- break;
-
- case "DTSTART":
- $message->starttime = Utils::MakeUTCDate($property->Value(), Utils::ParseTimezone($property->GetParameterValue("TZID")));
- if (strlen($property->Value()) == 8) {
- $message->alldayevent = "1";
- }
- break;
-
- case "SUMMARY":
- $message->subject = $property->Value();
- break;
-
- case "UID":
- $message->uid = $property->Value();
- break;
-
- case "ORGANIZER":
- $org_mail = str_ireplace("MAILTO:", "", $property->Value());
- $message->organizeremail = $org_mail;
- $org_cn = $property->GetParameterValue("CN");
- if ($org_cn) {
- $message->organizername = $org_cn;
- }
- break;
-
- case "LOCATION":
- $message->location = $property->Value();
- break;
-
- case "DTEND":
- $message->endtime = Utils::MakeUTCDate($property->Value(), Utils::ParseTimezone($property->GetParameterValue("TZID")));
- if (strlen($property->Value()) == 8) {
- $message->alldayevent = "1";
- }
- break;
-
- case "DURATION":
- if (!isset($message->endtime)) {
- $start = date_create("@" . $message->starttime);
- $val = str_replace("+", "", $property->Value());
- $interval = new DateInterval($val);
- $message->endtime = date_timestamp_get(date_add($start, $interval));
- }
- break;
-
- case "RRULE":
- $message->recurrence = $this->_ParseRecurrence($property->Value(), "vevent");
- break;
-
- case "CLASS":
- switch ($property->Value()) {
- case "PUBLIC":
- $message->sensitivity = "0";
- break;
- case "PRIVATE":
- $message->sensitivity = "2";
- break;
- case "CONFIDENTIAL":
- $message->sensitivity = "3";
- break;
- }
- break;
-
- case "TRANSP":
- switch ($property->Value()) {
- case "TRANSPARENT":
- $message->busystatus = "0";
- break;
- case "OPAQUE":
- $message->busystatus = "2";
- break;
- }
- break;
-
- // SYNC_POOMCAL_MEETINGSTATUS
- // Meetingstatus values
- // 0 = is not a meeting
- // 1 = is a meeting
- // 3 = Meeting received
- // 5 = Meeting is canceled
- // 7 = Meeting is canceled and received
- // 9 = as 1
- // 11 = as 3
- // 13 = as 5
- // 15 = as 7
- case "STATUS":
- switch ($property->Value()) {
- case "TENTATIVE":
- $message->meetingstatus = "3"; // was 1
- break;
- case "CONFIRMED":
- $message->meetingstatus = "1"; // was 3
- break;
- case "CANCELLED":
- $message->meetingstatus = "5"; // could also be 7
- break;
- }
- break;
-
- case "ATTENDEE":
- $attendee = new SyncAttendee();
- $att_email = str_ireplace("MAILTO:", "", $property->Value());
- $attendee->email = $att_email;
- $att_cn = $property->GetParameterValue("CN");
- if ($att_cn) {
- $attendee->name = $att_cn;
- }
- if (isset($message->attendees) && is_array($message->attendees)) {
- $message->attendees[] = $attendee;
- }
- else {
- $message->attendees = array($attendee);
- }
- break;
-
- case "DESCRIPTION":
- if (Request::GetProtocolVersion() >= 12.0) {
- $message->asbody = new SyncBaseBody();
- $message->asbody->data = str_replace("\n","\r\n", str_replace("\r","",Utils::ConvertHtmlToText($property->Value())));
- // truncate body, if requested
- if (strlen($message->asbody->data) > $truncsize) {
- $message->asbody->truncated = 1;
- $message->asbody->data = Utils::Utf8_truncate($message->asbody->data, $truncsize);
- }
- else {
- $message->asbody->truncated = 0;
- }
- $message->nativebodytype = SYNC_BODYPREFERENCE_PLAIN;
- }
- else {
- $body = $property->Value();
- // truncate body, if requested
- if(strlen($body) > $truncsize) {
- $body = Utils::Utf8_truncate($body, $truncsize);
- $message->bodytruncated = 1;
- } else {
- $message->bodytruncated = 0;
- }
- $body = str_replace("\n","\r\n", str_replace("\r","",$body));
- $message->body = $body;
- }
- break;
-
- case "CATEGORIES":
- $categories = explode(",", $property->Value());
- $message->categories = $categories;
- break;
-
- case "EXDATE":
- $exception = new SyncAppointmentException();
- $exception->deleted = "1";
- $exception->exceptionstarttime = Utils::MakeUTCDate($property->Value());
- if (!isset($message->exceptions)) {
- $message->exceptions = array();
- }
- $message->exceptions[] = $exception;
- break;
-
- //We can ignore the following
- case "PRIORITY":
- case "SEQUENCE":
- case "CREATED":
- case "DTSTAMP":
- case "X-MOZ-GENERATION":
- case "X-MOZ-LASTACK":
- case "X-LIC-ERROR":
- case "RECURRENCE-ID":
- break;
-
- default:
- ZLog::Write(LOGLEVEL_DEBUG, sprintf("BackendCalDAV->_ParseVEventToSyncObject(): '%s' is not yet supported.", $property->Name()));
- }
- }
-
- // Workaround #127 - No organizeremail defined
- if (!isset($message->organizeremail)) {
- ZLog::Write(LOGLEVEL_DEBUG, sprintf("BackendCalDAV->_ParseVEventToSyncObject(): No organizeremail defined, using username"));
- $message->organizeremail = $this->originalUsername;
- }
-
- $valarm = current($event->GetComponents("VALARM"));
- if ($valarm) {
- $properties = $valarm->GetProperties();
- foreach ($properties as $property) {
- if ($property->Name() == "TRIGGER") {
- $parameters = $property->Parameters();
- if (array_key_exists("VALUE", $parameters) && $parameters["VALUE"] == "DATE-TIME") {
- $trigger = date_create("@" . Utils::MakeUTCDate($property->Value()));
- $begin = date_create("@" . $message->starttime);
- $interval = date_diff($begin, $trigger);
- $message->reminder = $interval->format("%i") + $interval->format("%h") * 60 + $interval->format("%a") * 60 * 24;
- }
- elseif (!array_key_exists("VALUE", $parameters) || $parameters["VALUE"] == "DURATION") {
- $val = str_replace("-", "", $property->Value());
- $interval = new DateInterval($val);
- $message->reminder = $interval->format("%i") + $interval->format("%h") * 60 + $interval->format("%a") * 60 * 24;
- }
- }
- }
- }
-
- return $message;
- }
-
- /**
- * Parse a RRULE
- * @param string $rrulestr
- */
- private function _ParseRecurrence($rrulestr, $type) {
- $recurrence = new SyncRecurrence();
- if ($type == "vtodo") {
- $recurrence = new SyncTaskRecurrence();
- }
- $rrules = explode(";", $rrulestr);
- foreach ($rrules as $rrule) {
- $rule = explode("=", $rrule);
- switch ($rule[0]) {
- case "FREQ":
- switch ($rule[1]) {
- case "DAILY":
- $recurrence->type = "0";
- break;
- case "WEEKLY":
- $recurrence->type = "1";
- break;
- case "MONTHLY":
- $recurrence->type = "2";
- break;
- case "YEARLY":
- $recurrence->type = "5";
- }
- break;
-
- case "UNTIL":
- $recurrence->until = Utils::MakeUTCDate($rule[1]);
- break;
-
- case "COUNT":
- $recurrence->occurrences = $rule[1];
- break;
-
- case "INTERVAL":
- $recurrence->interval = $rule[1];
- break;
-
- case "BYDAY":
- $dval = 0;
- $days = explode(",", $rule[1]);
- foreach ($days as $day) {
- if ($recurrence->type == "2") {
- if (strlen($day) > 2) {
- $recurrence->weekofmonth = intval($day);
- $day = substr($day,-2);
- }
- else {
- $recurrence->weekofmonth = 1;
- }
- $recurrence->type = "3";
- }
- switch ($day) {
- // 1 = Sunday
- // 2 = Monday
- // 4 = Tuesday
- // 8 = Wednesday
- // 16 = Thursday
- // 32 = Friday
- // 62 = Weekdays // not in spec: daily weekday recurrence
- // 64 = Saturday
- case "SU":
- $dval += 1;
- break;
- case "MO":
- $dval += 2;
- break;
- case "TU":
- $dval += 4;
- break;
- case "WE":
- $dval += 8;
- break;
- case "TH":
- $dval += 16;
- break;
- case "FR":
- $dval += 32;
- break;
- case "SA":
- $dval += 64;
- break;
- }
- }
- $recurrence->dayofweek = $dval;
- break;
-
- //Only 1 BYMONTHDAY is supported, so BYMONTHDAY=2,3 will only include 2
- case "BYMONTHDAY":
- $days = explode(",", $rule[1]);
- $recurrence->dayofmonth = $days[0];
- break;
-
- case "BYMONTH":
- $recurrence->monthofyear = $rule[1];
- break;
-
- default:
- ZLog::Write(LOGLEVEL_DEBUG, sprintf("BackendCalDAV->_ParseRecurrence(): '%s' is not yet supported.", $rule[0]));
- }
- }
- return $recurrence;
- }
-
- /**
- * Generate a iCAL VCalendar from ActiveSync object.
- * @param string $data
- * @param string $folderid
- * @param string $id
- */
- private function _ParseASToVCalendar($data, $folderid, $id) {
- $ical = new iCalComponent();
- $ical->SetType("VCALENDAR");
- $ical->AddProperty("VERSION", "2.0");
- $ical->AddProperty("PRODID", "-//z-push-contrib//NONSGML Z-Push-contrib Calendar//EN");
- $ical->AddProperty("CALSCALE", "GREGORIAN");
-
- if ($folderid[0] == "C") {
- $vevent = $this->_ParseASEventToVEvent($data, $id);
- $vevent->AddProperty("UID", $id);
- $ical->AddComponent($vevent);
- if (isset($data->exceptions) && is_array($data->exceptions)) {
- foreach ($data->exceptions as $ex) {
- $exception = $this->_ParseASEventToVEvent($ex, $id);
- if ($data->alldayevent == 1) {
- $exception->AddProperty("RECURRENCE-ID", $this->_GetDateFromUTC("Ymd", $ex->exceptionstarttime, $data->timezone), array("VALUE" => "DATE"));
- }
- else {
- $exception->AddProperty("RECURRENCE-ID", gmdate("Ymd\THis\Z", $ex->exceptionstarttime));
- }
- $exception->AddProperty("UID", $id);
- $ical->AddComponent($exception);
- }
- }
- }
- if ($folderid[0] == "T") {
- $vtodo = $this->_ParseASTaskToVTodo($data, $id);
- $vtodo->AddProperty("UID", $id);
- $vtodo->AddProperty("DTSTAMP", gmdate("Ymd\THis\Z"));
- $ical->AddComponent($vtodo);
- }
-
- return $ical->Render();
- }
-
- /**
- * Generate a VEVENT from a SyncAppointment(Exception).
- * @param string $data
- * @param string $id
- * @return iCalComponent
- */
- private function _ParseASEventToVEvent($data, $id) {
- $vevent = new iCalComponent();
- $vevent->SetType("VEVENT");
-
- if (isset($data->dtstamp)) {
- $vevent->AddProperty("DTSTAMP", gmdate("Ymd\THis\Z", $data->dtstamp));
- $vevent->AddProperty("LAST-MODIFIED", gmdate("Ymd\THis\Z", $data->dtstamp));
- }
- if (isset($data->starttime)) {
- if ($data->alldayevent == 1) {
- $vevent->AddProperty("DTSTART", $this->_GetDateFromUTC("Ymd", $data->starttime, $data->timezone), array("VALUE" => "DATE"));
- }
- else {
- $vevent->AddProperty("DTSTART", gmdate("Ymd\THis\Z", $data->starttime));
- }
- }
- if (isset($data->subject)) {
- $vevent->AddProperty("SUMMARY", $data->subject);
- }
- if (isset($data->organizeremail)) {
- if (isset($data->organizername)) {
- $vevent->AddProperty("ORGANIZER", sprintf("MAILTO:%s", $data->organizeremail), array("CN" => $data->organizername));
- }
- else {
- $vevent->AddProperty("ORGANIZER", sprintf("MAILTO:%s", $data->organizeremail));
- }
- }
- if (isset($data->location)) {
- $vevent->AddProperty("LOCATION", $data->location);
- }
- if (isset($data->endtime)) {
- if ($data->alldayevent == 1) {
- $vevent->AddProperty("DTEND", $this->_GetDateFromUTC("Ymd", $data->endtime, $data->timezone), array("VALUE" => "DATE"));
- $vevent->AddProperty("X-MICROSOFT-CDO-ALLDAYEVENT", "TRUE");
- }
- else {
- $vevent->AddProperty("DTEND", gmdate("Ymd\THis\Z", $data->endtime));
- $vevent->AddProperty("X-MICROSOFT-CDO-ALLDAYEVENT", "FALSE");
- }
- }
- if (isset($data->recurrence)) {
- $vevent->AddProperty("RRULE", $this->_GenerateRecurrence($data->recurrence));
- }
- if (isset($data->sensitivity)) {
- switch ($data->sensitivity) {
- case "0":
- $vevent->AddProperty("CLASS", "PUBLIC");
- break;
- case "2":
- $vevent->AddProperty("CLASS", "PRIVATE");
- break;
- case "3":
- $vevent->AddProperty("CLASS", "CONFIDENTIAL");
- break;
- }
- }
- if (isset($data->busystatus)) {
- switch ($data->busystatus) {
- case "0":
- case "1":
- $vevent->AddProperty("TRANSP", "TRANSPARENT");
- break;
- case "2":
- case "3":
- $vevent->AddProperty("TRANSP", "OPAQUE");
- break;
- }
- }
- if (isset($data->reminder)) {
- $valarm = new iCalComponent();
- $valarm->SetType("VALARM");
- $valarm->AddProperty("ACTION", "DISPLAY");
- $trigger = "-PT" . $data->reminder . "M";
- $valarm->AddProperty("TRIGGER", $trigger);
- $vevent->AddComponent($valarm);
- }
- if (isset($data->rtf)) {
- $rtfparser = new rtf();
- $rtfparser->loadrtf(base64_decode($data->rtf));
- $rtfparser->output("ascii");
- $rtfparser->parse();
- $vevent->AddProperty("DESCRIPTION", $rtfparser->out);
- }
- if (isset($data->meetingstatus)) {
- switch ($data->meetingstatus) {
- case "1":
- $vevent->AddProperty("STATUS", "TENTATIVE");
- $vevent->AddProperty("X-MICROSOFT-CDO-BUSYSTATUS", "TENTATIVE");
- $vevent->AddProperty("X-MICROSOFT-DISALLOW-COUNTER", "FALSE");
- break;
- case "3":
- $vevent->AddProperty("STATUS", "CONFIRMED");
- $vevent->AddProperty("X-MICROSOFT-CDO-BUSYSTATUS", "CONFIRMED");
- $vevent->AddProperty("X-MICROSOFT-DISALLOW-COUNTER", "FALSE");
- break;
- case "5":
- case "7":
- $vevent->AddProperty("STATUS", "CANCELLED");
- $vevent->AddProperty("X-MICROSOFT-CDO-BUSYSTATUS", "CANCELLED");
- $vevent->AddProperty("X-MICROSOFT-DISALLOW-COUNTER", "TRUE");
- break;
- }
- }
- if (isset($data->attendees) && is_array($data->attendees)) {
- //If there are attendees, we need to set ORGANIZER
- //Some phones doesn't send the organizeremail, so we gotto get it somewhere else.
- //Lets use the login here ($username)
- if (!isset($data->organizeremail)) {
- $vevent->AddProperty("ORGANIZER", sprintf("MAILTO:%s", $this->originalUsername));
- }
- foreach ($data->attendees as $att) {
- if (isset($att->name)) {
- $vevent->AddProperty("ATTENDEE", sprintf("MAILTO:%s", $att->email), array("CN" => $att->name));
- }
- else {
- $vevent->AddProperty("ATTENDEE", sprintf("MAILTO:%s", $att->email));
- }
- }
- }
- if (isset($data->body)) {
- $vevent->AddProperty("DESCRIPTION", $data->body);
- }
- if (isset($data->asbody->data)) {
- $vevent->AddProperty("DESCRIPTION", $data->asbody->data);
- }
- if (isset($data->categories) && is_array($data->categories)) {
- $vevent->AddProperty("CATEGORIES", implode(",", $data->categories));
- }
-
-// X-MICROSOFT-CDO-APPT-SEQUENCE:0
-// X-MICROSOFT-CDO-OWNERAPPTID:2113393086
-// X-MICROSOFT-CDO-INTENDEDSTATUS:BUSY
-// X-MICROSOFT-CDO-IMPORTANCE:1
-// X-MICROSOFT-CDO-INSTTYPE:0
-
-
- return $vevent;
- }
-
- /**
- * Generate Recurrence
- * @param string $rec
- */
- private function _GenerateRecurrence($rec) {
- $rrule = array();
- if (isset($rec->type)) {
- $freq = "";
- switch ($rec->type) {
- case "0":
- $freq = "DAILY";
- break;
- case "1":
- $freq = "WEEKLY";
- break;
- case "2":
- case "3":
- $freq = "MONTHLY";
- break;
- case "5":
- $freq = "YEARLY";
- break;
- }
- $rrule[] = "FREQ=" . $freq;
- }
- if (isset($rec->until)) {
- $rrule[] = "UNTIL=" . gmdate("Ymd\THis\Z", $rec->until);
- }
- if (isset($rec->occurrences)) {
- $rrule[] = "COUNT=" . $rec->occurrences;
- }
- if (isset($rec->interval)) {
- $rrule[] = "INTERVAL=" . $rec->interval;
- }
- if (isset($rec->dayofweek)) {
- $week = '';
- if (isset($rec->weekofmonth)) {
- $week = $rec->weekofmonth;
- }
- $days = array();
- if (($rec->dayofweek & 1) == 1) {
- if (empty($week)) {
- $days[] = "SU";
- }
- else {
- $days[] = $week . "SU";
- }
- }
- if (($rec->dayofweek & 2) == 2) {
- if (empty($week)) {
- $days[] = "MO";
- }
- else {
- $days[] = $week . "MO";
- }
- }
- if (($rec->dayofweek & 4) == 4) {
- if (empty($week)) {
- $days[] = "TU";
- }
- else {
- $days[] = $week . "TU";
- }
- }
- if (($rec->dayofweek & 8) == 8) {
- if (empty($week)) {
- $days[] = "WE";
- }
- else {
- $days[] = $week . "WE";
- }
- }
- if (($rec->dayofweek & 16) == 16) {
- if (empty($week)) {
- $days[] = "TH";
- }
- else {
- $days[] = $week . "TH";
- }
- }
- if (($rec->dayofweek & 32) == 32) {
- if (empty($week)) {
- $days[] = "FR";
- }
- else {
- $days[] = $week . "FR";
- }
- }
- if (($rec->dayofweek & 64) == 64) {
- if (empty($week)) {
- $days[] = "SA";
- }
- else {
- $days[] = $week . "SA";
- }
- }
- $rrule[] = "BYDAY=" . implode(",", $days);
- }
- if (isset($rec->dayofmonth)) {
- $rrule[] = "BYMONTHDAY=" . $rec->dayofmonth;
- }
- if (isset($rec->monthofyear)) {
- $rrule[] = "BYMONTH=" . $rec->monthofyear;
- }
- return implode(";", $rrule);
- }
-
- /**
- * Convert a iCAL VTodo to ActiveSync format
- * @param string $data
- * @param ContentParameters $contentparameters
- */
- private function _ParseVTodoToAS($data, $contentparameters) {
- ZLog::Write(LOGLEVEL_DEBUG, sprintf("BackendCalDAV->_ParseVTodoToAS(): Parsing VTodo"));
- $truncsize = Utils::GetTruncSize($contentparameters->GetTruncation());
-
- $message = new SyncTask();
- $ical = new iCalComponent($data);
-
- $vtodos = $ical->GetComponents("VTODO");
- //Should only loop once
- foreach ($vtodos as $vtodo) {
- $message = $this->_ParseVTodoToSyncObject($vtodo, $message, $truncsize);
- }
- return $message;
- }
-
- /**
- * Parse 1 VEvent
- * @param ical_vtodo $vtodo
- * @param SyncAppointment(Exception) $message
- * @param int $truncsize
- */
- private function _ParseVTodoToSyncObject($vtodo, $message, $truncsize) {
- //Default
- $message->reminderset = "0";
- $message->importance = "1";
- $message->complete = "0";
-
- $properties = $vtodo->GetProperties();
- foreach ($properties as $property) {
- switch ($property->Name()) {
- case "SUMMARY":
- $message->subject = $property->Value();
- break;
-
- case "STATUS":
- switch ($property->Value()) {
- case "NEEDS-ACTION":
- case "IN-PROCESS":
- $message->complete = "0";
- break;
- case "COMPLETED":
- case "CANCELLED":
- $message->complete = "1";
- break;
- }
- break;
-
- case "COMPLETED":
- $message->datecompleted = Utils::MakeUTCDate($property->Value());
- break;
-
- case "DUE":
- $message->utcduedate = Utils::MakeUTCDate($property->Value());
- break;
-
- case "PRIORITY":
- $priority = $property->Value();
- if ($priority <= 3)
- $message->importance = "0";
- if ($priority <= 6)
- $message->importance = "1";
- if ($priority > 6)
- $message->importance = "2";
- break;
-
- case "RRULE":
- $message->recurrence = $this->_ParseRecurrence($property->Value(), "vtodo");
- break;
-
- case "CLASS":
- switch ($property->Value()) {
- case "PUBLIC":
- $message->sensitivity = "0";
- break;
- case "PRIVATE":
- $message->sensitivity = "2";
- break;
- case "CONFIDENTIAL":
- $message->sensitivity = "3";
- break;
- }
- break;
-
- case "DTSTART":
- $message->utcstartdate = Utils::MakeUTCDate($property->Value());
- break;
-
- case "SUMMARY":
- $message->subject = $property->Value();
- break;
-
- case "CATEGORIES":
- $categories = explode(",", $property->Value());
- $message->categories = $categories;
- break;
- }
- }
-
- if (isset($message->recurrence)) {
- $message->recurrence->start = $message->utcstartdate;
- }
-
- $valarm = current($vtodo->GetComponents("VALARM"));
- if ($valarm) {
- $properties = $valarm->GetProperties();
- foreach ($properties as $property) {
- if ($property->Name() == "TRIGGER") {
- $parameters = $property->Parameters();
- if (array_key_exists("VALUE", $parameters) && $parameters["VALUE"] == "DATE-TIME") {
- $message->remindertime = Utils::MakeUTCDate($property->Value());
- $message->reminderset = "1";
- }
- elseif (!array_key_exists("VALUE", $parameters) || $parameters["VALUE"] == "DURATION") {
- $val = str_replace("-", "", $property->Value());
- $interval = new DateInterval($val);
- $start = date_create("@" . $message->utcstartdate);
- $message->remindertime = date_timestamp_get(date_sub($start, $interval));
- $message->reminderset = "1";
- }
- }
- }
- }
- return $message;
- }
-
- /**
- * Generate a VTODO from a SyncAppointment(Exception)
- * @param string $data
- * @param string $id
- * @return iCalComponent
- */
- private function _ParseASTaskToVTodo($data, $id) {
- $vtodo = new iCalComponent();
- $vtodo->SetType("VTODO");
-
- if (isset($data->body)) {
- $vtodo->AddProperty("DESCRIPTION", $data->body);
- }
- if (isset($data->asbody->data)) {
- if (isset($data->nativebodytype) && $data->nativebodytype == SYNC_BODYPREFERENCE_RTF) {
- $rtfparser = new rtf();
- $rtfparser->loadrtf(base64_decode($data->asbody->data));
- $rtfparser->output("ascii");
- $rtfparser->parse();
- $vtodo->AddProperty("DESCRIPTION", $rtfparser->out);
- }
- else {
- $vtodo->AddProperty("DESCRIPTION", $data->asbody->data);
- }
- }
- if (isset($data->complete)) {
- if ($data->complete == "0") {
- $vtodo->AddProperty("STATUS", "NEEDS-ACTION");
- }
- else {
- $vtodo->AddProperty("STATUS", "COMPLETED");
- }
- }
- if (isset($data->datecompleted)) {
- $vtodo->AddProperty("COMPLETED", gmdate("Ymd\THis\Z", $data->datecompleted));
- }
- if ($data->utcduedate) {
- $vtodo->AddProperty("DUE", gmdate("Ymd\THis\Z", $data->utcduedate));
- }
- if (isset($data->importance)) {
- if ($data->importance == "1") {
- $vtodo->AddProperty("PRIORITY", 6);
- }
- elseif ($data->importance == "2") {
- $vtodo->AddProperty("PRIORITY", 9);
- }
- else {
- $vtodo->AddProperty("PRIORITY", 1);
- }
- }
- if (isset($data->recurrence)) {
- $vtodo->AddProperty("RRULE", $this->_GenerateRecurrence($data->recurrence));
- }
- if ($data->reminderset && $data->remindertime) {
- $valarm = new iCalComponent();
- $valarm->SetType("VALARM");
- $valarm->AddProperty("ACTION", "DISPLAY");
- $valarm->AddProperty("TRIGGER;VALUE=DATE-TIME", gmdate("Ymd\THis\Z", $data->remindertime));
- $vtodo->AddComponent($valarm);
- }
- if (isset($data->sensitivity)) {
- switch ($data->sensitivity) {
- case "0":
- $vtodo->AddProperty("CLASS", "PUBLIC");
- break;
-
- case "2":
- $vtodo->AddProperty("CLASS", "PRIVATE");
- break;
-
- case "3":
- $vtodo->AddProperty("CLASS", "CONFIDENTIAL");
- break;
- }
- }
- if (isset($data->utcstartdate)) {
- $vtodo->AddProperty("DTSTART", gmdate("Ymd\THis\Z", $data->utcstartdate));
- }
- if (isset($data->subject)) {
- $vtodo->AddProperty("SUMMARY", $data->subject);
- }
- if (isset($data->rtf)) {
- $rtfparser = new rtf();
- $rtfparser->loadrtf(base64_decode($data->rtf));
- $rtfparser->output("ascii");
- $rtfparser->parse();
- $vtodo->AddProperty("DESCRIPTION", $rtfparser->out);
- }
- if (isset($data->categories) && is_array($data->categories)) {
- $vtodo->AddProperty("CATEGORIES", implode(",", $data->categories));
- }
-
- return $vtodo;
- }
-
- private function _GetDateFromUTC($format, $date, $tz_str) {
- $timezone = $this->_GetTimezoneFromString($tz_str);
- $dt = date_create('@' . $date);
- date_timezone_set($dt, timezone_open($timezone));
- return date_format($dt, $format);
- }
-
- //This returns a timezone that matches the timezonestring.
- //We can't be sure this is the one you chose, as multiple timezones have same timezonestring
- private function _GetTimezoneFromString($tz_string) {
- //Get a list of all timezones
- $identifiers = DateTimeZone::listIdentifiers();
- //Try the default timezone first
- array_unshift($identifiers, date_default_timezone_get());
- foreach ($identifiers as $tz) {
- $str = $this->_GetTimezoneString($tz, false);
- if ($str == $tz_string) {
- ZLog::Write(LOGLEVEL_DEBUG, sprintf("BackendCalDAV->_GetTimezoneFromString(): Found timezone: '%s'.", $tz));
- return $tz;
- }
- }
- return date_default_timezone_get();
- }
-
- /**
- * Generate ActiveSync Timezone Packed String.
- * @param string $timezone
- * @param string $with_names
- * @throws Exception
- */
- private function _GetTimezoneString($timezone, $with_names = true) {
- // UTC needs special handling
- if ($timezone == "UTC")
- return base64_encode(pack('la64vvvvvvvvla64vvvvvvvvl', 0, '', 0, 0, 0, 0, 0, 0, 0, 0, 0, '', 0, 0, 0, 0, 0, 0, 0, 0, 0));
- try {
- //Generate a timezone string (PHP 5.3 needed for this)
- $timezone = new DateTimeZone($timezone);
- $trans = $timezone->getTransitions(time());
- $stdTime = null;
- $dstTime = null;
- if (count($trans) < 3) {
- throw new Exception();
- }
- if ($trans[1]['isdst'] == 1) {
- $dstTime = $trans[1];
- $stdTime = $trans[2];
- }
- else {
- $dstTime = $trans[2];
- $stdTime = $trans[1];
- }
- $stdTimeO = new DateTime($stdTime['time']);
- $stdFirst = new DateTime(sprintf("first sun of %s %s", $stdTimeO->format('F'), $stdTimeO->format('Y')), timezone_open("UTC"));
- $stdBias = $stdTime['offset'] / -60;
- $stdName = $stdTime['abbr'];
- $stdYear = 0;
- $stdMonth = $stdTimeO->format('n');
- $stdWeek = floor(($stdTimeO->format("j")-$stdFirst->format("j"))/7)+1;
- $stdDay = $stdTimeO->format('w');
- $stdHour = $stdTimeO->format('H');
- $stdMinute = $stdTimeO->format('i');
- $stdTimeO->add(new DateInterval('P7D'));
- if ($stdTimeO->format('n') != $stdMonth) {
- $stdWeek = 5;
- }
- $dstTimeO = new DateTime($dstTime['time']);
- $dstFirst = new DateTime(sprintf("first sun of %s %s", $dstTimeO->format('F'), $dstTimeO->format('Y')), timezone_open("UTC"));
- $dstName = $dstTime['abbr'];
- $dstYear = 0;
- $dstMonth = $dstTimeO->format('n');
- $dstWeek = floor(($dstTimeO->format("j")-$dstFirst->format("j"))/7)+1;
- $dstDay = $dstTimeO->format('w');
- $dstHour = $dstTimeO->format('H');
- $dstMinute = $dstTimeO->format('i');
- $dstTimeO->add(new DateInterval('P7D'));
- if ($dstTimeO->format('n') != $dstMonth) {
- $dstWeek = 5;
- }
- $dstBias = ($dstTime['offset'] - $stdTime['offset']) / -60;
- if ($with_names) {
- return base64_encode(pack('la64vvvvvvvvla64vvvvvvvvl', $stdBias, $stdName, 0, $stdMonth, $stdDay, $stdWeek, $stdHour, $stdMinute, 0, 0, 0, $dstName, 0, $dstMonth, $dstDay, $dstWeek, $dstHour, $dstMinute, 0, 0, $dstBias));
- }
- else {
- return base64_encode(pack('la64vvvvvvvvla64vvvvvvvvl', $stdBias, '', 0, $stdMonth, $stdDay, $stdWeek, $stdHour, $stdMinute, 0, 0, 0, '', 0, $dstMonth, $dstDay, $dstWeek, $dstHour, $dstMinute, 0, 0, $dstBias));
- }
- }
- catch (Exception $e) {
- // If invalid timezone is given, we return UTC
- return base64_encode(pack('la64vvvvvvvvla64vvvvvvvvl', 0, '', 0, 0, 0, 0, 0, 0, 0, 0, 0, '', 0, 0, 0, 0, 0, 0, 0, 0, 0));
- }
- return base64_encode(pack('la64vvvvvvvvla64vvvvvvvvl', 0, '', 0, 0, 0, 0, 0, 0, 0, 0, 0, '', 0, 0, 0, 0, 0, 0, 0, 0, 0));
- }
-}
\ No newline at end of file
diff --git a/sources/backend/caldav/config.php b/sources/backend/caldav/config.php
deleted file mode 100644
index 7c5c9fa..0000000
--- a/sources/backend/caldav/config.php
+++ /dev/null
@@ -1,74 +0,0 @@
-.
-*
-* Consult LICENSE file for details
-************************************************/
-
-// ************************
-// BackendCalDAV settings
-// ************************
-
-// Server protocol: http or https
-define('CALDAV_PROTOCOL', 'https');
-
-// Server name
-define('CALDAV_SERVER', 'caldavserver.domain.com');
-
-// Server port
-define('CALDAV_PORT', '443');
-
-// Path
-define('CALDAV_PATH', '/caldav.php/%u/');
-
-// Default CalDAV folder (calendar folder/principal). This will be marked as the default calendar in the mobile
-define('CALDAV_PERSONAL', 'PRINCIPAL');
-
-// If the CalDAV server supports the sync-collection operation
-// DAViCal, SOGo and SabreDav support it
-// SabreDav version must be at least 1.9.0, otherwise set this to false
-// Setting this to false will work with most servers, but it will be slower
-define('CALDAV_SUPPORTS_SYNC', false);
-
-
-// Maximum period to sync.
-// Some servers don't support more than 10 years so you will need to change this
-define('CALDAV_MAX_SYNC_PERIOD', 2147483647);
\ No newline at end of file
diff --git a/sources/backend/carddav/README b/sources/backend/carddav/README
deleted file mode 100644
index e0228d4..0000000
--- a/sources/backend/carddav/README
+++ /dev/null
@@ -1,12 +0,0 @@
-This is a CardDAV backend based in the vcarddir backend.
-
-It supports DAViCal, Sogo, OwnCloud, SabreDav... and should works with any carddav server. So if it doesn't work with your server, please open a issue.
-
-It supports ChangesSink method that will detect and send faster changes to your device.
-
-DAViCal implements the SYNC operation, it's a very fast method to detect changes in your vcards.
-The others servers don't implement it, so the code will fallback to a slower method (suggest your carddav server developers to implement it!!).
-
-This is controlled with a flag in the config.php file.
-
-Also, it can autodetect multiple addressbooks and will present them to the mobile device as an unique addressbook (only iOS supports multiple addressbook).
diff --git a/sources/backend/carddav/REQUIREMENTS b/sources/backend/carddav/REQUIREMENTS
deleted file mode 100644
index ada7371..0000000
--- a/sources/backend/carddav/REQUIREMENTS
+++ /dev/null
@@ -1,5 +0,0 @@
-REQUIREMENTS:
-php-curl
-php-xsl
-
-CardDAV server (DAViCal, Sabredav, Sogo, Owncloud...)
\ No newline at end of file
diff --git a/sources/backend/carddav/THANKS b/sources/backend/carddav/THANKS
deleted file mode 100644
index f213642..0000000
--- a/sources/backend/carddav/THANKS
+++ /dev/null
@@ -1,8 +0,0 @@
-*Drenalina SRL (www.drenalina.com)* sponsored the development of the following features in the BackendCardDAV, any existing bug it's my fault not theirs ;-)
-Thank you very much for helping to improve it!!
-
-
- - Autodetecting addressbooks within a DAV principal.
- - Merging multiple addressbooks so the device will see a unique one. Only iOS based devices support multiple addressbooks, so we will merge them for now.
- - Selecting default addressbook to store new contacts created from the device.
- - GAL addressbook and GAL search.
\ No newline at end of file
diff --git a/sources/backend/carddav/carddav.php b/sources/backend/carddav/carddav.php
deleted file mode 100644
index 21ad6ba..0000000
--- a/sources/backend/carddav/carddav.php
+++ /dev/null
@@ -1,1462 +0,0 @@
-.
-*
-* Consult LICENSE file for details
-************************************************/
-
-// config file
-require_once("backend/carddav/config.php");
-
-class BackendCardDAV extends BackendDiff implements ISearchProvider {
-
- private $domain = '';
- private $username = '';
- private $url = null;
- /**
- * @var carddav_backend
- */
- private $server = null;
- private $default_url = null;
- private $gal_url = null;
-
- // Android only supports synchronizing 1 AddressBook per account, this is the foldername for Z-Push
- private $foldername = "contacts";
-
- // We can have multiple addressbooks, but the mobile device will only see one (all of them merged)
- private $addressbooks;
-
- private $changessinkinit;
- private $contactsetag;
- private $sinkdata;
-
- /**
- * Constructor
- *
- */
- public function BackendCardDAV() {
- if (!function_exists("curl_init")) {
- throw new FatalException("BackendCardDAV(): php-curl is not found", 0, null, LOGLEVEL_FATAL);
- }
-
- $this->addressbooks = array();
- $this->changessinkinit = false;
- $this->contactsetag = array();
- $this->sinkdata = array();
- }
-
- /**
- * Authenticates the user - NOT EFFECTIVELY IMPLEMENTED
- * Normally some kind of password check would be done here.
- * Alternatively, the password could be ignored and an Apache
- * authentication via mod_auth_* could be done
- *
- * @param string $username
- * @param string $domain
- * @param string $password
- *
- * @access public
- * @return boolean
- */
- public function Logon($username, $domain, $password) {
- $this->url = CARDDAV_PROTOCOL . '://' . CARDDAV_SERVER . ':' . CARDDAV_PORT . str_replace("%d", $domain, str_replace("%u", $username, CARDDAV_PATH));
- $this->default_url = CARDDAV_PROTOCOL . '://' . CARDDAV_SERVER . ':' . CARDDAV_PORT . str_replace("%d", $domain, str_replace("%u", $username, CARDDAV_DEFAULT_PATH));
- if (defined('CARDDAV_GAL_PATH')) {
- $this->gal_url = CARDDAV_PROTOCOL . '://' . CARDDAV_SERVER . ':' . CARDDAV_PORT . str_replace("%d", $domain, str_replace("%u", $username, CARDDAV_GAL_PATH));
- }
- else {
- $this->gal_url = false;
- }
- $this->server = new carddav_backend($this->url, CARDDAV_URL_VCARD_EXTENSION);
- $this->server->set_auth($username, $password);
-
- if (($connected = $this->server->check_connection())) {
- ZLog::Write(LOGLEVEL_DEBUG, sprintf("BackendCardDAV->Logon(): User '%s' is authenticated on '%s'", $username, $this->url));
- $this->username = $username;
- $this->domain = $domain;
-
- // Autodiscover all the addressbooks
- $this->discoverAddressbooks();
- }
- else {
- //TODO: get error message
- $error = '';
- ZLog::Write(LOGLEVEL_ERROR, sprintf("BackendCardDAV->Logon(): User '%s' failed to authenticate on '%s': %s", $username, $this->url, $error));
- $this->server = null;
- }
-
- return $connected;
- }
-
- /**
- * Logs off
- *
- * @access public
- * @return boolean
- */
- public function Logoff() {
- if ($this->server != null) {
- $this->server->disconnect();
- unset($this->server);
- }
-
- $this->SaveStorages();
-
- unset($this->contactsetag);
- unset($this->sinkdata);
- unset($this->addressbooks);
-
- ZLog::Write(LOGLEVEL_DEBUG, "BackendCardDAV->Logoff(): disconnected from CARDDAV server");
-
- return true;
- }
-
- /**
- * Sends an e-mail
- * Not implemented here
- *
- * @param SyncSendMail $sm SyncSendMail object
- *
- * @access public
- * @return boolean
- * @throws StatusException
- */
- public function SendMail($sm) {
- return false;
- }
-
- /**
- * Returns the waste basket
- * Not implemented here
- *
- * @access public
- * @return string
- */
- public function GetWasteBasket() {
- return false;
- }
-
- /**
- * Returns the content of the named attachment as stream
- * Not implemented here
- *
- * @param string $attname
- *
- * @access public
- * @return SyncItemOperationsAttachment
- * @throws StatusException
- */
- public function GetAttachmentData($attname) {
- return false;
- }
-
- /**
- * Indicates if the backend has a ChangesSink.
- * A sink is an active notification mechanism which does not need polling.
- * The CardDAV backend simulates a sink by polling revision dates from the vcards
- *
- * @access public
- * @return boolean
- */
- public function HasChangesSink() {
- return true;
- }
-
- /**
- * The folder should be considered by the sink.
- * Folders which were not initialized should not result in a notification
- * of IBackend->ChangesSink().
- *
- * @param string $folderid
- *
- * @access public
- * @return boolean false if found can not be found
- */
- public function ChangesSinkInitialize($folderid) {
- ZLog::Write(LOGLEVEL_DEBUG, sprintf("BackendCardDAV->ChangesSinkInitialize(): folderid '%s'", $folderid));
-
- // We don't need the actual cards, we only need to get the changes since this moment
- $init_ok = true;
- foreach ($this->addressbooks as $addressbook) {
- try {
- $this->server->set_url($addressbook);
- $this->sinkdata[$addressbook] = $this->server->do_sync(true, false, CARDDAV_SUPPORTS_SYNC);
- }
- catch (Exception $ex) {
- ZLog::Write(LOGLEVEL_ERROR, sprintf("BackendCardDAV->ChangesSinkInitialize - Error doing the initial sync for '%s': %s", $addressbook, $ex->getMessage()));
- $init_ok = false;
- }
-
- if ($this->sinkdata[$addressbook] === false) {
- ZLog::Write(LOGLEVEL_ERROR, sprintf("BackendCardDAV->ChangesSinkInitialize - Error initializing the sink for '%s'", $addressbook));
- $init_ok = false;
- }
-
- if (CARDDAV_SUPPORTS_SYNC) {
- // we don't need to store the sinkdata if the carddav server supports native sync
- unset($this->sinkdata[$addressbook]);
- }
- }
-
- $this->changessinkinit = $init_ok;
-
- return $this->changessinkinit;
- }
-
- /**
- * The actual ChangesSink.
- * For max. the $timeout value this method should block and if no changes
- * are available return an empty array.
- * If changes are available a list of folderids is expected.
- *
- * @param int $timeout max. amount of seconds to block
- *
- * @access public
- * @return array
- */
- public function ChangesSink($timeout = 30) {
- $notifications = array();
- $stopat = time() + $timeout - 1;
- $changed = false;
-
- //We can get here and the ChangesSink not be initialized yet
- if (!$this->changessinkinit) {
- ZLog::Write(LOGLEVEL_DEBUG, sprintf("BackendCardDAV->ChangesSink - Not initialized ChangesSink, sleep and exit"));
- // We sleep and do nothing else
- sleep($timeout);
- return $notifications;
- }
-
- // only check once to reduce pressure in the DAV server
- foreach ($this->addressbooks as $addressbook) {
- $vcards = false;
- try {
- $this->server->set_url($addressbook);
- $vcards = $this->server->do_sync(false, false, CARDDAV_SUPPORTS_SYNC);
- }
- catch (Exception $ex) {
- ZLog::Write(LOGLEVEL_ERROR, sprintf("BackendCardDAV->ChangesSink - Error resyncing vcards: %s", $ex->getMessage()));
- }
-
- if ($vcards === false) {
- ZLog::Write(LOGLEVEL_ERROR, sprintf("BackendCardDAV->ChangesSink - Error getting the changes"));
- return false;
- }
- else {
- $xml_vcards = new SimpleXMLElement($vcards);
-
- if (CARDDAV_SUPPORTS_SYNC) {
- if (count($xml_vcards->element) > 0) {
- $changed = true;
- ZLog::Write(LOGLEVEL_DEBUG, sprintf("BackendCardDAV->ChangesSink - Changes detected"));
- }
- }
- else {
- $xml_sinkdata = new SimpleXMLElement($this->sinkdata[$addressbook]);
- if (count($xml_vcards->element) != count($xml_sinkdata->element)) {
- // If the number of cards is different, we know for sure, there are changes
- $changed = true;
- ZLog::Write(LOGLEVEL_DEBUG, sprintf("BackendCardDAV->ChangesSink - Changes detected"));
- }
- else {
- // If it's the same we need to check vcard to vcard, or the original strings
- if (strcmp($this->sinkdata[$addressbook], $vcards) != 0) {
- $changed = true;
- ZLog::Write(LOGLEVEL_DEBUG, sprintf("BackendCardDAV->ChangesSink - Changes detected"));
- }
- }
- unset($xml_sinkdata);
- }
-
- unset($vcards);
- unset($xml_vcards);
- }
-
- if ($changed) {
- $notifications[] = $this->foldername;
- }
- }
-
- // Wait to timeout
- if (empty($notifications)) {
- while ($stopat > time()) {
- sleep(1);
- }
- }
-
- return $notifications;
- }
-
- /**----------------------------------------------------------------------------------------------------------
- * implemented DiffBackend methods
- */
-
- /**
- * Returns a list (array) of folders.
- * In simple implementations like this one, probably just one folder is returned.
- *
- * @access public
- * @return array
- */
- public function GetFolderList() {
- ZLog::Write(LOGLEVEL_DEBUG, 'BackendCardDAV::GetFolderList()');
-
- // The mobile will only see one
- $addressbooks = array();
- $addressbook = $this->StatFolder($this->foldername);
- $addressbooks[] = $addressbook;
-
- return $addressbooks;
- }
-
- /**
- * Returns an actual SyncFolder object
- *
- * @param string $id id of the folder
- *
- * @access public
- * @return object SyncFolder with information
- */
- public function GetFolder($id) {
- ZLog::Write(LOGLEVEL_DEBUG, sprintf("BackendCardDAV::GetFolder('%s')", $id));
-
- $addressbook = false;
-
- if ($id == $this->foldername) {
- $addressbook = new SyncFolder();
- $addressbook->serverid = $id;
- $addressbook->parentid = "0";
- $addressbook->displayname = str_replace("%d", $this->domain, str_replace("%u", $this->username, CARDDAV_CONTACTS_FOLDER_NAME));
- $addressbook->type = SYNC_FOLDER_TYPE_CONTACT;
- }
-
- return $addressbook;
- }
-
- /**
- * Returns folder stats. An associative array with properties is expected.
- *
- * @param string $id id of the folder
- *
- * @access public
- * @return array
- */
- public function StatFolder($id) {
- ZLog::Write(LOGLEVEL_DEBUG, sprintf("BackendCardDAV::StatFolder('%s')", $id));
-
- $addressbook = $this->GetFolder($id);
-
- $stat = array();
- $stat["id"] = $id;
- $stat["parent"] = $addressbook->parentid;
- $stat["mod"] = $addressbook->displayname;
-
- return $stat;
- }
-
- /**
- * Creates or modifies a folder
- * Not implemented here
- *
- * @param string $folderid id of the parent folder
- * @param string $oldid if empty -> new folder created, else folder is to be renamed
- * @param string $displayname new folder name (to be created, or to be renamed to)
- * @param int $type folder type
- *
- * @access public
- * @return boolean status
- * @throws StatusException could throw specific SYNC_FSSTATUS_* exceptions
- *
- */
- public function ChangeFolder($folderid, $oldid, $displayname, $type) {
- return false;
- }
-
- /**
- * Deletes a folder
- * Not implemented here
- *
- * @param string $id
- * @param string $parent is normally false
- *
- * @access public
- * @return boolean status - false if e.g. does not exist
- * @throws StatusException could throw specific SYNC_FSSTATUS_* exceptions
- *
- */
- public function DeleteFolder($id, $parentid) {
- return false;
- }
-
- /**
- * Returns a list (array) of messages
- *
- * @param string $folderid id of the parent folder
- * @param long $cutoffdate timestamp in the past from which on messages should be returned
- *
- * @access public
- * @return array/false array with messages or false if folder is not available
- */
- public function GetMessageList($folderid, $cutoffdate) {
- ZLog::Write(LOGLEVEL_DEBUG, sprintf("BackendCardDAV->GetMessageList('%s', '%s')", $folderid, $cutoffdate));
-
- $messages = array();
-
- foreach ($this->addressbooks as $addressbook) {
- $addressbookId = $this->convertAddressbookUrl($addressbook);
-
- $vcards = false;
- try {
- // We don't need the actual vcards here, we only need a list of all them
- // This petition is always "initial", and we don't "include_vcards"
- $this->server->set_url($addressbook);
- $vcards = $this->server->do_sync(true, false, CARDDAV_SUPPORTS_SYNC);
- }
- catch (Exception $ex) {
- ZLog::Write(LOGLEVEL_ERROR, sprintf("BackendCardDAV->GetMessageList - Error getting the vcards in '%s': %s", $addressbook, $ex->getMessage()));
- }
-
- if ($vcards === false) {
- ZLog::Write(LOGLEVEL_ERROR, sprintf("BackendCardDAV->GetMessageList - Error getting the vcards"));
- }
- else {
- $xml_vcards = new SimpleXMLElement($vcards);
- foreach ($xml_vcards->element as $vcard) {
- $id = $addressbookId . "-" . $vcard->id->__toString();
- $this->contactsetag[$id] = $vcard->etag->__toString();
- $messages[] = $this->StatMessage($folderid, $id);
- }
- }
- }
-
- return $messages;
- }
-
- /**
- * Returns the actual SyncXXX object type.
- *
- * @param string $folderid id of the parent folder
- * @param string $id id of the message
- * @param ContentParameters $contentparameters parameters of the requested message (truncation, mimesupport etc)
- *
- * @access public
- * @return object/false false if the message could not be retrieved
- */
- public function GetMessage($folderid, $id, $contentparameters) {
- ZLog::Write(LOGLEVEL_DEBUG, sprintf("BackendCardDAV->GetMessage('%s', '%s')", $folderid, $id));
-
- $message = false;
- $addressbookId = $this->getAddressbookIdFromVcard($id);
- $vcardId = $this->getVcardId($id);
- $addressbookUrl = $this->getAddressbookFromId($addressbookId);
-
- if ($addressbookUrl !== false) {
- $xml_vcard = false;
- try {
- $this->server->set_url($addressbookUrl);
- $xml_vcard = $this->server->get_xml_vcard($vcardId);
- }
- catch (Exception $ex) {
- ZLog::Write(LOGLEVEL_ERROR, sprintf("BackendCardDAV->GetMessage - Error getting vcard '%s' in '%s': %s", $vcardId, $addressbookId, $ex->getMessage()));
- }
-
- if ($xml_vcard !== false) {
- $truncsize = Utils::GetTruncSize($contentparameters->GetTruncation());
- $xml_data = new SimpleXMLElement($xml_vcard);
- $message = $this->ParseFromVCard($xml_data->element[0]->vcard->__toString(), $truncsize);
- }
- }
-
- if ($message === false) {
- ZLog::Write(LOGLEVEL_ERROR, sprintf("BackendCardDAV->GetMessage(): vCard not found"));
- }
-
- return $message;
- }
-
-
- /**
- * Returns message stats, analogous to the folder stats from StatFolder().
- *
- * @param string $folderid id of the folder
- * @param string $id id of the message
- *
- * @access public
- * @return array
- */
- public function StatMessage($folderid, $id) {
- ZLog::Write(LOGLEVEL_DEBUG, sprintf("BackendCardDAV->StatMessage('%s', '%s')", $folderid, $id));
-
- $message = array();
- if (!isset($this->contactsetag[$id])) {
- $addressbookId = $this->getAddressbookIdFromVcard($id);
- $vcardId = $this->getVcardId($id);
- $addressbookUrl = $this->getAddressbookFromId($addressbookId);
- ZLog::Write(LOGLEVEL_DEBUG, sprintf("BackendCardDAV->StatMessage - No contactsetag found, getting vcard '%s' in '%s'", $vcardId, $addressbookId));
- if ($addressbookUrl !== false) {
- $xml_vcard = false;
- try {
- $this->server->set_url($addressbookUrl);
- $xml_vcard = $this->server->get_xml_vcard($vcardId);
- }
- catch (Exception $ex) {
- ZLog::Write(LOGLEVEL_ERROR, sprintf("BackendCardDAV->StatMessage - Error getting vcard '%s' in '%s': %s", $vcardId, $addressbookId, $ex->getMessage()));
- }
-
- if ($xml_vcard !== false) {
- $vcard = new SimpleXMLElement($xml_vcard);
- $this->contactsetag[$id] = $vcard->element[0]->etag->__toString();
- unset($vcard);
- }
- unset($xml_vcard);
- }
- }
- $message["mod"] = $this->contactsetag[$id];
- $message["id"] = $id;
- $message["flags"] = 1;
-
- return $message;
- }
-
- /**
- * Called when a message has been changed on the mobile.
- * This functionality is not available for emails.
- *
- * @param string $folderid id of the folder
- * @param string $id id of the message
- * @param SyncXXX $message the SyncObject containing a message
- * @param ContentParameters $contentParameters
- *
- * @access public
- * @return array same return value as StatMessage()
- * @throws StatusException could throw specific SYNC_STATUS_* exceptions
- */
- public function ChangeMessage($folderid, $id, $message, $contentParameters) {
- ZLog::Write(LOGLEVEL_DEBUG, sprintf("BackendCardDAV->ChangeMessage('%s', '%s')", $folderid, $id));
-
- $vcard_text = $this->ParseToVCard($message);
-
- if ($vcard_text === false) {
- ZLog::Write(LOGLEVEL_ERROR, sprintf("BackendCardDAV->ChangeMessage - Error converting message to vCard"));
- }
- else {
- ZLog::Write(LOGLEVEL_WBXML, sprintf("BackendCardDAV->ChangeMessage - vCard\n%s\n", $vcard_text));
-
- $updated = false;
- if (strlen($id) == 0) {
- //no id, new vcard
- try {
- $addressbookId = $this->getAddressbookFromUrl($this->default_url);
- if ($addressbookId === false) {
- $addressbookId = $this->getAddressbookFromUrl($this->addressbooks[0]);
- $this->server->set_url($this->addressbooks[0]);
- }
- else {
- $this->server->set_url($this->default_url);
- }
-
- $updated = $this->server->add($vcard_text);
- if ($updated !== false) {
- $id = $addressbookId . "-" . $updated;
- }
- }
- catch (Exception $ex) {
- ZLog::Write(LOGLEVEL_ERROR, sprintf("BackendCardDAV->ChangeMessage - Error adding vcard '%s' : %s", $id, $ex->getMessage()));
- }
- }
- else {
- //id, update vcard
-
- $vcardId = $this->getVcardId($id);
- $addressbookUrl = $this->getAddressbookFromId($this->getAddressbookIdFromVcard($id));
-
- if ($addressbookUrl !== false) {
- try {
- $this->server->set_url($addressbookUrl);
- $updated = $this->server->update($vcard_text, $vcardId);
- }
- catch (Exception $ex) {
- ZLog::Write(LOGLEVEL_ERROR, sprintf("BackendCardDAV->ChangeMessage - Error updating vcard '%s' : %s", $id, $ex->getMessage()));
- }
- }
- }
-
- if ($updated !== false) {
- ZLog::Write(LOGLEVEL_DEBUG, sprintf("BackendCardDAV->ChangeMessage - vCard updated"));
- }
- else {
- ZLog::Write(LOGLEVEL_ERROR, sprintf("BackendCardDAV->ChangeMessage - vCard not updated"));
- }
- }
-
- return $this->StatMessage($folderid, $id);
- }
-
- /**
- * Changes the 'read' flag of a message on disk
- * Not implemented here
- *
- * @param string $folderid id of the folder
- * @param string $id id of the message
- * @param int $flags read flag of the message
- * @param ContentParameters $contentParameters
- *
- * @access public
- * @return boolean status of the operation
- * @throws StatusException could throw specific SYNC_STATUS_* exceptions
- */
- public function SetReadFlag($folderid, $id, $flags, $contentParameters) {
- return false;
- }
-
- /**
- * Called when the user has requested to delete (really delete) a message
- *
- * @param string $folderid id of the folder
- * @param string $id id of the message
- * @param ContentParameters $contentParameters
- *
- * @access public
- * @return boolean status of the operation
- * @throws StatusException could throw specific SYNC_STATUS_* exceptions
- */
- public function DeleteMessage($folderid, $id, $contentParameters) {
- ZLog::Write(LOGLEVEL_DEBUG, sprintf("BackendCardDAV->DeleteMessage('%s', '%s')", $folderid, $id));
-
- $deleted = false;
-
- $vcardId = $this->getVcardId($id);
- $addressbookUrl = $this->getAddressbookFromId($this->getAddressbookIdFromVcard($id));
-
- if ($addressbookUrl !== false) {
- try {
- $this->server->set_url($addressbookUrl);
- $deleted = $this->server->delete($vcardId);
- }
- catch (Exception $ex) {
- ZLog::Write(LOGLEVEL_ERROR, sprintf("BackendCardDAV->DeleteMessage - Error deleting vcard: %s", $ex->getMessage()));
- }
- }
-
- if ($deleted) {
- ZLog::Write(LOGLEVEL_DEBUG, sprintf("BackendCardDAV->DeleteMessage - vCard deleted"));
- }
- else {
- ZLog::Write(LOGLEVEL_ERROR, sprintf("BackendCardDAV->DeleteMessage - cannot delete vCard"));
- }
-
- return $deleted;
- }
-
- /**
- * Called when the user moves an item on the PDA from one folder to another
- * Not implemented here
- *
- * @param string $folderid id of the source folder
- * @param string $id id of the message
- * @param string $newfolderid id of the destination folder
- * @param ContentParameters $contentParameters
- *
- * @access public
- * @return boolean status of the operation
- * @throws StatusException could throw specific SYNC_MOVEITEMSSTATUS_* exceptions
- */
- public function MoveMessage($folderid, $id, $newfolderid, $contentParameters) {
- return false;
- }
-
-
- /**
- * Resolves recipients
- *
- * @param SyncObject $resolveRecipients
- *
- * @access public
- * @return SyncObject $resolveRecipients
- */
- public function ResolveRecipients($resolveRecipients) {
- // TODO:
- return false;
- }
-
-
- /**
- * Indicates which AS version is supported by the backend.
- *
- * @access public
- * @return string AS version constant
- */
- public function GetSupportedASVersion() {
- return ZPush::ASV_14;
- }
-
-
- /**
- * Returns the BackendCardDAV as it implements the ISearchProvider interface
- * This could be overwritten by the global configuration
- *
- * @access public
- * @return object Implementation of ISearchProvider
- */
- public function GetSearchProvider() {
- return $this;
- }
-
-
- /**----------------------------------------------------------------------------------------------------------
- * public ISearchProvider methods
- */
-
- /**
- * Indicates if a search type is supported by this SearchProvider
- * Currently only the type ISearchProvider::SEARCH_GAL (Global Address List) is implemented
- *
- * @param string $searchtype
- *
- * @access public
- * @return boolean
- */
- public function SupportsType($searchtype) {
- if ($this->gal_url !== false) {
- return ($searchtype == ISearchProvider::SEARCH_GAL);
- }
- else {
- return false;
- }
- }
-
-
- /**
- * Queries the CardDAV backend
- *
- * @param string $searchquery string to be searched for
- * @param string $searchrange specified searchrange
- *
- * @access public
- * @return array search results
- */
- public function GetGALSearchResults($searchquery, $searchrange) {
- ZLog::Write(LOGLEVEL_DEBUG, sprintf("BackendCardDAV->GetGALSearchResults(%s, %s)", $searchquery, $searchrange));
- if ($this->gal_url !== false && $this->server !== false) {
- // Don't search if the length is < 5, we are typing yet
- if (strlen($searchquery) < CARDDAV_GAL_MIN_LENGTH) {
- return false;
- }
-
- ZLog::Write(LOGLEVEL_DEBUG, sprintf("BackendCardDAV->GetGALSearchResults searching: %s", $this->url));
- try {
- ZLog::Write(LOGLEVEL_DEBUG, sprintf("BackendCardDAV->GetGALSearchResults server is null? %d", $this->server == null));
- $this->server->set_url($this->gal_url);
- $vcards = $this->server->search_vcards(str_replace("<", "", str_replace(">", "", $searchquery)), 15, true, false,
- defined('CARDDAV_SUPPORTS_FN_SEARCH') ? CARDDAV_SUPPORTS_FN_SEARCH : false);
- }
- catch (Exception $e) {
- $vcards = false;
- ZLog::Write(LOGLEVEL_ERROR, sprintf("BackendCardDAV->GetGALSearchResults : Error in search %s", $e->getMessage()));
- }
- if ($vcards === false) {
- ZLog::Write(LOGLEVEL_ERROR, "BackendCardDAV->GetGALSearchResults : Error in search query. Search aborted");
- return false;
- }
-
- $xml_vcards = new SimpleXMLElement($vcards);
- unset($vcards);
-
- // range for the search results, default symbian range end is 50, wm 99,
- // so we'll use that of nokia
- $rangestart = 0;
- $rangeend = 50;
-
- if ($searchrange != '0') {
- $pos = strpos($searchrange, '-');
- $rangestart = substr($searchrange, 0, $pos);
- $rangeend = substr($searchrange, ($pos + 1));
- }
- $items = array();
-
- // TODO the limiting of the searchresults could be refactored into Utils as it's probably used more than once
- $querycnt = $xml_vcards->count();
- //do not return more results as requested in range
- $querylimit = (($rangeend + 1) < $querycnt) ? ($rangeend + 1) : $querycnt == 0 ? 1 : $querycnt;
- $items['range'] = $rangestart.'-'.($querylimit - 1);
- $items['searchtotal'] = $querycnt;
-
- ZLog::Write(LOGLEVEL_DEBUG, sprintf("BackendCardDAV->GetGALSearchResults : %s entries found, returning %s to %s", $querycnt, $rangestart, $querylimit));
-
- $i = 0;
- $rc = 0;
- foreach ($xml_vcards->element as $xml_vcard) {
- if ($i >= $rangestart && $i < $querylimit) {
- $contact = $this->ParseFromVCard($xml_vcard->vcard->__toString());
- if ($contact === false) {
- ZLog::Write(LOGLEVEL_ERROR, sprintf("BackendCardDAV->GetGALSearchResults : error converting vCard to AS contact\n%s\n", $xml_vcard->vcard->__toString()));
- }
- else {
- $items[$rc][SYNC_GAL_EMAILADDRESS] = $contact->email1address;
- if (isset($contact->fileas)) {
- $items[$rc][SYNC_GAL_DISPLAYNAME] = $contact->fileas;
- }
- else if (isset($contact->firstname) || isset($contact->middlename) || isset($contact->lastname)) {
- $items[$rc][SYNC_GAL_DISPLAYNAME] = $contact->firstname . (isset($contact->middlename) ? " " . $contact->middlename : "") . (isset($contact->lastname) ? " " . $contact->lastname : "");
- }
- else {
- $items[$rc][SYNC_GAL_DISPLAYNAME] = $contact->email1address;
- }
- if (isset($contact->firstname)) {
- $items[$rc][SYNC_GAL_FIRSTNAME] = $contact->firstname;
- }
- else {
- $items[$rc][SYNC_GAL_FIRSTNAME] = "";
- }
- if (isset($contact->lastname)) {
- $items[$rc][SYNC_GAL_LASTNAME] = $contact->lastname;
- }
- else {
- $items[$rc][SYNC_GAL_LASTNAME] = "";
- }
- if (isset($contact->business2phonenumber)) {
- $items[$rc][SYNC_GAL_PHONE] = $contact->business2phonenumber;
- }
- if (isset($contact->home2phonenumber)) {
- $items[$rc][SYNC_GAL_HOMEPHONE] = $contact->home2phonenumber;
- }
- if (isset($contact->mobilephonenumber)) {
- $items[$rc][SYNC_GAL_MOBILEPHONE] = $contact->mobilephonenumber;
- }
- if (isset($contact->title)) {
- $items[$rc][SYNC_GAL_TITLE] = $contact->title;
- }
- if (isset($contact->companyname)) {
- $items[$rc][SYNC_GAL_COMPANY] = $contact->companyname;
- }
- if (isset($contact->department)) {
- $items[$rc][SYNC_GAL_OFFICE] = $contact->department;
- }
- if (isset($contact->nickname)) {
- $items[$rc][SYNC_GAL_ALIAS] = $contact->nickname;
- }
- unset($contact);
- $rc++;
- }
- }
- $i++;
- }
-
- unset($xml_vcards);
- return $items;
- }
- else {
- unset($xml_vcards);
- return false;
- }
- }
-
- /**
- * Searches for the emails on the server
- *
- * @param ContentParameter $cpo
- *
- * @return array
- */
- public function GetMailboxSearchResults($cpo) {
- return false;
- }
-
- /**
- * Terminates a search for a given PID
- *
- * @param int $pid
- *
- * @return boolean
- */
- public function TerminateSearch($pid) {
- return true;
- }
-
- /**
- * Disconnects from CardDAV
- *
- * @access public
- * @return boolean
- */
- public function Disconnect() {
- return true;
- }
-
-
- /**----------------------------------------------------------------------------------------------------------
- * private vcard-specific internals
- */
-
-
- /**
- * Escapes a string
- *
- * @param string $data string to be escaped
- *
- * @access private
- * @return string
- */
- private function escape($data) {
- if (is_array($data)) {
- foreach ($data as $key => $val) {
- $data[$key] = $this->escape($val);
- }
- return $data;
- }
- $data = str_replace("\r\n", "\n", $data);
- $data = str_replace("\r", "\n", $data);
- $data = str_replace(array('\\', ';', ',', "\n"), array('\\\\', '\\;', '\\,', '\\n'), $data);
- return $data;
- }
-
- /**
- * Un-escapes a string
- *
- * @param string $data string to be un-escaped
- *
- * @access private
- * @return string
- */
- private function unescape($data) {
- $data = str_replace(array('\\\\', '\\;', '\\,', '\\n','\\N'),array('\\', ';', ',', "\n", "\n"),$data);
- return $data;
- }
-
- /**
- * Converts the vCard into SyncContact.
- * See RFC 6350 for vCard format details.
- *
- * @param string $data string with the vcard
- * @param int $truncsize truncate size requested
- * @return SyncContact
- */
- private function ParseFromVCard($data, $truncsize = -1) {
- ZLog::Write(LOGLEVEL_WBXML, sprintf("BackendCardDAV->ParseFromVCard : vCard\n%s\n", $data));
-
- $types = array ('dom' => 'type', 'intl' => 'type', 'postal' => 'type', 'parcel' => 'type', 'home' => 'type', 'work' => 'type',
- 'pref' => 'type', 'voice' => 'type', 'fax' => 'type', 'msg' => 'type', 'cell' => 'type', 'pager' => 'type',
- 'bbs' => 'type', 'modem' => 'type', 'car' => 'type', 'isdn' => 'type', 'video' => 'type',
- 'aol' => 'type', 'applelink' => 'type', 'attmail' => 'type', 'cis' => 'type', 'eworld' => 'type',
- 'internet' => 'type', 'ibmmail' => 'type', 'mcimail' => 'type',
- 'powershare' => 'type', 'prodigy' => 'type', 'tlx' => 'type', 'x400' => 'type',
- 'gif' => 'type', 'cgm' => 'type', 'wmf' => 'type', 'bmp' => 'type', 'met' => 'type', 'pmb' => 'type', 'dib' => 'type',
- 'pict' => 'type', 'tiff' => 'type', 'pdf' => 'type', 'ps' => 'type', 'jpeg' => 'type', 'qtime' => 'type',
- 'mpeg' => 'type', 'mpeg2' => 'type', 'avi' => 'type',
- 'wave' => 'type', 'aiff' => 'type', 'pcm' => 'type',
- 'x509' => 'type', 'pgp' => 'type', 'text' => 'value', 'inline' => 'value', 'url' => 'value', 'cid' => 'value', 'content-id' => 'value',
- '7bit' => 'encoding', '8bit' => 'encoding', 'quoted-printable' => 'encoding', 'base64' => 'encoding',
- );
-
- // Parse the vcard
- $message = new SyncContact();
-
- $data = str_replace("\x00", '', $data);
- $data = str_replace("\r\n", "\n", $data);
- $data = str_replace("\r", "\n", $data);
- $data = preg_replace('/(\n)([ \t])/i', '', $data);
-
- $lines = explode("\n", $data);
-
- $vcard = array();
- foreach ($lines as $line) {
- if (trim($line) == '')
- continue;
- $pos = strpos($line, ':');
- if ($pos === false)
- continue;
-
- $field = trim(substr($line, 0, $pos));
- $value = trim(substr($line, $pos + 1));
-
- $fieldparts = preg_split('/(? $v) {
- $val[$i] = quoted_printable_decode($v);
- }
- break;
- case 'b':
- case 'base64':
- foreach ($val as $i => $v) {
- $val[$i] = base64_decode($v);
- }
- break;
- }
- } else {
- foreach ($val as $i => $v) {
- $val[$i] = $this->unescape($v);
- }
- }
- $fieldvalue['val'] = $val;
- $vcard[$type][] = $fieldvalue;
- }
-
- if (isset($vcard['email'][0]['val'][0]))
- $message->email1address = $vcard['email'][0]['val'][0];
- if (isset($vcard['email'][1]['val'][0]))
- $message->email2address = $vcard['email'][1]['val'][0];
- if (isset($vcard['email'][2]['val'][0]))
- $message->email3address = $vcard['email'][2]['val'][0];
-
- if (isset($vcard['tel'])) {
- foreach ($vcard['tel'] as $tel) {
- if (!isset($tel['type'])) {
- $tel['type'] = array();
- }
- if (in_array('car', $tel['type'])) {
- $message->carphonenumber = $tel['val'][0];
- }
- elseif (in_array('pager', $tel['type'])) {
- $message->pagernumber = $tel['val'][0];
- }
- elseif (in_array('cell', $tel['type'])) {
- $message->mobilephonenumber = $tel['val'][0];
- }
- elseif (in_array('home', $tel['type'])) {
- if (in_array('fax', $tel['type'])) {
- $message->homefaxnumber = $tel['val'][0];
- }
- elseif (empty($message->homephonenumber)) {
- $message->homephonenumber = $tel['val'][0];
- }
- else {
- $message->home2phonenumber = $tel['val'][0];
- }
- }
- elseif (in_array('work', $tel['type'])) {
- if (in_array('fax', $tel['type'])) {
- $message->businessfaxnumber = $tel['val'][0];
- }
- elseif (empty($message->businessphonenumber)) {
- $message->businessphonenumber = $tel['val'][0];
- }
- else {
- $message->business2phonenumber = $tel['val'][0];
- }
- }
- elseif (empty($message->homephonenumber)) {
- $message->homephonenumber = $tel['val'][0];
- }
- elseif (empty($message->home2phonenumber)) {
- $message->home2phonenumber = $tel['val'][0];
- }
- else {
- $message->radiophonenumber = $tel['val'][0];
- }
- }
- }
-
- //;;street;city;state;postalcode;country
- if (isset($vcard['adr'])) {
- foreach ($vcard['adr'] as $adr) {
- if (empty($adr['type'])) {
- $a = 'other';
- }
- elseif (in_array('home', $adr['type'])) {
- $a = 'home';
- }
- elseif (in_array('work', $adr['type'])) {
- $a = 'business';
- }
- else {
- $a = 'other';
- }
- if (!empty($adr['val'][2])) {
- $b=$a.'street';
- $message->$b = $adr['val'][2];
- }
- if (!empty($adr['val'][3])) {
- $b=$a.'city';
- $message->$b = $adr['val'][3];
- }
- if (!empty($adr['val'][4])) {
- $b=$a.'state';
- $message->$b = $adr['val'][4];
- }
- if (!empty($adr['val'][5])) {
- $b=$a.'postalcode';
- $message->$b = $adr['val'][5];
- }
- if (!empty($adr['val'][6])) {
- $b=$a.'country';
- $message->$b = $adr['val'][6];
- }
- }
- }
-
- if (!empty($vcard['fn'][0]['val'][0]))
- $message->fileas = $vcard['fn'][0]['val'][0];
- if (!empty($vcard['n'][0]['val'][0]))
- $message->lastname = $vcard['n'][0]['val'][0];
- if (!empty($vcard['n'][0]['val'][1]))
- $message->firstname = $vcard['n'][0]['val'][1];
- if (!empty($vcard['n'][0]['val'][2]))
- $message->middlename = $vcard['n'][0]['val'][2];
- if (!empty($vcard['n'][0]['val'][3]))
- $message->title = $vcard['n'][0]['val'][3];
- if (!empty($vcard['n'][0]['val'][4]))
- $message->suffix = $vcard['n'][0]['val'][4];
- if (!empty($vcard['nickname'][0]['val'][0]))
- $message->nickname = $vcard['nickname'][0]['val'][0];
- if (!empty($vcard['bday'][0]['val'][0])) {
- $tz = date_default_timezone_get();
- date_default_timezone_set('UTC');
- $message->birthday = strtotime($vcard['bday'][0]['val'][0]);
- date_default_timezone_set($tz);
- }
- if (!empty($vcard['org'][0]['val'][0]))
- $message->companyname = $vcard['org'][0]['val'][0];
- if (!empty($vcard['note'][0]['val'][0])) {
- if (Request::GetProtocolVersion() >= 12.0) {
- $message->asbody = new SyncBaseBody();
- $message->asbody->type = SYNC_BODYPREFERENCE_PLAIN;
- $message->asbody->data = $vcard['note'][0]['val'][0];
- if ($truncsize > 0 && $truncsize < strlen($message->asbody->data)) {
- $message->asbody->truncated = 1;
- $message->asbody->data = Utils::Utf8_truncate($message->asbody->data, $truncsize);
- }
- else {
- $message->asbody->truncated = 0;
- }
-
- $message->asbody->estimatedDataSize = strlen($message->asbody->data);
- }
- else {
- $message->body = $vcard['note'][0]['val'][0];
- if ($truncsize > 0 && $truncsize < strlen($message->body)) {
- $message->bodytruncated = 1;
- $message->body = Utils::Utf8_truncate($message->body, $truncsize);
- }
- else {
- $message->bodytruncated = 0;
- }
- $message->bodysize = strlen($message->body);
- }
- }
-
- // Support both ROLE and TITLE (RFC 6350 § 6.6.1 / § 6.6.2) as mapped to JobTitle
- if (!empty($vcard['role'][0]['val'][0]))
- $message->jobtitle = $vcard['role'][0]['val'][0];
- if (!empty($vcard['title'][0]['val'][0]))
- $message->jobtitle = $vcard['title'][0]['val'][0];
-
- if (!empty($vcard['url'][0]['val'][0]))
- $message->webpage = $vcard['url'][0]['val'][0];
- if (!empty($vcard['categories'][0]['val']))
- $message->categories = $vcard['categories'][0]['val'];
-
- if (!empty($vcard['photo'][0]['val'][0]))
- $message->picture = base64_encode($vcard['photo'][0]['val'][0]);
-
- return $message;
- }
-
- /**
- * Convert a SyncObject into vCard.
- *
- * @param SyncContact $message AS Contact
- * @return string vcard text
- */
- private function ParseToVCard($message) {
- // http://tools.ietf.org/html/rfc6350
- $mapping = array(
- 'fileas' => 'FN',
- 'lastname;firstname;middlename;title;suffix' => 'N',
- 'email1address' => 'EMAIL;PREF=1',
- 'email2address' => 'EMAIL;PREF=2',
- 'email3address' => 'EMAIL;PREF=3',
- 'businessphonenumber' => 'TEL;TYPE=WORK,VOICE',
- 'business2phonenumber' => 'TEL;TYPE=WORK,VOICE',
- 'businessfaxnumber' => 'TEL;TYPE=WORK,FAX',
- 'homephonenumber' => 'TEL;TYPE=HOME,VOICE',
- 'home2phonenumber' => 'TEL;TYPE=HOME,VOICE',
- 'homefaxnumber' => 'TEL;TYPE=HOME,FAX',
- 'mobilephonenumber' => 'TEL;TYPE=CELL',
- 'carphonenumber' => 'TEL;TYPE=VOICE',
- 'pagernumber' => 'TEL;TYPE=PAGER',
- ';;businessstreet;businesscity;businessstate;businesspostalcode;businesscountry' => 'ADR;TYPE=WORK',
- ';;homestreet;homecity;homestate;homepostalcode;homecountry' => 'ADR;TYPE=HOME',
- ';;otherstreet;othercity;otherstate;otherpostalcode;othercountry' => 'ADR',
- 'companyname' => 'ORG',
- 'body' => 'NOTE',
- 'jobtitle' => 'ROLE',
- 'webpage' => 'URL',
- 'nickname' => 'NICKNAME'
- );
-
- $data = "BEGIN:VCARD\nVERSION:3.0\nPRODID:Z-Push\n";
- foreach ($mapping as $k => $v) {
- $val = '';
- $ks = explode(';', $k);
- foreach ($ks as $i) {
- if (!empty($message->$i))
- $val .= $this->escape($message->$i);
- $val.=';';
- }
- if ($k == 'body' && isset($message->asbody)) {
- $val = $message->asbody->data;
- }
- if (empty($val) || preg_match('/^(\;)+$/', $val) == 1)
- continue;
- // Remove trailing ;
- $val = substr($val, 0, -1);
- if (strlen($val) > 50) {
- $data .= $v.":\n\t".substr(chunk_split($val, 50, "\n\t"), 0, -1);
- }
- else {
- $data .= $v.':'.$val."\n";
- }
- }
- if (!empty($message->categories))
- $data .= 'CATEGORIES:'.implode(',', $message->categories)."\n";
- if (!empty($message->picture))
- $data .= 'PHOTO;ENCODING=BASE64;TYPE=JPEG:'."\n\t".substr(chunk_split($message->picture, 50, "\n\t"), 0, -1);
- if (isset($message->birthday))
- $data .= 'BDAY:'.date('Y-m-d', $message->birthday)."\n";
- $data .= "END:VCARD";
-
- // http://en.wikipedia.org/wiki/VCard
- // TODO: add support for v4.0
- // not supported: anniversary, assistantname, assistnamephonenumber, children, department, officelocation, radiophonenumber, spouse, rtf
-
- return $data;
- }
-
-
- /**
- * Discover all the addressbooks collections for a user under a root.
- *
- */
- private function discoverAddressbooks() {
- unset($this->addressbooks);
- $this->addressbooks = array();
- $raw = $this->server->get(false, false, true);
- if ($raw !== false) {
- $xml = new SimpleXMLElement($raw);
- foreach ($xml->addressbook_element as $response) {
- if ($this->gal_url !== false) {
- if (strcmp(urldecode($response->url), $this->gal_url) == 0) {
- ZLog::Write(LOGLEVEL_DEBUG, sprintf("BackendCardDAV::discoverAddressbooks() Ignoring GAL addressbook '%s'", $this->gal_url));
- continue;
- }
- }
- ZLog::Write(LOGLEVEL_DEBUG, sprintf("BackendCardDAV::discoverAddressbooks() Found addressbook '%s'", urldecode($response->url)));
- $this->addressbooks[] = urldecode($response->url);
- }
- unset($xml);
- }
- }
-
- /**
- * Returns de addressbookId of a vcard.
- * The vcardId sent to the device is formed as [addressbookId]-[vcardId]
- *
- * @param string $vcardId vcard ID in device.
- * @return addressbookId
- */
- private function getAddressbookIdFromVcard($vcardId) {
- $parts = explode("-", $vcardId);
-
- return $parts[0];
- }
-
- /**
- * Returns de vcard id stored in the carddav server.
- *
- * @param string $vcardId vcard ID in device
- * @return vcard id in carddav server
- */
- private function getVcardId($vcardId) {
- $parts = explode("-", $vcardId);
-
- $id = "";
- for ($i = 1; $i < count($parts); $i++) {
- if ($i > 1) {
- $id .= "-";
- }
- $id .= $parts[$i];
- }
-
- return $id;
- }
-
- /**
- * Convert an addressbook url into a zpush id.
- *
- * @param string $addressbookUrl AddressBook URL
- * @return id or false
- */
- private function convertAddressbookUrl($addressbookUrl) {
- $this->InitializePermanentStorage();
-
- // check if this addressbookUrl was converted before
- $addressbookId = $this->getAddressbookFromUrl($addressbookUrl);
-
- // nothing found, so generate a new id and put it in the cache
- if ($addressbookId === false) {
- ZLog::Write(LOGLEVEL_DEBUG, sprintf("BackendCardDAV::convertAddressbookUrl('%s') New addressbook", $addressbookUrl));
- // generate addressbookId and add it to the mapping
- $addressbookId = sprintf('%04x%04x', mt_rand( 0, 0xffff ), mt_rand( 0, 0xffff ));
-
- // addressbookId to addressbookUrl mapping
- if (!isset($this->permanentStorage->fmAidAurl))
- $this->permanentStorage->fmAidAurl = array();
-
- $a = $this->permanentStorage->fmAidAurl;
- $a[$addressbookId] = $addressbookUrl;
- $this->permanentStorage->fmAidAurl = $a;
-
- // addressbookUrl to addressbookId mapping
- if (!isset($this->permanentStorage->fmAurlAid))
- $this->permanentStorage->fmAurlAid = array();
-
- $b = $this->permanentStorage->fmAurlAid;
- $b[$addressbookUrl] = $addressbookId;
- $this->permanentStorage->fmAurlAid = $b;
- }
-
- ZLog::Write(LOGLEVEL_DEBUG, sprintf("BackendCardDAV::convertAddressbookUrl('%s') = %s", $addressbookUrl, $addressbookId));
-
- return $addressbookId;
- }
-
- /**
- * Get the URL of an addressbook zpush id.
- *
- * @param string $addressbookId AddressBook Z-Push based ID
- * @return url or false
- */
- private function getAddressbookFromId($addressbookId) {
- $this->InitializePermanentStorage();
-
- $addressbookUrl = false;
-
- if (isset($this->permanentStorage->fmAidAurl)) {
- if (isset($this->permanentStorage->fmAidAurl[$addressbookId])) {
- $addressbookUrl = $this->permanentStorage->fmAidAurl[$addressbookId];
- ZLog::Write(LOGLEVEL_DEBUG, sprintf("BackendCardDAV::getAddressbookFromId('%s') = %s", $addressbookId, $addressbookUrl));
- }
- else {
- ZLog::Write(LOGLEVEL_WARN, sprintf("BackendCardDAV::getAddressbookFromId('%s') = %s", $addressbookId, 'not found'));
- }
- }
- else {
- ZLog::Write(LOGLEVEL_DEBUG, sprintf("BackendCardDAV::getAddressbookFromId('%s') = %s", $addressbookId, 'not initialized!'));
- }
-
- return $addressbookUrl;
- }
-
- /**
- * Get the zpush id of an addressbook.
- *
- * @param string $addressbookUrl AddressBook URL
- * @return id or false
- */
- private function getAddressbookFromUrl($addressbookUrl) {
- $this->InitializePermanentStorage();
-
- $addressbookId = false;
-
- if (isset($this->permanentStorage->fmAurlAid)) {
- if (isset($this->permanentStorage->fmAurlAid[$addressbookUrl])) {
- $addressbookId = $this->permanentStorage->fmAurlAid[$addressbookUrl];
- ZLog::Write(LOGLEVEL_DEBUG, sprintf("BackendCardDAV::getAddressbookFromUrl('%s') = %s", $addressbookUrl, $addressbookId));
- }
- else {
- ZLog::Write(LOGLEVEL_DEBUG, sprintf("BackendCardDAV::getAddressbookFromUrl('%s') = %s", $addressbookUrl, 'not found'));
- }
- }
- else {
- ZLog::Write(LOGLEVEL_DEBUG, sprintf("BackendCardDAV::getAddressbookFromUrl('%s') = %s", $addressbookUrl, 'not initialized!'));
- }
-
- return $addressbookId;
- }
-
-}
\ No newline at end of file
diff --git a/sources/backend/carddav/config.php b/sources/backend/carddav/config.php
deleted file mode 100644
index b82b5c9..0000000
--- a/sources/backend/carddav/config.php
+++ /dev/null
@@ -1,109 +0,0 @@
-.
-*
-* Consult LICENSE file for details
-************************************************/
-
-// ************************
-// BackendCardDAV settings
-// ************************
-
-// Server protocol: http or https
-define('CARDDAV_PROTOCOL', 'https');
-
-// Server name
-define('CARDDAV_SERVER', 'localhost');
-
-// Server port
-define('CARDDAV_PORT', '443');
-
-// Server path to the addressbook, or the principal with the addressbooks
-// If your user has more than 1 addressbook point it to the principal.
-// Example: user test@domain.com will have 2 addressbooks
-// http://localhost/caldav.php/test@domain.com/addresses/personal
-// http://localhost/caldav.php/test@domain.com/addresses/work
-// You set the CARDDAV_PATH to '/caldav.php/%u/addresses/' and personal and work will be autodiscovered
-// %u: replaced with the username
-// %d: replaced with the domain
-// Add the trailing /
-define('CARDDAV_PATH', '/caldav.php/%u/');
-
-
-// Server path to the default addressbook
-// Mobile device will create new contacts here. It must be under CARDDAV_PATH
-// %u: replaced with the username
-// %d: replaced with the domain
-// Add the trailing /
-define('CARDDAV_DEFAULT_PATH', '/caldav.php/%u/addresses/');
-
-// Server path to the GAL addressbook. This addressbook is readonly and searchable by the user, but it will NOT be synced.
-// If you don't want GAL, comment it
-// %u: replaced with the username
-// %d: replaced with the domain
-// Add the trailing /
-define('CARDDAV_GAL_PATH', '/caldav.php/%d/GAL/');
-
-// Minimal length for the search pattern to do the real search.
-define('CARDDAV_GAL_MIN_LENGTH', 5);
-
-// Addressbook display name, the name showed in the mobile device
-// %u: replaced with the username
-// %d: replaced with the domain
-define('CARDDAV_CONTACTS_FOLDER_NAME', '%u Addressbook');
-
-
-// If the CardDAV server supports the sync-collection operation
-// DAViCal and SabreDav support it, but Owncloud, SOGo don't
-// SabreDav version must be at least 1.9.0, otherwise set this to false
-// Setting this to false will work with most servers, but it will be slower: 1 petition for the href of vcards, and 1 petition for each vcard
-define('CARDDAV_SUPPORTS_SYNC', false);
-
-
-// If the CardDAV server supports the FN attribute for searches
-// DAViCal supports it, but SabreDav, Owncloud and SOGo don't
-// Setting this to true will search by FN. If false will search by sn, givenName and email
-// It's safe to leave it as false
-define('CARDDAV_SUPPORTS_FN_SEARCH', false);
-
-
-// If your carddav server needs to use file extension to recover a vcard.
-// Davical needs it
-// SOGo official demo online needs it, but some SOGo installation don't need it, so test it
-define('CARDDAV_URL_VCARD_EXTENSION', '.vcf');
\ No newline at end of file
diff --git a/sources/backend/combined/combined.php b/sources/backend/combined/combined.php
deleted file mode 100644
index 28d110e..0000000
--- a/sources/backend/combined/combined.php
+++ /dev/null
@@ -1,718 +0,0 @@
-.
-*
-* Consult LICENSE file for details
-************************************************/
-
-//include the CombinedBackend's own config file
-require_once("backend/combined/config.php");
-require_once("backend/combined/importer.php");
-require_once("backend/combined/exporter.php");
-
-class BackendCombined extends Backend implements ISearchProvider {
- public $config;
- /**
- * @var IBackend[]
- */
- public $backends;
- /**
- * @var IBackend
- */
- private $activeBackend;
- private $activeBackendID;
- private $numberChangesSink;
-
- private $logon_done = false;
-
- /**
- * Constructor of the combined backend
- *
- * @access public
- */
- public function BackendCombined() {
- parent::Backend();
- $this->config = BackendCombinedConfig::GetBackendCombinedConfig();
-
- $backend_values = array_unique(array_values($this->config['folderbackend']));
- foreach ($backend_values as $i) {
- ZPush::IncludeBackend($this->config['backends'][$i]['name']);
- $this->backends[$i] = new $this->config['backends'][$i]['name']();
- }
- ZLog::Write(LOGLEVEL_DEBUG, sprintf("Combined %d backends loaded.", count($this->backends)));
- }
-
- /**
- * Authenticates the user on each backend
- *
- * @param string $username
- * @param string $domain
- * @param string $password
- *
- * @access public
- * @return boolean
- */
- public function Logon($username, $domain, $password) {
- ZLog::Write(LOGLEVEL_DEBUG, sprintf("Combined->Logon('%s', '%s',***))", $username, $domain));
- if(!is_array($this->backends)){
- return false;
- }
- foreach ($this->backends as $i => $b){
- $u = $username;
- $d = $domain;
- $p = $password;
-
- // Apply mapping from configuration
- if(isset($this->config['backends'][$i]['users'])){
- if(!isset($this->config['backends'][$i]['users'][$username])){
- unset($this->backends[$i]);
- continue;
- }
- if(isset($this->config['backends'][$i]['users'][$username]['username']))
- $u = $this->config['backends'][$i]['users'][$username]['username'];
- if(isset($this->config['backends'][$i]['users'][$username]['password']))
- $p = $this->config['backends'][$i]['users'][$username]['password'];
- if(isset($this->config['backends'][$i]['users'][$username]['domain']))
- $d = $this->config['backends'][$i]['users'][$username]['domain'];
- }
-
- // Apply username mapping from state backend
- if (isset($this->config['usemapping']) && $this->config['usemapping']) {
- $mappedUsername = ZPush::GetStateMachine()->GetMappedUsername($u, strtolower($this->config['backends'][$i]['name']));
- if ($mappedUsername !== null) {
- $u = $mappedUsername;
- }
- }
-
- if ($this->backends[$i]->Logon($u, $d, $p) == false) {
- ZLog::Write(LOGLEVEL_DEBUG, sprintf("Combined->Logon() failed on %s ", $this->config['backends'][$i]['name']));
- return false;
- }
- $this->backends[$i]->SetOriginalUsername($username);
- }
-
- $this->logon_done = true;
- ZLog::Write(LOGLEVEL_DEBUG, "Combined->Logon() success");
- return true;
- }
-
- /**
- * Setup the backend to work on a specific store or checks ACLs there.
- * If only the $store is submitted, all Import/Export/Fetch/Etc operations should be
- * performed on this store (switch operations store).
- * If the ACL check is enabled, this operation should just indicate the ACL status on
- * the submitted store, without changing the store for operations.
- * For the ACL status, the currently logged on user MUST have access rights on
- * - the entire store - admin access if no folderid is sent, or
- * - on a specific folderid in the store (secretary/full access rights)
- *
- * The ACLcheck MUST fail if a folder of the authenticated user is checked!
- *
- * @param string $store target store, could contain a "domain\user" value
- * @param boolean $checkACLonly if set to true, Setup() should just check ACLs
- * @param string $folderid if set, only ACLs on this folderid are relevant
- *
- * @access public
- * @return boolean
- */
- public function Setup($store, $checkACLonly = false, $folderid = false) {
- ZLog::Write(LOGLEVEL_DEBUG, sprintf("Combined->Setup('%s', '%s', '%s')", $store, Utils::PrintAsString($checkACLonly), $folderid));
- if(!is_array($this->backends)){
- return false;
- }
- foreach ($this->backends as $i => $b){
- $u = $store;
- if(isset($this->config['backends'][$i]['users']) && isset($this->config['backends'][$i]['users'][$store]['username'])){
- $u = $this->config['backends'][$i]['users'][$store]['username'];
- }
- if($this->backends[$i]->Setup($u, $checkACLonly, $folderid) == false){
- ZLog::Write(LOGLEVEL_WARN, "Combined->Setup() failed");
- return false;
- }
- }
- ZLog::Write(LOGLEVEL_DEBUG, "Combined->Setup() success");
- return true;
- }
-
- /**
- * Logs off each backend
- *
- * @access public
- * @return boolean
- */
- public function Logoff() {
- // If no Logon in done, omit Logoff
- if (!$this->logon_done)
- return true;
-
- ZLog::Write(LOGLEVEL_DEBUG, "Combined->Logoff()");
- foreach ($this->backends as $i => $b){
- $this->backends[$i]->Logoff();
- }
- ZLog::Write(LOGLEVEL_DEBUG, "Combined->Logoff() success");
- return true;
- }
-
- /**
- * Returns an array of SyncFolder types with the entire folder hierarchy
- * from all backends combined
- *
- * provides AS 1.0 compatibility
- *
- * @access public
- * @return array SYNC_FOLDER
- */
- public function GetHierarchy(){
- ZLog::Write(LOGLEVEL_DEBUG, "Combined->GetHierarchy()");
- $ha = array();
- foreach ($this->backends as $i => $b){
- if(!empty($this->config['backends'][$i]['subfolder'])){
- $f = new SyncFolder();
- $f->serverid = $i.$this->config['delimiter'].'0';
- $f->parentid = '0';
- $f->displayname = $this->config['backends'][$i]['subfolder'];
- $f->type = SYNC_FOLDER_TYPE_OTHER;
- $ha[] = $f;
- }
- $h = $this->backends[$i]->GetHierarchy();
- if(is_array($h)){
- foreach($h as $j => $f){
- $h[$j]->serverid = $i.$this->config['delimiter'].$h[$j]->serverid;
- if($h[$j]->parentid != '0' || !empty($this->config['backends'][$i]['subfolder'])){
- $h[$j]->parentid = $i.$this->config['delimiter'].$h[$j]->parentid;
- }
- if(isset($this->config['folderbackend'][$h[$j]->type]) && $this->config['folderbackend'][$h[$j]->type] != $i){
- $h[$j]->type = SYNC_FOLDER_TYPE_OTHER;
- }
- }
- $ha = array_merge($ha, $h);
- }
- }
- ZLog::Write(LOGLEVEL_DEBUG, "Combined->GetHierarchy() success");
- return $ha;
- }
-
- /**
- * Returns the importer to process changes from the mobile
- *
- * @param string $folderid (opt)
- *
- * @access public
- * @return object(ImportChanges)
- */
- public function GetImporter($folderid = false) {
- if($folderid !== false) {
- ZLog::Write(LOGLEVEL_DEBUG, sprintf("Combined->GetImporter() Content: ImportChangesCombined:('%s')", $folderid));
-
- // get the contents importer from the folder in a backend
- // the importer is wrapped to check foldernames in the ImportMessageMove function
- $backend = $this->GetBackend($folderid);
- if($backend === false)
- return false;
- $importer = $backend->GetImporter($this->GetBackendFolder($folderid));
- if($importer){
- return new ImportChangesCombined($this, $folderid, $importer);
- }
- return false;
- }
- else {
- ZLog::Write(LOGLEVEL_DEBUG, "Combined->GetImporter() -> Hierarchy: ImportChangesCombined()");
- //return our own hierarchy importer which send each change to the right backend
- return new ImportChangesCombined($this);
- }
- }
-
- /**
- * Returns the exporter to send changes to the mobile
- * the exporter from right backend for contents exporter and our own exporter for hierarchy exporter
- *
- * @param string $folderid (opt)
- *
- * @access public
- * @return object(ExportChanges)
- */
- public function GetExporter($folderid = false){
- ZLog::Write(LOGLEVEL_DEBUG, sprintf("Combined->GetExporter('%s')", $folderid));
- if($folderid){
- $backend = $this->GetBackend($folderid);
- if($backend == false)
- return false;
- return $backend->GetExporter($this->GetBackendFolder($folderid));
- }
- return new ExportChangesCombined($this);
- }
-
- /**
- * Sends an e-mail
- * This messages needs to be saved into the 'sent items' folder
- *
- * @param SyncSendMail $sm SyncSendMail object
- *
- * @access public
- * @return boolean
- * @throws StatusException
- */
- public function SendMail($sm) {
- ZLog::Write(LOGLEVEL_DEBUG, "Combined->SendMail()");
- // Convert source folderid
- if (isset($sm->source->folderid)) {
- $sm->source->folderid = $this->GetBackendFolder($sm->source->folderid);
- }
- foreach ($this->backends as $i => $b){
- if($this->backends[$i]->SendMail($sm) == true){
- return true;
- }
- }
- return false;
- }
-
- /**
- * Returns all available data of a single message
- *
- * @param string $folderid
- * @param string $id
- * @param ContentParameters $contentparameters flag
- *
- * @access public
- * @return object(SyncObject)
- * @throws StatusException
- */
- public function Fetch($folderid, $id, $contentparameters) {
- ZLog::Write(LOGLEVEL_DEBUG, sprintf("Combined->Fetch('%s', '%s', CPO)", $folderid, $id));
- $backend = $this->GetBackend($folderid);
- if($backend == false)
- return false;
- return $backend->Fetch($this->GetBackendFolder($folderid), $id, $contentparameters);
- }
-
- /**
- * Returns the waste basket
- * If the wastebasket is set to one backend, return the wastebasket of that backend
- * else return the first waste basket we can find
- *
- * @access public
- * @return string
- */
- function GetWasteBasket(){
- ZLog::Write(LOGLEVEL_DEBUG, "Combined->GetWasteBasket()");
-
- if (isset($this->activeBackend)) {
- if (!$this->activeBackend->GetWasteBasket())
- return false;
- else
- return $this->activeBackendID . $this->config['delimiter'] . $this->activeBackend->GetWasteBasket();
- }
-
- return false;
- }
-
- /**
- * Returns the content of the named attachment as stream.
- * There is no way to tell which backend the attachment is from, so we try them all
- *
- * @param string $attname
- *
- * @access public
- * @return SyncItemOperationsAttachment
- * @throws StatusException
- */
- public function GetAttachmentData($attname) {
- ZLog::Write(LOGLEVEL_DEBUG, sprintf("Combined->GetAttachmentData('%s')", $attname));
- foreach ($this->backends as $i => $b) {
- try {
- $attachment = $this->backends[$i]->GetAttachmentData($attname);
- if ($attachment instanceof SyncItemOperationsAttachment)
- return $attachment;
- }
- catch (StatusException $s) {
- // backends might throw StatusExceptions if it's not their attachment
- }
- }
- throw new StatusException("Combined->GetAttachmentData(): no backend found", SYNC_ITEMOPERATIONSSTATUS_INVALIDATT);
- }
-
- /**
- * Processes a response to a meeting request.
- *
- * @param string $requestid id of the object containing the request
- * @param string $folderid id of the parent folder of $requestid
- * @param string $response
- *
- * @access public
- * @return string id of the created/updated calendar obj
- * @throws StatusException
- */
- public function MeetingResponse($requestid, $folderid, $response) {
- $backend = $this->GetBackend($folderid);
- if($backend === false)
- return false;
- return $backend->MeetingResponse($requestid, $this->GetBackendFolder($folderid), $response);
- }
-
- /**
- * Resolves recipients
- *
- * @param SyncObject $resolveRecipients
- *
- * @access public
- * @return SyncObject $resolveRecipients
- */
- public function ResolveRecipients($resolveRecipients) {
- // TODO:
- return false;
- }
-
-
- /**
- * Deletes all contents of the specified folder.
- * This is generally used to empty the trash (wastebasked), but could also be used on any
- * other folder.
- *
- * @param string $folderid
- * @param boolean $includeSubfolders (opt) also delete sub folders, default true
- *
- * @access public
- * @return boolean
- * @throws StatusException
- */
- public function EmptyFolder($folderid, $includeSubfolders = true) {
- $backend = $this->GetBackend($folderid);
- if($backend === false)
- return false;
- return $backend->EmptyFolder($this->GetBackendFolder($folderid), $includeSubfolders);
- }
-
- /**
- * Indicates if the backend has a ChangesSink.
- * A sink is an active notification mechanism which does not need polling.
- *
- * @access public
- * @return boolean
- */
- public function HasChangesSink() {
- ZLog::Write(LOGLEVEL_DEBUG, sprintf("BackendCombined->HasChangesSink()"));
-
- $this->numberChangesSink = 0;
-
- foreach ($this->backends as $i => $b) {
- if ($this->backends[$i]->HasChangesSink()) {
- $this->numberChangesSink++;
- }
- }
-
- ZLog::Write(LOGLEVEL_DEBUG, sprintf("BackendCombined->HasChangesSink - Number ChangesSink found: %d", $this->numberChangesSink));
-
- return true;
- }
-
- /**
- * The folder should be considered by the sink.
- * Folders which were not initialized should not result in a notification
- * of IBacken->ChangesSink().
- *
- * @param string $folderid
- *
- * @access public
- * @return boolean false if there is any problem with that folder
- */
- public function ChangesSinkInitialize($folderid) {
- ZLog::Write(LOGLEVEL_DEBUG, sprintf("BackendCombined->ChangesSinkInitialize('%s')", $folderid));
-
- $backend = $this->GetBackend($folderid);
- if($backend === false) {
- // if not backend is found we return true, we don't want this to cause an error
- return true;
- }
-
- if ($backend->HasChangesSink()) {
- ZLog::Write(LOGLEVEL_DEBUG, sprintf("BackendCombined->ChangesSinkInitialize('%s') is supported, initializing", $folderid));
- return $backend->ChangesSinkInitialize($this->GetBackendFolder($folderid));
- }
-
- // if the backend doesn't support ChangesSink, we also return true so we don't get an error
- return true;
- }
-
- /**
- * The actual ChangesSink.
- * For max. the $timeout value this method should block and if no changes
- * are available return an empty array.
- * If changes are available a list of folderids is expected.
- *
- * @param int $timeout max. amount of seconds to block
- *
- * @access public
- * @return array
- */
- public function ChangesSink($timeout = 30) {
- ZLog::Write(LOGLEVEL_DEBUG, sprintf("BackendCombined->ChangesSink(%d)", $timeout));
-
- $notifications = array();
- if ($this->numberChangesSink == 0) {
- ZLog::Write(LOGLEVEL_DEBUG, "BackendCombined doesn't include any Sinkable backends");
- } else {
- $time_each = $timeout / $this->numberChangesSink;
- foreach ($this->backends as $i => $b) {
- if ($this->backends[$i]->HasChangesSink()) {
- ZLog::Write(LOGLEVEL_DEBUG, sprintf("BackendCombined->ChangesSink - Calling in '%s' with %d", get_class($b), $time_each));
-
- $notifications_backend = $this->backends[$i]->ChangesSink($time_each);
- //preppend backend delimiter
- for ($c = 0; $c < count($notifications_backend); $c++) {
- $notifications_backend[$c] = $i . $this->config['delimiter'] . $notifications_backend[$c];
- }
- $notifications = array_merge($notifications, $notifications_backend);
- }
- }
- }
-
- return $notifications;
- }
-
- /**
- * Finds the correct backend for a folder
- *
- * @param string $folderid combinedid of the folder
- *
- * @access public
- * @return object
- */
- public function GetBackend($folderid){
- $pos = strpos($folderid, $this->config['delimiter']);
- if($pos === false)
- return false;
- $id = substr($folderid, 0, $pos);
- if(!isset($this->backends[$id]))
- return false;
-
- $this->activeBackend = $this->backends[$id];
- $this->activeBackendID = $id;
- return $this->backends[$id];
- }
-
- /**
- * Returns an understandable folderid for the backend
- *
- * @param string $folderid combinedid of the folder
- *
- * @access public
- * @return string
- */
- public function GetBackendFolder($folderid){
- $pos = strpos($folderid, $this->config['delimiter']);
- if($pos === false)
- return false;
- return substr($folderid,$pos + strlen($this->config['delimiter']));
- }
-
- /**
- * Returns backend id for a folder
- *
- * @param string $folderid combinedid of the folder
- *
- * @access public
- * @return object
- */
- public function GetBackendId($folderid){
- $pos = strpos($folderid, $this->config['delimiter']);
- if($pos === false)
- return false;
- return substr($folderid, 0, $pos);
- }
-
- /**
- * Indicates which AS version is supported by the backend.
- * Return the lowest version supported by the backends used.
- *
- * @access public
- * @return string AS version constant
- */
- public function GetSupportedASVersion() {
- $version = ZPush::ASV_14;
- foreach ($this->backends as $i => $b) {
- $subversion = $this->backends[$i]->GetSupportedASVersion();
- if ($subversion < $version) {
- $version = $subversion;
- }
- }
- return $version;
- }
-
- /**
- * Returns the BackendCombined as it implements the ISearchProvider interface
- * This could be overwritten by the global configuration
- *
- * @access public
- * @return object Implementation of ISearchProvider
- */
- public function GetSearchProvider() {
- return $this;
- }
-
-
- /*-----------------------------------------------------------------------------------------
- -- ISearchProvider
- ------------------------------------------------------------------------------------------*/
- /**
- * Indicates if a search type is supported by this SearchProvider
- * It supports all the search types, searches are delegated.
- *
- * @param string $searchtype
- *
- * @access public
- * @return boolean
- */
- public function SupportsType($searchtype) {
- ZLog::Write(LOGLEVEL_DEBUG, sprintf("Combined->SupportsType('%s')", $searchtype));
- $i = $this->getSearchBackend($searchtype);
-
- return $i !== false;
- }
-
-
- /**
- * Queries the LDAP backend
- *
- * @param string $searchquery string to be searched for
- * @param string $searchrange specified searchrange
- *
- * @access public
- * @return array search results
- */
- public function GetGALSearchResults($searchquery, $searchrange) {
- ZLog::Write(LOGLEVEL_DEBUG, "Combined->GetGALSearchResults()");
- $i = $this->getSearchBackend(ISearchProvider::SEARCH_GAL);
-
- $result = false;
- if ($i !== false) {
- $result = $this->backends[$i]->GetGALSearchResults($searchquery, $searchrange);
- }
-
- return $result;
- }
-
-
- /**
- * Searches for the emails on the server
- *
- * @param ContentParameter $cpo
- *
- * @return array
- */
- public function GetMailboxSearchResults($cpo) {
- ZLog::Write(LOGLEVEL_DEBUG, "Combined->GetMailboxSearchResults()");
- $i = $this->getSearchBackend(ISearchProvider::SEARCH_MAILBOX);
-
- $result = false;
- if ($i !== false) {
- //Convert $cpo GetSearchFolderid
- $cpo->SetSearchFolderid($this->GetBackendFolder($cpo->GetSearchFolderid()));
- $result = $this->backends[$i]->GetMailboxSearchResults($cpo, $i . $this->config['delimiter']);
- }
-
- return $result;
- }
-
-
- /**
- * Terminates a search for a given PID
- *
- * @param int $pid
- *
- * @return boolean
- */
- public function TerminateSearch($pid) {
- ZLog::Write(LOGLEVEL_DEBUG, "Combined->TerminateSearch()");
- foreach ($this->backends as $i => $b) {
- if ($this->backends[$i] instanceof ISearchProvider) {
- $this->backends[$i]->TerminateSearch($pid);
- }
- }
-
- return true;
- }
-
-
- /**
- * Disconnects backends
- *
- * @access public
- * @return boolean
- */
- public function Disconnect() {
- ZLog::Write(LOGLEVEL_DEBUG, "Combined->Disconnect()");
- foreach ($this->backends as $i => $b) {
- if ($this->backends[$i] instanceof ISearchProvider) {
- $this->backends[$i]->Disconnect();
- }
- }
-
- return true;
- }
-
-
- /**
- * Returns the first backend that support a search type
- *
- * @param string $searchtype
- *
- * @access private
- * @return string
- */
- private function getSearchBackend($searchtype) {
- foreach ($this->backends as $i => $b) {
- if ($this->backends[$i] instanceof ISearchProvider) {
- if ($this->backends[$i]->SupportsType($searchtype)) {
- return $i;
- }
- }
- }
- ZLog::Write(LOGLEVEL_DEBUG, sprintf("Combined->getSearchBackend('%s') No support found!", $searchtype));
-
- return false;
- }
-}
diff --git a/sources/backend/combined/config.php b/sources/backend/combined/config.php
deleted file mode 100644
index ca6bd16..0000000
--- a/sources/backend/combined/config.php
+++ /dev/null
@@ -1,116 +0,0 @@
-.
-*
-* Consult LICENSE file for details
-************************************************/
-
-class BackendCombinedConfig {
-
- // *************************
- // BackendCombined settings
- // *************************
- /**
- * Returns the configuration of the combined backend
- *
- * @access public
- * @return array
- *
- */
- public static function GetBackendCombinedConfig() {
- //use a function for it because php does not allow
- //assigning variables to the class members (expecting T_STRING)
- return array(
- //the order in which the backends are loaded.
- //login only succeeds if all backend return true on login
- //sending mail: the mail is sent with first backend that is able to send the mail
- 'backends' => array(
- 'i' => array(
- 'name' => 'BackendIMAP',
- ),
- 'z' => array(
- 'name' => 'BackendZarafa',
- ),
- 'm' => array(
- 'name' => 'BackendMaildir',
- ),
- 'v' => array(
- 'name' => 'BackendVCardDir',
- ),
- 'c' => array(
- 'name' => 'BackendCalDAV',
- ),
- 'l' => array(
- 'name' => 'BackendLDAP',
- ),
- 'd' => array(
- 'name' => 'BackendCardDAV',
- ),
- ),
- 'delimiter' => '/',
- //force one type of folder to one backend
- //it must match one of the above defined backends
- 'folderbackend' => array(
- SYNC_FOLDER_TYPE_INBOX => 'i',
- SYNC_FOLDER_TYPE_DRAFTS => 'i',
- SYNC_FOLDER_TYPE_WASTEBASKET => 'i',
- SYNC_FOLDER_TYPE_SENTMAIL => 'i',
- SYNC_FOLDER_TYPE_OUTBOX => 'i',
- SYNC_FOLDER_TYPE_TASK => 'z',
- SYNC_FOLDER_TYPE_APPOINTMENT => 'z',
- SYNC_FOLDER_TYPE_CONTACT => 'z',
- SYNC_FOLDER_TYPE_NOTE => 'z',
- SYNC_FOLDER_TYPE_JOURNAL => 'z',
- SYNC_FOLDER_TYPE_OTHER => 'i',
- SYNC_FOLDER_TYPE_USER_MAIL => 'i',
- SYNC_FOLDER_TYPE_USER_APPOINTMENT => 'z',
- SYNC_FOLDER_TYPE_USER_CONTACT => 'z',
- SYNC_FOLDER_TYPE_USER_TASK => 'z',
- SYNC_FOLDER_TYPE_USER_JOURNAL => 'z',
- SYNC_FOLDER_TYPE_USER_NOTE => 'z',
- SYNC_FOLDER_TYPE_UNKNOWN => 'z',
- ),
- //creating a new folder in the root folder should create a folder in one backend
- 'rootcreatefolderbackend' => 'i',
- //enable to use username mapping for the different backends
- 'usemapping' => false,
- );
- }
-}
diff --git a/sources/backend/combined/exporter.php b/sources/backend/combined/exporter.php
deleted file mode 100644
index eb1abbd..0000000
--- a/sources/backend/combined/exporter.php
+++ /dev/null
@@ -1,189 +0,0 @@
-.
-*
-* Consult LICENSE file for details
-************************************************/
-
-/**
- * the ExportChangesCombined class is returned from GetExporter for changes.
- * It combines the changes from all backends and prepends all folderids with the backendid
- */
-
-class ExportChangesCombined implements IExportChanges {
- /**
- * @var BackendCombined
- */
- private $backend;
- private $syncstates;
- /**
- * @var IExportChanges[]
- */
- private $exporters;
- private $importer;
- private $importwraps;
-
- public function ExportChangesCombined(&$backend) {
- $this->backend =& $backend;
- $this->exporters = array();
- ZLog::Write(LOGLEVEL_DEBUG, "ExportChangesCombined constructed");
- }
-
- /**
- * Initializes the state and flags
- *
- * @param string $state
- * @param int $flags
- *
- * @access public
- * @return boolean status flag
- */
- public function Config($syncstate, $flags = 0) {
- ZLog::Write(LOGLEVEL_DEBUG, "ExportChangesCombined->Config(...)");
- $this->syncstates = $syncstate;
- if(!is_array($this->syncstates)){
- $this->syncstates = array();
- }
-
- foreach($this->backend->backends as $i => $b){
- if(isset($this->syncstates[$i])){
- $state = $this->syncstates[$i];
- } else {
- $state = '';
- }
-
- $this->exporters[$i] = $this->backend->backends[$i]->GetExporter();
- $this->exporters[$i]->Config($state, $flags);
- }
- ZLog::Write(LOGLEVEL_DEBUG, "ExportChangesCombined->Config() success");
- }
-
- /**
- * Returns the amount of changes to be exported
- *
- * @access public
- * @return int
- */
- public function GetChangeCount() {
- ZLog::Write(LOGLEVEL_DEBUG, "ExportChangesCombined->GetChangeCount()");
- $c = 0;
- foreach($this->exporters as $i => $e){
- $c += $this->exporters[$i]->GetChangeCount();
- }
- ZLog::Write(LOGLEVEL_DEBUG, "ExportChangesCombined->GetChangeCount() success");
- return $c;
- }
-
- /**
- * Synchronizes a change to the configured importer
- *
- * @access public
- * @return array with status information
- */
- public function Synchronize() {
- ZLog::Write(LOGLEVEL_DEBUG, "ExportChangesCombined->Synchronize()");
- foreach($this->exporters as $i => $e){
- if(!empty($this->backend->config['backends'][$i]['subfolder']) && !isset($this->syncstates[$i])){
- // first sync and subfolder backend
- $f = new SyncFolder();
- $f->serverid = $i.$this->backend->config['delimiter'].'0';
- $f->parentid = '0';
- $f->displayname = $this->backend->config['backends'][$i]['subfolder'];
- $f->type = SYNC_FOLDER_TYPE_OTHER;
- $this->importer->ImportFolderChange($f);
- }
- while(is_array($this->exporters[$i]->Synchronize()));
- }
- ZLog::Write(LOGLEVEL_DEBUG, "ExportChangesCombined->Synchronize() success");
- return true;
- }
-
- /**
- * Reads and returns the current state
- *
- * @access public
- * @return string
- */
- public function GetState() {
- ZLog::Write(LOGLEVEL_DEBUG, "ExportChangesCombined->GetState()");
- foreach($this->exporters as $i => $e){
- $this->syncstates[$i] = $this->exporters[$i]->GetState();
- }
- ZLog::Write(LOGLEVEL_DEBUG, "ExportChangesCombined->GetState() success");
- return $this->syncstates;
- }
-
- /**
- * Configures additional parameters used for content synchronization
- *
- * @param ContentParameters $contentparameters
- *
- * @access public
- * @return boolean
- * @throws StatusException
- */
- public function ConfigContentParameters($contentparameters) {
- ZLog::Write(LOGLEVEL_DEBUG, "ExportChangesCombined->ConfigContentParameters()");
- foreach($this->exporters as $i => $e){
- //call the ConfigContentParameters() of each exporter
- $e->ConfigContentParameters($contentparameters);
- }
- ZLog::Write(LOGLEVEL_DEBUG, "ExportChangesCombined->ConfigContentParameters() success");
- }
-
- /**
- * Sets the importer where the exporter will sent its changes to
- * This exporter should also be ready to accept calls after this
- *
- * @param object &$importer Implementation of IImportChanges
- *
- * @access public
- * @return boolean
- */
- public function InitializeExporter(&$importer) {
- ZLog::Write(LOGLEVEL_DEBUG, "ExportChangesCombined->InitializeExporter(...)");
- foreach ($this->exporters as $i => $e) {
- if(!isset($this->importwraps[$i])){
- $this->importwraps[$i] = new ImportHierarchyChangesCombinedWrap($i, $this->backend, $importer);
- }
- $e->InitializeExporter($this->importwraps[$i]);
- }
- ZLog::Write(LOGLEVEL_DEBUG, "ExportChangesCombined->InitializeExporter(...) success");
- }
-}
diff --git a/sources/backend/combined/importer.php b/sources/backend/combined/importer.php
deleted file mode 100644
index 1c01826..0000000
--- a/sources/backend/combined/importer.php
+++ /dev/null
@@ -1,360 +0,0 @@
-.
-*
-* Consult LICENSE file for details
-************************************************/
-
-class ImportChangesCombined implements IImportChanges {
- private $backend;
- private $folderid;
- private $icc;
-
- /**
- * Constructor of the ImportChangesCombined class
- *
- * @param object $backend
- * @param string $folderid
- * @param object $importer
- *
- * @access public
- */
- public function ImportChangesCombined(&$backend, $folderid = false, $icc = false) {
- $this->backend = $backend;
- $this->folderid = $folderid;
- $this->icc = &$icc;
- }
-
- /**
- * Loads objects which are expected to be exported with the state
- * Before importing/saving the actual message from the mobile, a conflict detection should be done
- *
- * @param ContentParameters $contentparameters class of objects
- * @param string $state
- *
- * @access public
- * @return boolean
- * @throws StatusException
- */
- public function LoadConflicts($contentparameters, $state) {
- if (!$this->icc) {
- ZLog::Write(LOGLEVEL_ERROR, "ImportChangesCombined->LoadConflicts() icc not configured");
- return false;
- }
- $this->icc->LoadConflicts($contentparameters, $state);
- }
-
- /**
- * Imports a single message
- *
- * @param string $id
- * @param SyncObject $message
- *
- * @access public
- * @return boolean/string failure / id of message
- */
- public function ImportMessageChange($id, $message) {
- if (!$this->icc) {
- ZLog::Write(LOGLEVEL_ERROR, "ImportChangesCombined->ImportMessageChange() icc not configured");
- return false;
- }
- return $this->icc->ImportMessageChange($id, $message);
- }
-
- /**
- * Imports a deletion. This may conflict if the local object has been modified
- *
- * @param string $id
- *
- * @access public
- * @return boolean
- */
- public function ImportMessageDeletion($id) {
- if (!$this->icc) {
- ZLog::Write(LOGLEVEL_ERROR, "ImportChangesCombined->ImportMessageDeletion() icc not configured");
- return false;
- }
- return $this->icc->ImportMessageDeletion($id);
- }
-
- /**
- * Imports a change in 'read' flag
- * This can never conflict
- *
- * @param string $id
- * @param int $flags
- *
- * @access public
- * @return boolean
- */
- public function ImportMessageReadFlag($id, $flags) {
- if (!$this->icc) {
- ZLog::Write(LOGLEVEL_ERROR, "ImportChangesCombined->ImportMessageReadFlag() icc not configured");
- return false;
- }
- return $this->icc->ImportMessageReadFlag($id, $flags);
- }
-
-
- /**
- * Imports a move of a message. This occurs when a user moves an item to another folder
- *
- * @param string $id
- * @param string $newfolder
- *
- * @access public
- * @return boolean
- */
- public function ImportMessageMove($id, $newfolder) {
- ZLog::Write(LOGLEVEL_DEBUG, sprintf("ImportChangesCombined->ImportMessageMove('%s', '%s')", $id, $newfolder));
- if (!$this->icc) {
- ZLog::Write(LOGLEVEL_ERROR, "ImportChangesCombined->ImportMessageMove icc not configured");
- return false;
- }
- if($this->backend->GetBackendId($this->folderid) != $this->backend->GetBackendId($newfolder)){
- ZLog::Write(LOGLEVEL_WARN, "ImportChangesCombined->ImportMessageMove() cannot move message between two backends");
- return false;
- }
- $res = $this->icc->ImportMessageMove($id, $this->backend->GetBackendFolder($newfolder));
-
- if ($res) {
- //TODO: we should add newid to new folder, instead of a full folder resync
- ZLog::Write(LOGLEVEL_DEBUG, sprintf("ImportChangesCombined->ImportMessageMove(): Force resync of dest folder (%s)", $newfolder));
- ZPushAdmin::ResyncFolder(Request::GetAuthUser(), Request::GetDeviceID(), $newfolder);
- }
-
- return $res;
- }
-
-
- /**----------------------------------------------------------------------------------------------------------
- * Methods to import hierarchy
- */
-
- /**
- * Imports a change on a folder
- *
- * @param object $folder SyncFolder
- *
- * @access public
- * @return boolean/string status/id of the folder
- */
- public function ImportFolderChange($folder) {
- $id = $folder->serverid;
- $parent = $folder->parentid;
- ZLog::Write(LOGLEVEL_DEBUG, sprintf("ImportChangesCombined->ImportFolderChange() id: '%s', parent: '%s'", $id, $parent));
- if($parent == '0') {
- if($id) {
- $backendid = $this->backend->GetBackendId($id);
- }
- else {
- $backendid = $this->backend->config['rootcreatefolderbackend'];
- }
- }
- else {
- $backendid = $this->backend->GetBackendId($parent);
- $parent = $this->backend->GetBackendFolder($parent);
- }
-
- if(!empty($this->backend->config['backends'][$backendid]['subfolder']) && $id == $backendid.$this->backend->config['delimiter'].'0') {
- ZLog::Write(LOGLEVEL_WARN, "ImportChangesCombined->ImportFolderChange() cannot change static folder");
- return false;
- }
-
- if($id != false) {
- if($backendid != $this->backend->GetBackendId($id)) {
- ZLog::Write(LOGLEVEL_WARN, "ImportChangesCombined->ImportFolderChange() cannot move folder between two backends");
- return false;
- }
- $id = $this->backend->GetBackendFolder($id);
- }
-
- $this->icc = $this->backend->getBackend($backendid)->GetImporter();
- $res = $this->icc->ImportFolderChange($folder);
- ZLog::Write(LOGLEVEL_DEBUG, 'ImportChangesCombined->ImportFolderChange() success');
- return $backendid.$this->backend->config['delimiter'].$res;
- }
-
- /**
- * Imports a folder deletion
- *
- * @param string $id
- * @param string $parent id
- *
- * @access public
- * @return boolean/int success/SYNC_FOLDERHIERARCHY_STATUS
- */
- public function ImportFolderDeletion($id, $parent = false) {
- ZLog::Write(LOGLEVEL_DEBUG, sprintf("ImportChangesCombined->ImportFolderDeletion('%s', '%s'), $id, $parent"));
- $backendid = $this->backend->GetBackendId($id);
- if(!empty($this->backend->config['backends'][$backendid]['subfolder']) && $id == $backendid.$this->backend->config['delimiter'].'0') {
- ZLog::Write(LOGLEVEL_WARN, "ImportChangesCombined->ImportFolderDeletion() cannot change static folder");
- return false; //we can not change a static subfolder
- }
-
- $backend = $this->backend->GetBackend($id);
- $id = $this->backend->GetBackendFolder($id);
-
- if($parent != '0')
- $parent = $this->backend->GetBackendFolder($parent);
-
- $this->icc = $backend->GetImporter();
- $res = $this->icc->ImportFolderDeletion($id, $parent);
- ZLog::Write(LOGLEVEL_DEBUG, 'ImportChangesCombined->ImportFolderDeletion() success');
- return $res;
- }
-
-
- /**
- * Initializes the state and flags
- *
- * @param string $state
- * @param int $flags
- *
- * @access public
- * @return boolean status flag
- */
- public function Config($state, $flags = 0) {
- ZLog::Write(LOGLEVEL_DEBUG, 'ImportChangesCombined->Config(...)');
- if (!$this->icc) {
- ZLog::Write(LOGLEVEL_ERROR, "ImportChangesCombined->Config() icc not configured");
- return false;
- }
- $this->icc->Config($state, $flags);
- ZLog::Write(LOGLEVEL_DEBUG, 'ImportChangesCombined->Config() success');
- }
-
-
- /**
- * Configures additional parameters used for content synchronization
- *
- * @param ContentParameters $contentparameters
- *
- * @access public
- * @return boolean
- * @throws StatusException
- */
- public function ConfigContentParameters($contentparameters) {
- ZLog::Write(LOGLEVEL_DEBUG, "ImportChangesCombined->ConfigContentParameters()");
- if (!$this->icc) {
- ZLog::Write(LOGLEVEL_ERROR, "ImportChangesCombined->ConfigContentParameters() icc not configured");
- return false;
- }
- $this->icc->ConfigContentParameters($contentparameters);
- ZLog::Write(LOGLEVEL_DEBUG, "ImportChangesCombined->ConfigContentParameters() success");
- }
-
- /**
- * Reads and returns the current state
- *
- * @access public
- * @return string
- */
- public function GetState() {
- if (!$this->icc) {
- ZLog::Write(LOGLEVEL_ERROR, "ImportChangesCombined->GetState() icc not configured");
- return false;
- }
- return $this->icc->GetState();
- }
-}
-
-
-/**
- * The ImportHierarchyChangesCombinedWrap class wraps the importer given in ExportChangesCombined->Config.
- * It prepends the backendid to all folderids and checks foldertypes.
- */
-
-class ImportHierarchyChangesCombinedWrap {
- private $ihc;
- private $backend;
- private $backendid;
-
- /**
- * Constructor of the ImportChangesCombined class
- *
- * @param string $backendid
- * @param object $backend
- * @param object $ihc
- *
- * @access public
- */
- public function ImportHierarchyChangesCombinedWrap($backendid, &$backend, &$ihc) {
- ZLog::Write(LOGLEVEL_DEBUG, "ImportHierarchyChangesCombinedWrap->ImportHierarchyChangesCombinedWrap('$backendid',...)");
- $this->backendid = $backendid;
- $this->backend =& $backend;
- $this->ihc = &$ihc;
- }
-
- /**
- * Imports a change on a folder
- *
- * @param object $folder SyncFolder
- *
- * @access public
- * @return boolean/string status/id of the folder
- */
- public function ImportFolderChange($folder) {
- ZLog::Write(LOGLEVEL_DEBUG, sprintf("ImportHierarchyChangesCombinedWrap->ImportFolderChange('%s')", $folder->serverid));
- $folder->serverid = $this->backendid.$this->backend->config['delimiter'].$folder->serverid;
- if($folder->parentid != '0' || !empty($this->backend->config['backends'][$this->backendid]['subfolder'])){
- $folder->parentid = $this->backendid.$this->backend->config['delimiter'].$folder->parentid;
- }
- if(isset($this->backend->config['folderbackend'][$folder->type]) && $this->backend->config['folderbackend'][$folder->type] != $this->backendid){
- ZLog::Write(LOGLEVEL_DEBUG, sprintf("not using folder: '%s' ('%s')", $folder->displayname, $folder->serverid));
- return true;
- }
- ZLog::Write(LOGLEVEL_DEBUG, "ImportHierarchyChangesCombinedWrap->ImportFolderChange() success");
- return $this->ihc->ImportFolderChange($folder);
- }
-
- /**
- * Imports a folder deletion
- *
- * @param string $id
- *
- * @access public
- *
- * @return boolean/int success/SYNC_FOLDERHIERARCHY_STATUS
- */
- public function ImportFolderDeletion($id) {
- ZLog::Write(LOGLEVEL_DEBUG, sprintf("ImportHierarchyChangesCombinedWrap->ImportFolderDeletion('%s')", $id));
- return $this->ihc->ImportFolderDeletion($this->backendid.$this->backend->config['delimiter'].$id);
- }
-}
diff --git a/sources/backend/imap/README b/sources/backend/imap/README
deleted file mode 100644
index ab6c342..0000000
--- a/sources/backend/imap/README
+++ /dev/null
@@ -1,28 +0,0 @@
-BackendIMAP - NOTES
-===================
-
-This backend support the Search operation in the mailbox.
-Since the IMAP search operation is pretty slow, with a medium/big mailbox, or with a lots of folders,
-the mobile device will timeout the operation before this is completed on server.
-
-I'm using Dovecot + FTS-SOLR plugin so the real search is done against an Apache SOLR server.
-It reduces a 1-2 minutes search to 1-5 seconds, and the response is given to the mobile device in time.
-
-
-CHANGESSINK
-===========
-It supports ChangesSink method that will detect and send faster changes to your device.
-
-
-SMTP
-====
-You can choice between 3 methods for send mails: mail (php mail), sendmail (native binary), smtp (php smtp direct connection).
-Remember to configure it in the config.php
-
-"mail" is a sendmail wrapper in Linux.
-
-
-MBCONVERT
-=========
-A lot of messages come with wrong encoding, to them to look better with any device you can pre-convert them to UTF-8.
-You will need to install the php-mbstring module
\ No newline at end of file
diff --git a/sources/backend/imap/REQUIREMENTS b/sources/backend/imap/REQUIREMENTS
deleted file mode 100644
index 740bdae..0000000
--- a/sources/backend/imap/REQUIREMENTS
+++ /dev/null
@@ -1,7 +0,0 @@
-REQUIREMENTS:
-php-imap
-php-mbstring (optional but recommended)
-libawl-php
-
-
-IMAP server (Dovecot, Courier...)
\ No newline at end of file
diff --git a/sources/backend/imap/THANKS b/sources/backend/imap/THANKS
deleted file mode 100644
index b56e45c..0000000
--- a/sources/backend/imap/THANKS
+++ /dev/null
@@ -1,4 +0,0 @@
-*Drenalina SRL (www.drenalina.com)* sponsored the development of the following features in the BackendIMAP, any existing bug it's my fault not theirs ;-)
-Thank you very much for helping to improve it!!
-
- - Meeting invitations and attendees
\ No newline at end of file
diff --git a/sources/backend/imap/config.php b/sources/backend/imap/config.php
deleted file mode 100644
index 8f00310..0000000
--- a/sources/backend/imap/config.php
+++ /dev/null
@@ -1,227 +0,0 @@
-.
-*
-* Consult LICENSE file for details
-************************************************/
-
-// ************************
-// BackendIMAP settings
-// ************************
-
-// Defines the server to which we want to connect
-define('IMAP_SERVER', 'localhost');
-
-// connecting to default port (143)
-define('IMAP_PORT', 143);
-
-// best cross-platform compatibility (see http://php.net/imap_open for options)
-define('IMAP_OPTIONS', '/notls/norsh');
-
-
-// Mark messages as read when moving to Trash.
-// BE AWARE that you will lose the unread flag, but some mail clients do this so the Trash folder doesn't get boldened
-define('IMAP_AUTOSEEN_ON_DELETE', false);
-
-
-// IMPORTANT: BASIC IMAP FOLDERS [ask your mail admin]
- // We can have diferent cases (case insensitive):
- // 1.
- // inbox
- // sent
- // drafts
- // trash
- // 2.
- // inbox
- // common.sent
- // common.drafts
- // common.trash
- // 3.
- // common.inbox
- // common.sent
- // common.drafts
- // common.trash
- // 4.
- // common
- // common.sent
- // common.drafts
- // common.trash
- //
- // gmail is a special case, where the default folders are under the [gmail] prefix and the folders defined by the user are under INBOX.
- // This configuration seems to work:
- // define('IMAP_FOLDER_PREFIX', '');
- // define('IMAP_FOLDER_INBOX', 'INBOX');
- // define('IMAP_FOLDER_SENT', '[Gmail]/Sent');
- // define('IMAP_FOLDER_DRAFTS', '[Gmail]/Drafts');
- // define('IMAP_FOLDER_TRASH', '[Gmail]/Trash');
- // define('IMAP_FOLDER_SPAM', '[Gmail]/Spam');
- // define('IMAP_FOLDER_ARCHIVE', '[Gmail]/All Mail');
-
-// Since I know you won't configure this, I will raise an error unless you do.
-// When configured set this to true to remove the error
-define('IMAP_FOLDER_CONFIGURED', false);
-
-// Folder prefix is the common part in your names (3, 4)
-define('IMAP_FOLDER_PREFIX', '');
-
-// Inbox will have the preffix preppend (3 & 4 to true)
-define('IMAP_FOLDER_PREFIX_IN_INBOX', false);
-
-// Inbox folder name (case doesn't matter) - (empty in 4)
-define('IMAP_FOLDER_INBOX', 'INBOX');
-
-// Sent folder name (case doesn't matter)
-define('IMAP_FOLDER_SENT', 'SENT');
-
-// Draft folder name (case doesn't matter)
-define('IMAP_FOLDER_DRAFT', 'DRAFTS');
-
-// Trash folder name (case doesn't matter)
-define('IMAP_FOLDER_TRASH', 'TRASH');
-
-// Spam folder name (case doesn't matter). Only showed as special by iOS devices
-define('IMAP_FOLDER_SPAM', 'SPAM');
-
-// Archive folder name (case doesn't matter). Only showed as special by iOS devices
-define('IMAP_FOLDER_ARCHIVE', 'ARCHIVE');
-
-
-
-// forward messages inline (default true - inlined)
-define('IMAP_INLINE_FORWARD', true);
-
-// list of folders we want to exclude from sync. Names, or part of it, separated by |
-// example: dovecot.sieve|archive|spam
-define('IMAP_EXCLUDED_FOLDERS', '');
-
-
-
-// overwrite the "from" header with some value
-// options:
-// '' - do nothing, use the From header
-// 'username' - the username will be set (usefull if your login is equal to your emailaddress)
-// 'domain' - the value of the "domain" field is used
-// 'sql' - the username will be the result of a sql query. REMEMBER TO INSTALL PHP-PDO AND PHP-DATABASE
-// 'ldap' - the username will be the result of a ldap query. REMEMBER TO INSTALL PHP-LDAP!!
-// '@mydomain.com' - the username is used and the given string will be appended
-define('IMAP_DEFAULTFROM', '');
-
-// DSN: formatted PDO connection string
-// mysql:host=xxx;port=xxx;dbname=xxx
-// USER: username to DB
-// PASSWORD: password to DB
-// OPTIONS: array with options needed
-// QUERY: query to execute
-// FIELDS: columns in the query
-// FROM: string that will be the from, replacing the column names with the values
-define('IMAP_FROM_SQL_DSN', '');
-define('IMAP_FROM_SQL_USER', '');
-define('IMAP_FROM_SQL_PASSWORD', '');
-define('IMAP_FROM_SQL_OPTIONS', serialize(array(PDO::ATTR_PERSISTENT => true)));
-define('IMAP_FROM_SQL_QUERY', "select first_name, last_name, mail_address from users where mail_address = '#username@#domain'");
-define('IMAP_FROM_SQL_FIELDS', serialize(array('first_name', 'last_name', 'mail_address')));
-define('IMAP_FROM_SQL_FROM', '#first_name #last_name <#mail_address>');
-define('IMAP_FROM_SQL_FULLNAME', '#first_name #last_name');
-
-// SERVER: ldap server
-// SERVER_PORT: ldap port
-// USER: dn to use for connecting
-// PASSWORD: password
-// QUERY: query to execute
-// FIELDS: columns in the query
-// FROM: string that will be the from, replacing the field names with the values
-define('IMAP_FROM_LDAP_SERVER', 'localhost');
-define('IMAP_FROM_LDAP_SERVER_PORT', '389');
-define('IMAP_FROM_LDAP_USER', 'cn=zpush,ou=servers,dc=zpush,dc=org');
-define('IMAP_FROM_LDAP_PASSWORD', 'password');
-define('IMAP_FROM_LDAP_BASE', 'dc=zpush,dc=org');
-define('IMAP_FROM_LDAP_QUERY', '(mail=#username@#domain)');
-define('IMAP_FROM_LDAP_FIELDS', serialize(array('givenname', 'sn', 'mail')));
-define('IMAP_FROM_LDAP_FROM', '#givenname #sn <#mail>');
-define('IMAP_FROM_LDAP_FULLNAME', '#givenname #sn');
-
-
-
-// Method used for sending mail
-// mail => mail() php function
-// sendmail => sendmail executable
-// smtp => direct connection against SMTP
-define('IMAP_SMTP_METHOD', 'mail');
-
-global $imap_smtp_params;
-// SMTP Parameters
-// mail : no params
-$imap_smtp_params = array();
-// sendmail
-//$imap_smtp_params = array('sendmail_path' => '/usr/bin/sendmail', 'sendmail_args' => '-i');
-// smtp
-// "host" - The server to connect. Default is localhost.
-// "port" - The port to connect. Default is 25.
-// "auth" - Whether or not to use SMTP authentication. Default is FALSE.
-// "username" - The username to use for SMTP authentication. "imap_username" for using the same username as the imap server
-// "password" - The password to use for SMTP authentication. "imap_password" for using the same password as the imap server
-// "localhost" - The value to give when sending EHLO or HELO. Default is localhost
-// "timeout" - The SMTP connection timeout. Default is NULL (no timeout).
-// "verp" - Whether to use VERP or not. Default is FALSE.
-// "debug" - Whether to enable SMTP debug mode or not. Default is FALSE.
-// "persist" - Indicates whether or not the SMTP connection should persist over multiple calls to the send() method.
-// "pipelining" - Indicates whether or not the SMTP commands pipelining should be used.
-// "verify_peer" - Require verification of SSL certificate used. Default is TRUE.
-// "verify_peer_name" - Require verification of peer name. Default is TRUE.
-// "allow_self_signed" - Allow self-signed certificates. Requires verify_peer. Default is FALSE.
-//$imap_smtp_params = array('host' => 'localhost', 'port' => 25, 'auth' => false);
-// If you want to use SSL with port 25 or port 465 you must preppend "ssl://" before the hostname or IP of your SMTP server
-// IMPORTANT: To use SSL you must use PHP 5.1 or later, install openssl libs and use ssl:// within the host variable
-// IMPORTANT: To use SSL with PHP 5.6 you should set verify_peer, verify_peer_name and allow_self_signed
-//$imap_smtp_params = array('host' => 'ssl://localhost', 'port' => 465, 'auth' => true, 'username' => 'imap_username', 'password' => 'imap_password');
-
-
-
-// If you are using IMAP_SMTP_METHOD = mail or sendmail and your sent messages are not correctly displayed you can change this to "\n".
-// BUT, it doesn't comply with RFC 2822 and will break if using smtp method
-define('MAIL_MIMEPART_CRLF', "\r\n");
-
-
-// A file containing file mime types->extension mappings.
-// SELINUX users: make sure the file has a security context accesible by your apache/php-fpm process
-define('SYSTEM_MIME_TYPES_MAPPING', '/etc/mime.types');
-
-
-// Use BackendCalDAV for Meetings. You cannot hope to get that functionality working without a caldav backend.
-define('IMAP_MEETING_USE_CALDAV', false);
\ No newline at end of file
diff --git a/sources/backend/imap/imap.php b/sources/backend/imap/imap.php
deleted file mode 100644
index 3889f05..0000000
--- a/sources/backend/imap/imap.php
+++ /dev/null
@@ -1,2707 +0,0 @@
-.
-*
-* Consult LICENSE file for details
-************************************************/
-
-// config file
-require_once("backend/imap/config.php");
-
-require_once("backend/imap/mime_calendar.php");
-require_once("backend/imap/mime_encode.php");
-require_once("backend/imap/user_identity.php");
-
-class BackendIMAP extends BackendDiff implements ISearchProvider {
- private $wasteID;
- private $sentID;
- private $server;
- private $mbox;
- private $mboxFolder;
- private $username;
- private $password;
- private $domain;
- private $sinkfolders = array();
- private $sinkstates = array();
- private $changessinkinit = false;
- private $folderhierarchy;
- private $excludedFolders;
- private static $mimeTypes = false;
-
-
- public function BackendIMAP() {
- if (BackendIMAP::$mimeTypes === false) {
- BackendIMAP::$mimeTypes = $this->SystemExtensionMimeTypes();
- }
- $this->wasteID = false;
- $this->sentID = false;
- $this->mboxFolder = "";
-
- if (!function_exists("imap_open"))
- throw new FatalException("BackendIMAP(): php-imap module is not installed", 0, null, LOGLEVEL_FATAL);
-
- if (!function_exists("mb_detect_order")) {
- ZLog::Write(LOGLEVEL_WARN, sprintf("BackendIMAP(): php-mbstring module is not installed, you should install it for better encoding conversions"));
- }
- }
-
- /**----------------------------------------------------------------------------------------------------------
- * default backend methods
- */
-
- /**
- * Authenticates the user
- *
- * @param string $username
- * @param string $domain
- * @param string $password
- *
- * @access public
- * @return boolean
- * @throws FatalException if php-imap module can not be found
- */
- public function Logon($username, $domain, $password) {
- $this->wasteID = false;
- $this->sentID = false;
- $this->server = "{" . IMAP_SERVER . ":" . IMAP_PORT . "/imap" . IMAP_OPTIONS . "}";
-
- if (!function_exists("imap_open"))
- throw new FatalException("BackendIMAP(): php-imap module is not installed", 0, null, LOGLEVEL_FATAL);
-
- if (defined('IMAP_FOLDER_CONFIGURED') && IMAP_FOLDER_CONFIGURED == false)
- throw new FatalException("BackendIMAP(): You didn't configure your IMAP folder names. Do it before!", 0, null, LOGLEVEL_FATAL);
-
- /* BEGIN fmbiete's contribution r1527, ZP-319 */
- $this->excludedFolders = array();
- if (defined('IMAP_EXCLUDED_FOLDERS') && strlen(IMAP_EXCLUDED_FOLDERS) > 0) {
- $this->excludedFolders = explode("|", IMAP_EXCLUDED_FOLDERS);
- ZLog::Write(LOGLEVEL_DEBUG, sprintf("BackendIMAP->Logon(): Excluding Folders (%s)", IMAP_EXCLUDED_FOLDERS));
- }
- /* END fmbiete's contribution r1527, ZP-319 */
-
- // open the IMAP-mailbox
- $this->mbox = @imap_open($this->server , $username, $password, OP_HALFOPEN);
- $this->mboxFolder = "";
-
- if ($this->mbox) {
- ZLog::Write(LOGLEVEL_DEBUG, sprintf("BackendIMAP->Logon(): User '%s' is authenticated on '%s'", $username, $this->server));
- $this->username = $username;
- $this->password = $password;
- $this->domain = $domain;
- return true;
- }
- else {
- ZLog::Write(LOGLEVEL_ERROR, sprintf("BackendIMAP->Logon(): can't connect as user '%s' on '%s': %s", $username, $this->server, imap_last_error()));
- return false;
- }
- }
-
- /**
- * Logs off
- * Called before shutting down the request to close the IMAP connection
- * writes errors to the log
- *
- * @access public
- * @return boolean
- */
- public function Logoff() {
- $this->close_connection();
- $this->SaveStorages();
- }
-
- /**
- * Sends an e-mail
- * This messages needs to be saved into the 'sent items' folder
- *
- * @param SyncSendMail $sm SyncSendMail object
- *
- * @access public
- * @return boolean
- * @throws StatusException
- */
- public function SendMail($sm) {
- ZLog::Write(LOGLEVEL_DEBUG, sprintf("BackendIMAP->SendMail(): RFC822: %d bytes forward-id: '%s' reply-id: '%s' parent-id: '%s' SaveInSent: '%s' ReplaceMIME: '%s'",
- strlen($sm->mime),
- Utils::PrintAsString($sm->forwardflag ? (isset($sm->source->itemid) ? $sm->source->itemid : "error no itemid") : false),
- Utils::PrintAsString($sm->replyflag ? (isset($sm->source->itemid) ? $sm->source->itemid : "error no itemid") : false),
- Utils::PrintAsString((isset($sm->source->folderid) ? $sm->source->folderid : false)),
- Utils::PrintAsString(($sm->saveinsent)), Utils::PrintAsString(isset($sm->replacemime))));
-
- // by splitting the message in several lines we can easily grep later
- foreach(preg_split("/((\r)?\n)/", $sm->mime) as $rfc822line)
- ZLog::Write(LOGLEVEL_WBXML, "RFC822: ". $rfc822line);
-
- $sourceMessage = $sourceMail = false;
- // If we have a reference to a source message and we are not replacing mime (since we wouldn't use it)
- if (isset($sm->source->folderid) && isset($sm->source->itemid) && (!isset($sm->replacemime) || $sm->replacemime === false)) {
- ZLog::Write(LOGLEVEL_DEBUG, sprintf("BackendIMAP->SendMail(): We have a source message and we try to fetch it"));
- $parent = $this->getImapIdFromFolderId($sm->source->folderid);
- if ($parent === false) {
- throw new StatusException(sprintf("BackendIMAP->SendMail(): Could not get imapid from source folderid '%'", $sm->source->folderid), SYNC_COMMONSTATUS_ITEMNOTFOUND);
- }
- else {
- $this->imap_reopen_folder($parent);
- $sourceMail = @imap_fetchheader($this->mbox, $sm->source->itemid, FT_UID) . @imap_body($this->mbox, $sm->source->itemid, FT_PEEK | FT_UID);
- $mobj = new Mail_mimeDecode($sourceMail);
- $sourceMessage = $mobj->decode(array('decode_headers' => false, 'decode_bodies' => true, 'include_bodies' => true, 'rfc_822bodies' => true, 'charset' => 'utf-8'));
- unset($mobj);
- //We will need $sourceMail if the message is forwarded and not inlined
-
- // If it's a reply, we mark the original message as answered
- if ($sm->replyflag) {
- if (!@imap_setflag_full($this->mbox, $sm->source->itemid, "\\Answered", ST_UID)) {
- ZLog::Write(LOGLEVEL_WARN, sprintf("BackendIMAP->SendMail(): Unable to mark the message as Answered"));
- }
- }
-
- // If it's a forward, we mark the original message as forwarded
- if ($sm->forwardflag) {
- if (!@imap_setflag_full($this->mbox, $sm->source->itemid, "\\Forwarded", ST_UID)) {
- ZLog::Write(LOGLEVEL_WARN, sprintf("BackendIMAP->SendMail(): Unable to mark the message as Forwarded"));
- }
- }
- }
- }
-
- ZLog::Write(LOGLEVEL_DEBUG, sprintf("BackendIMAP->SendMail(): We get the new message"));
- $mobj = new Mail_mimeDecode($sm->mime);
- $message = $mobj->decode(array('decode_headers' => true, 'decode_bodies' => true, 'include_bodies' => true, 'rfc_822bodies' => true, 'charset' => 'utf-8'));
- unset($mobj);
-
- ZLog::Write(LOGLEVEL_DEBUG, sprintf("BackendIMAP->SendMail(): We get the From and To"));
- $Mail_RFC822 = new Mail_RFC822();
-
- $toaddr = "";
- $this->setFromHeaderValue($message->headers);
- $fromaddr = $this->parseAddr($Mail_RFC822->parseAddressList($message->headers["from"]));
-
- if (isset($message->headers["to"])) {
- $toaddr = $this->parseAddr($Mail_RFC822->parseAddressList($message->headers["to"]));
- ZLog::Write(LOGLEVEL_DEBUG, sprintf("BackendIMAP->SendMail(): To defined: %s", $toaddr));
- }
- unset($Mail_RFC822);
-
- // overwrite CC and BCC with the decoded versions, because we will parse/validate the address in the sending method
- if (isset($message->headers["cc"])) {
- $message->headers["cc"] = $message->headers["cc"];
- }
- if (isset($message->headers["bcc"])) {
- $message->headers["bcc"] = $message->headers["bcc"];
- }
-
- $this->setReturnPathValue($message->headers, $fromaddr);
-
- $finalBody = "";
- $finalHeaders = array();
-
- // if it's a S/MIME message or has VCALENDAR objects I don't do anything with it
- if (is_smime($message) || has_calendar_object($message)) {
- $mobj = new Mail_mimeDecode($sm->mime);
- $parts = $mobj->getSendArray();
- unset($mobj);
- if ($parts === false) {
- throw new StatusException(sprintf("BackendIMAP->SendMail(): Could not getSendArray for SMIME messages"), SYNC_COMMONSTATUS_MAILSUBMISSIONFAILED);
- }
- else {
- list($recipients, $finalHeaders, $finalBody) = $parts;
-
- $this->setFromHeaderValue($finalHeaders);
- $this->setReturnPathValue($finalHeaders, $fromaddr);
- }
- }
- else {
- //http://pear.php.net/manual/en/package.mail.mail-mime.example.php
- //http://pear.php.net/manual/en/package.mail.mail-mimedecode.decode.php
- //http://pear.php.net/manual/en/package.mail.mail-mimepart.addsubpart.php
-
- // I don't mind if the new message is multipart or not, I always will create a multipart. It's simpler
- $finalEmail = new Mail_mimePart('', array('content_type' => 'multipart/mixed'));
-
- if ($sm->replyflag && (!isset($sm->replacemime) || $sm->replacemime === false)) {
- ZLog::Write(LOGLEVEL_DEBUG, sprintf("BackendIMAP->SendMail(): Replying message"));
- $this->addTextParts($finalEmail, $message, $sourceMessage, true);
-
- if (isset($message->parts)) {
- // We add extra parts from the replying message
- add_extra_sub_parts($finalEmail, $message->parts);
- }
- // A replied message doesn't include the original attachments
- }
- else if ($sm->forwardflag && (!isset($sm->replacemime) || $sm->replacemime === false)) {
- if (!defined('IMAP_INLINE_FORWARD') || IMAP_INLINE_FORWARD === false) {
- ZLog::Write(LOGLEVEL_DEBUG, "BackendIMAP->SendMail(): Forwarding message as attached file - eml");
- $finalEmail->addSubPart($sourceMail, array('content_type' => 'message/rfc822', 'encoding' => 'base64', 'disposition' => 'attachment', 'dfilename' => 'forwarded_message.eml'));
- }
- else {
- ZLog::Write(LOGLEVEL_DEBUG, "BackendIMAP->SendMail(): Forwarding inlined message");
- $this->addTextParts($finalEmail, $message, $sourceMessage, false);
-
- if (isset($message->parts)) {
- // We add extra parts from the forwarding message
- add_extra_sub_parts($finalEmail, $message->parts);
- }
- if (isset($sourceMessage->parts)) {
- // We add extra parts from the forwarded message
- add_extra_sub_parts($finalEmail, $sourceMessage->parts);
- }
- }
- }
- else {
- ZLog::Write(LOGLEVEL_DEBUG, sprintf("BackendIMAP->SendMail(): is a new message or we are replacing mime"));
- $this->addTextPartsMessage($finalEmail, $message);
- if (isset($message->parts)) {
- // We add extra parts from the new message
- add_extra_sub_parts($finalEmail, $message->parts);
- }
- }
-
- // We encode the final message
- $boundary = '=_' . md5(rand() . microtime());
- $finalEmail = $finalEmail->encode($boundary);
-
- $finalHeaders = array('Mime-Version' => '1.0');
- // We copy all the non-existent headers, minus content_type
- ZLog::Write(LOGLEVEL_DEBUG, sprintf("BackendIMAP->SendMail(): Copying new headers"));
- foreach ($message->headers as $k => $v) {
- if (strcasecmp($k, 'content-type') != 0 && strcasecmp($k, 'content-transfer-encoding') != 0 && strcasecmp($k, 'mime-version') != 0) {
- if (!isset($finalHeaders[$k]))
- $finalHeaders[ucwords($k)] = $v;
- }
- }
- foreach ($finalEmail['headers'] as $k => $v) {
- if (!isset($finalHeaders[$k]))
- $finalHeaders[$k] = $v;
- }
-
- $finalBody = "This is a multi-part message in MIME format.\n" . $finalEmail['body'];
-
- unset($finalEmail);
- }
-
- unset($sourceMail);
- unset($message);
- unset($sourceMessage);
-
- ZLog::Write(LOGLEVEL_DEBUG, sprintf("BackendIMAP->SendMail(): Final mail to send:"));
- foreach ($finalHeaders as $k => $v)
- ZLog::Write(LOGLEVEL_WBXML, sprintf("%s: %s", $k, $v));
- foreach (preg_split("/((\r)?\n)/", $finalBody) as $bodyline)
- ZLog::Write(LOGLEVEL_WBXML, sprintf("Body: %s", $bodyline));
-
- $send = $this->sendMessage($fromaddr, $toaddr, $finalHeaders, $finalBody);
-
- if ($send) {
- if (isset($sm->saveinsent)) {
- $this->saveSentMessage($finalHeaders, $finalBody);
- }
- else {
- ZLog::Write(LOGLEVEL_DEBUG, "BackendIMAP->SendMail(): Not saving in SentFolder");
- }
- }
-
- unset($finalHeaders);
- unset($finalBody);
-
- return $send;
- }
-
- /**
- * Add text parts to a mimepart object, with reply or forward tags
- *
- * @param Mail_mimePart $email reference to the object
- * @param Mail_mimeDecode $message reference to the message
- * @param Mail_mimeDecode $sourceMessage reference to the original message
- * @param boolean $isReply true if it's a reply, false if it's a forward
- *
- * @access private
- * @return void
- */
- private function addTextParts(&$email, &$message, &$sourceMessage, $isReply = true) {
- $htmlBody = $plainBody = '';
- Mail_mimeDecode::getBodyRecursive($message, "html", $htmlBody);
- Mail_mimeDecode::getBodyRecursive($message, "plain", $plainBody);
- $htmlSource = $plainSource = '';
- Mail_mimeDecode::getBodyRecursive($sourceMessage, "html", $htmlSource);
- Mail_mimeDecode::getBodyRecursive($sourceMessage, "plain", $plainSource);
-
- $separator = '';
- if ($isReply) {
- $separator = ">\r\n";
- $separatorHtml = "";
- $separatorHtmlEnd = "