1
0
Fork 0
mirror of https://github.com/YunoHost-Apps/z-push_ynh.git synced 2024-09-03 18:05:58 +02:00
This commit is contained in:
polytan02 2017-02-09 18:08:00 +00:00
commit 45babea26c
338 changed files with 300 additions and 84164 deletions

View file

@ -659,38 +659,3 @@ specific requirements.
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
<http://www.gnu.org/licenses/>.
---
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 <http://www.gnu.org/licenses/>.

View file

@ -1,7 +1,11 @@
# Z-Push For Yunohost
==========
===========
* Z-push for Yunohost support IMAP sync and Carddav/Caldav sync if Baikal is installed
* Ability to send calendar invitations
* The sources are based on https://github.com/fmbiete/Z-Push-contrib
* The sources are based on http://download.z-push.org/final/
* tbd :
<br/>update sources
<br/>download them at installation

30
check_process Normal file
View file

@ -0,0 +1,30 @@
;; Test complet
auto_remove=1
; Manifest
domain="domain.tld" (DOMAIN)
; Checks
pkg_linter=1
setup_sub_dir=1
setup_root=1
setup_nourl=1
setup_private=0
setup_public=0
upgrade=1
backup_restore=1
multi_instance=0
wrong_user=0
wrong_path=1
incorrect_path=1
corrupt_source=0
fail_download_source=0
port_already_use=0
final_path_already_use=0
;;; Levels
Level 1=auto
Level 2=auto
Level 3=auto
Level 4=1
Level 5=1
Level 6=auto
Level 7=auto

View file

@ -3,8 +3,8 @@
"id": "z-push",
"packaging_format": 1,
"description": {
"en": "Z-Push is an open-source application to synchronize ActiveSync",
"fr": "Z-Push est un serveur open source de synchronisation ActiveSync"
"en": "Z-Push is an open-source application to synchronize email, calendar and contacts via ActiveSync",
"fr": "Z-Push est un serveur open source de synchronisation email, contacts et calendrier via ActiveSync"
},
"url": "https://z-push.org",
"license": "free",
@ -30,6 +30,16 @@
"fr": "Choisissez un domaine pour Z-push"
},
"example": "domain.org"
},
{
"name": "path",
"type": "path",
"ask": {
"en": "No choice, it must be /Microsoft-Server-ActiveSync",
"fr": "Pas de choix, cela doit être /Microsoft-Server-ActiveSync"
},
"choices": ["/Microsoft-Server-ActiveSync"],
"default": "/Microsoft-Server-ActiveSync"
}
]
}

37
scripts/backup Executable file
View file

@ -0,0 +1,37 @@
#!/bin/bash
# Exit on command errors and treat unset variables as an error
#set -eu
#TRAP_ON () { # Activate signal capture
# trap EXIT_PROPERLY ERR # Capturing ex it signals on error
#}
# Active trap pour arrêter le script si une erreur est détectée.
#TRAP_ON
# See comments in install script
app=$YNH_APP_INSTANCE_NAME
# Source YunoHost helpers
source /usr/share/yunohost/helpers
path=$(ynh_app_setting_get $app path)
domain=$(ynh_app_setting_get $app domain)
final_path=$(ynh_app_setting_get $app final_path)
final_logpath=$(ynh_app_setting_get $app final_logpath)
final_nginxconf=$(ynh_app_setting_get $app final_nginxconf)
final_phpconf=$(ynh_app_setting_get $app final_phpconf)
# Backup sources & data
# Note: the last argument is where to save this path, see the restore script.
ynh_backup "$final_path" "sources"
# Backup logs
ynh_backup "$final_logpath" "logs"
# Copy NGINX configuration
ynh_backup "$final_nginxconf" "nginx.conf"
# Copy PHP-FPM configuration
ynh_backup "$final_phpconf" "php-fpm.conf"

122
scripts/install Normal file → Executable file
View file

@ -1,25 +1,108 @@
#!/bin/bash
# Active trap pour arrêter le script si une erreur est détectée.
EXIT_PROPERLY () { # Provoque l'arrêt du script en cas d'erreur. Et nettoye les résidus.
exit_code=$?
if [ "$exit_code" -eq 0 ]; then
exit 0 # Quitte sans erreur si le script se termine correctement.
fi
trap '' EXIT
set +eu
echo -e "\e[91m \e[1m" # Shell in light red bold
echo -e "!!\n $app install's script has encountered an error. Installation was cancelled.\n!!" >&2
if type -t CLEAN_SETUP > /dev/null; then # Vérifie l'existance de la fonction avant de l'exécuter.
CLEAN_SETUP # Appel la fonction de nettoyage spécifique du script install.
fi
# Compense le bug de ssowat qui ne supprime pas l'entrée de l'app en cas d'erreur d'installation.
sudo sed -i "\@\"$domain$path/\":@d" /etc/ssowat/conf.json
ynh_die
}
TRAP_ON () { # Activate signal capture
set -eu # Exit if a command fail, and if a variable is used unset.
trap EXIT_PROPERLY EXIT # Capturing exit signals on shell script
}
TRAP_ON
# Source app helpers
source /usr/share/yunohost/helpers
# Retrieve arguments
app=$YNH_APP_INSTANCE_NAME
domain=$YNH_APP_ARG_DOMAIN
path=$YNH_APP_ARG_PATH
app=$YNH_APP_INSTANCE_NAME
# Check domain/path availability
sudo yunohost app checkurl $domain$path -a $app
if [[ ! $? -eq 0 ]]; then
exit 1
fi
# We check variables are not empty
CHECK_VAR () { # Vérifie que la variable n'est pas vide.
# $1 = Variable à vérifier
# $2 = Texte à afficher en cas d'erreur
test -n "$1" || (echo "$2" >&2 && false)
}
CHECK_VAR "$app" "app name not set"
# Check the path value and correct it (adds / at begining and removes it at the end)
CHECK_PATH () { # Vérifie la présence du / en début de path. Et son absence à la fin.
if [ "${path:0:1}" != "/" ]; then # Si le premier caractère n'est pas un /
path="/$path" # Ajoute un / en début de path
fi
if [ "${path:${#path}-1}" == "/" ] && [ ${#path} -gt 1 ]; then # Si le dernier caractère est un / et que ce n'est pas l$
path="${path:0:${#path}-1}" # Supprime le dernier caractère
fi
}
CHECK_PATH;
# Check domain and path availibility
CHECK_DOMAINPATH () { # Vérifie la disponibilité du path et du domaine.
sudo yunohost app checkurl $domain$path -a $app
}
CHECK_DOMAINPATH
# Prereqs
sudo apt-get install php-soap php5-imap libawl-php php5-xsl -y
final_logpath=/var/log/$app
final_path=/var/www/$app
final_path="/var/www/$app"
CHECK_FINALPATH () { # Vérifie que le dossier de destination n'est pas déjà utilisé.
if [ -e "$final_path" ]
then
echo "This path already contains a folder" >&2
false
fi
}
CHECK_FINALPATH
# Define variables and Save app settings
ynh_app_setting_set "$app" domain "$domain"
ynh_app_setting_set "$app" final_path "$final_path"
final_logpath="/var/log/$app"
ynh_app_setting_set "$app" final_logpath "$final_logpath"
final_nginxconf="/etc/nginx/conf.d/${domain}.d/${app}.conf"
ynh_app_setting_set "$app" final_nginxconf "$final_nginxconf"
final_phpconf="/etc/php5/fpm/pool.d/${app}.conf"
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
SFILE=`sudo cat ../sources/source_file`;
sudo wget -nv -i ../sources/source_url -O $SFILE
sudo md5sum -c ../sources/source_md5 --status || (echo "Corrupt source" >&2 && false)
sudo tar xvf $SFILE -d ../sources/
sudo cp -R ../sources/$SFILE/* $final_path
# Configuration
sed -i "s^define('TIMEZONE', .*^define('TIMEZONE', '$(cat /etc/timezone)');^" ../conf/config.php
@ -27,11 +110,10 @@ sed -i "s@ALIASTOCHANGE@$final_path@g" ../conf/config.php
sed -i "s@LOGTOCHANGE@$final_logpath@g" ../conf/config.php
# Enable caldav carddav support
sudo yunohost app list -f baikal --json | grep '"installed": true'
if [ "$?" -eq 0 ]; then
if sudo yunohost app list --installed -f baikal | grep -q id ; then
echo "Detected Baikal"
caldavdomain=$(sudo yunohost app setting baikal domain)
caldavpath=$(sudo yunohost app setting baikal path)
caldavdomain=$(ynh_app_setting_get baikal domain)
caldavpath=$(ynh_app_setting_get baikal path)
caldavpath=${caldavpath%/}
# Configuration of backend
@ -64,7 +146,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
@ -76,17 +157,16 @@ sudo ln -s /usr/share/awl/inc/XML* /var/www/$app/include/
# Modify Nginx configuration file and copy it to Nginx conf directory
sudo sed -i "s@ALIASTOCHANGE@$final_path/@g" ../conf/nginx.conf
sudo sed -i "s@NAMETOCHANGE@$app@g" ../conf/nginx.conf
sudo cp ../conf/nginx.conf /etc/nginx/conf.d/$domain.d/$app.conf
sudo cp ../conf/nginx.conf $final_nginxconf
# Dedicated php-fpm processes
sudo sed -i "s@NAMETOCHANGE@$app@g" ../conf/php-fpm.conf
finalphpconf=/etc/php5/fpm/pool.d/$app.conf
sudo cp ../conf/php-fpm.conf $finalphpconf
sudo chown root: $finalphpconf
sudo chmod 644 $finalphpconf
sudo cp ../conf/php-fpm.conf $final_phpconf
sudo chown root: $final_phpconf
sudo chmod 644 $final_phpconf
# Enable api for client
sudo yunohost app setting $app skipped_uris -v "/"
# Enable api for client and make app public
ynh_app_setting_set $app skipped_uris "/"
# Reload Nginx and regenerate SSOwat conf
sudo service php5-fpm reload

6
scripts/remove Normal file → Executable file
View file

@ -1,7 +1,11 @@
#!/bin/bash
# Source app helpers
source /usr/share/yunohost/helpers
# We retrieve app parameters
app=$YNH_APP_INSTANCE_NAME
domain=$(sudo yunohost app setting $app domain)
domain=$(ynh_app_setting_get $app domain)
# Cleaning
sudo rm -rf /var/www/$app

42
scripts/restore Executable file
View file

@ -0,0 +1,42 @@
#!/bin/bash
app=$YNH_APP_INSTANCE_NAME
# Source YunoHost helpers
source /usr/share/yunohost/helpers
# Retrieve old app settings
path=$(ynh_app_setting_get $app path)
domain=$(ynh_app_setting_get $app domain)
final_path=$(ynh_app_setting_get $app final_path)
final_logpath=$(ynh_app_setting_get $app final_logpath)
final_nginxconf=$(ynh_app_setting_get $app final_nginxconf)
final_phpconf=$(ynh_app_setting_get $app final_phpconf)
# Restore sources, data and permissions
sudo mkdir -p $final_path
sudo cp -a ./sources/* $final_path/
sudo chown -R www-data: $final_path
# Restore logs, data & permissions
sudo mkdir -p $final_logpath
sudo cp -a ./logs/* $final_logpath/
sudo chown -R www-data:www-data $final_logpath
# Restore NGINX configuration
sudo cp -a ./nginx.conf $final_nginxconf
# Restore PHP-FPM configuration
sudo cp -a ./php-fpm.conf $final_phpconf
sudo chown root: $final_phpconf
sudo chmod 644 $final_phpconf
# Make app public
ynh_app_setting_set $app skipped_uris "/"
# Restart webserver
sudo service nginx reload
sudo service php5-fpm reload
sudo yunohost app ssowatconf

88
scripts/upgrade Normal file → Executable file
View file

@ -1,25 +1,69 @@
#!/bin/bash
# Source app helpers
source /usr/share/yunohost/helpers
# Retrieve arguments
app=$YNH_APP_INSTANCE_NAME
domain=$(sudo yunohost app setting $app domain)
path="/Microsoft-Server-ActiveSync"
domain=$(ynh_app_setting_get $app domain)
path=$(ynh_app_setting_get $app path)
final_path=$(ynh_app_setting_get $app final_path)
final_logpath=$(ynh_app_setting_get $app final_logpath)
final_nginxconf=$(ynh_app_setting_get $app final_nginxconf)
final_phpconf=$(ynh_app_setting_get $app final_phpconf)
# We make the app retro-compatible with previous versions
if [ -z "$final_path" ];
then
final_path="/var/www/$app"
ynh_app_setting_set "$app" final_path "$final_path"
else
echo "${final_path} exists, we don't create it."
fi
if [ -z "$final_logpath" ];
then
final_logpath="/var/log/$app"
ynh_app_setting_set "$app" final_logpath "$final_logpath"
else
echo "${final_logpath} exists, we don't create it."
fi
if [ -z "$final_nginxconf" ];
then
final_nginxconf="/etc/nginx/conf.d/${domain}.d/${app}.conf"
ynh_app_setting_set "$app" final_nginxconf "$final_nginxconf"
else
echo "${final_nginxconf} exists, we don't create it."
fi
if [ -z "$final_phpconf" ];
then
final_phpconf=/etc/php5/fpm/pool.d/$app.conf
ynh_app_setting_set "$app" final_phpconf "$final_phpconf"
else
echo "${final_phpconf} exists, we don't create it."
fi
# Prereqs
sudo apt-get install php-soap php5-imap libawl-php php5-xsl
# Cleaning
sudo rm -rf /var/www/$app
sudo rm -rf /var/log/$app
sudo rm -f /etc/nginx/conf.d/$domain.d/$app.conf
sudo rm -f /etc/php5/fpm/pool.d/$app.conf
final_logpath=/var/log/$app
final_path=/var/www/$app
sudo rm -rf $final_path
sudo rm -rf $final_logpath
sudo rm -f $final_nginxconf
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
SFILE=`sudo cat ../sources/source_file`;
sudo wget -nv -i ../sources/source_url -O $SFILE
sudo md5sum -c ../sources/source_md5 --status || (echo "Corrupt source" >&2 && false)
sudo tar xvf $SFILE -d ../sources/
sudo cp -R ../sources/$SFILE/* $final_path
# Configuration
sed -i "s^define('TIMEZONE', .*^define('TIMEZONE', '$(cat /etc/timezone)');^" ../conf/config.php
@ -27,11 +71,10 @@ sed -i "s@ALIASTOCHANGE@$final_path@g" ../conf/config.php
sed -i "s@LOGTOCHANGE@$final_logpath@g" ../conf/config.php
# Enable caldav carddav support
sudo yunohost app list -f baikal --json | grep '"installed": true'
if [ "$?" -eq 0 ]; then
if sudo yunohost app list --installed -f baikal | grep -q id ; then
echo "Detected Baikal"
caldavdomain=$(sudo yunohost app setting baikal domain)
caldavpath=$(sudo yunohost app setting baikal path)
caldavdomain=$(ynh_app_setting_get baikal domain)
caldavpath=$(ynh_app_setting_get baikal path)
caldavpath=${caldavpath%/}
# Configuration of backend
@ -55,7 +98,7 @@ else
# Copy config
sudo cp ../conf/config.php $final_path/config.php
sed -i "s@DOMAINTOCHANGE@$domain@g" ../conf/backend/config-imap.php
sed -i "s@DOMAINTOCHANGE@$domain@g" ../conf/backend/config-imap.php
sed -i "s@FLAGTOCHANGE@false@g" ../conf/backend/config-imap.php
sudo cp ../conf/backend/config-imap.php $final_path/backend/imap/config.php
@ -64,7 +107,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
@ -76,18 +118,16 @@ sudo ln -s /usr/share/awl/inc/XML* /var/www/z-push/include/
# Modify Nginx configuration file and copy it to Nginx conf directory
sudo sed -i "s@ALIASTOCHANGE@$final_path/@g" ../conf/nginx.conf
sudo sed -i "s@NAMETOCHANGE@$app@g" ../conf/nginx.conf
sudo cp ../conf/nginx.conf /etc/nginx/conf.d/$domain.d/$app.conf
sudo cp ../conf/nginx.conf $final_nginxconf
# Dedicated php-fpm processes
sudo sed -i "s@NAMETOCHANGE@$app@g" ../conf/php-fpm.conf
finalphpconf=/etc/php5/fpm/pool.d/$app.conf
sudo cp ../conf/php-fpm.conf $finalphpconf
sudo chown root: $finalphpconf
sudo chmod 644 $finalphpconf
# Enable api for client
sudo yunohost app setting $app skipped_uris -v "/"
sudo cp ../conf/php-fpm.conf $final_phpconf
sudo chown root: $final_phpconf
sudo chmod 644 $final_phpconf
# Enable api for client and make the app public
ynh_app_setting_set $app skipped_uris "/"
# Restart of services
sudo service php5-fpm reload

View file

@ -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.
<Directory /usr/share/z-push>
php_flag magic_quotes_gpc off
php_flag register_globals off
php_flag magic_quotes_runtime off
php_flag short_open_tag on
</Directory>
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 users 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 its 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://<serverip>/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.

View file

@ -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)

View file

@ -1,4 +0,0 @@
Run composer to update autoinclude
==================================
curl -sS https://getcomposer.org/installer | php
php composer.phar dump-autoload -o

View file

@ -1,114 +0,0 @@
Z-Push-contrib
==============
[![Join the chat at https://gitter.im/fmbiete/Z-Push-contrib](https://badges.gitter.im/Join%20Chat.svg)](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

View file

@ -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! :)

View file

@ -1,256 +0,0 @@
<?php
/***********************************************
* File : autodiscover.php
* Project : Z-Push
* Descr : The autodiscover service for Z-Push.
*
* Created : 14.05.2014
*
* Copyright 2007 - 2014 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 <http://www.gnu.org/licenses/>.
*
* 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();

View file

@ -1,19 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<Autodiscover xmlns="http://schemas.microsoft.com/exchange/autodiscover/responseschema/2006" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<Response xmlns="http://schemas.microsoft.com/exchange/autodiscover/mobilesync/responseschema/2006">
<Culture>en:us</Culture>
<User>
<DisplayName></DisplayName>
<EMailAddress></EMailAddress>
</User>
<Action>
<Settings>
<Server>
<Type>MobileSync</Type>
<Url></Url>
<Name></Name>
</Server>
</Settings>
</Action>
</Response>
</Autodiscover>

View file

@ -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

View file

@ -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...)

File diff suppressed because it is too large Load diff

View file

@ -1,74 +0,0 @@
<?php
/***********************************************
* File : config.php
* Project : Z-Push
* Descr : CalDAV backend configuration file
*
* Created : 27.11.2012
*
* Copyright 2012 - 2014 Jean-Louis Dupond
*
* Jean-Louis Dupond released this code as AGPLv3 here: https://github.com/dupondje/PHP-Push-2/issues/93
*
* 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 <http://www.gnu.org/licenses/>.
*
* 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);

View file

@ -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).

View file

@ -1,5 +0,0 @@
REQUIREMENTS:
php-curl
php-xsl
CardDAV server (DAViCal, Sabredav, Sogo, Owncloud...)

View file

@ -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.

File diff suppressed because it is too large Load diff

View file

@ -1,109 +0,0 @@
<?php
/***********************************************
* File : config.php
* Project : Z-Push
* Descr : CardDAV backend configuration file
*
* Created : 16.03.2013
*
* Copyright 2013 - 2016 Francisco Miguel Biete
*
* 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 <http://www.gnu.org/licenses/>.
*
* 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');

View file

@ -1,718 +0,0 @@
<?php
/***********************************************
* File : backend/combined/combined.php
* Project : Z-Push
* Descr : Combines several backends. Each type of message
* (Emails, Contacts, Calendar, Tasks) can be handled by
* a separate backend.
* As the CombinedBackend is a subclass of the default Backend
* class, it returns by that the supported AS version is 2.5.
* The method GetSupportedASVersion() could be implemented
* here, checking the version with all backends.
* But still, the lowest version in common must be
* returned, even if some backends support a higher version.
*
* Created : 29.11.2010
*
* 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 <http://www.gnu.org/licenses/>.
*
* 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;
}
}

View file

@ -1,116 +0,0 @@
<?php
/***********************************************
* File : backend/combined/config.php
* Project : Z-Push
* Descr : configuration file for the
* combined backend.
*
* Created : 29.11.2010
*
* 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 <http://www.gnu.org/licenses/>.
*
* 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,
);
}
}

View file

@ -1,189 +0,0 @@
<?php
/***********************************************
* File : backend/combined/exporter.php
* Project : Z-Push
* Descr : Exporter class for the combined backend.
*
* Created : 11.05.2010
*
* 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 <http://www.gnu.org/licenses/>.
*
* 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");
}
}

View file

@ -1,360 +0,0 @@
<?php
/***********************************************
* File : backend/combined/importer.php
* Project : Z-Push
* Descr : Importer class for the combined backend.
*
* Created : 11.05.2010
*
* 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 <http://www.gnu.org/licenses/>.
*
* 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);
}
}

View file

@ -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

View file

@ -1,7 +0,0 @@
REQUIREMENTS:
php-imap
php-mbstring (optional but recommended)
libawl-php
IMAP server (Dovecot, Courier...)

View file

@ -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

View file

@ -1,227 +0,0 @@
<?php
/***********************************************
* File : config.php
* Project : Z-Push
* Descr : IMAP backend configuration file
*
* Created : 27.11.2012
*
* 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 <http://www.gnu.org/licenses/>.
*
* 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);

File diff suppressed because it is too large Load diff

View file

@ -1,357 +0,0 @@
<?php
function create_calendar_dav($data) {
ZLog::Write(LOGLEVEL_DEBUG, "BackendIMAP->create_calendar_dav(): Creating calendar event");
if (defined('IMAP_MEETING_USE_CALDAV') && IMAP_MEETING_USE_CALDAV) {
$caldav = new BackendCalDAV();
if ($caldav->Logon(Request::GetAuthUser(), Request::GetAuthDomain(), Request::GetAuthPassword())) {
$etag = $caldav->CreateUpdateCalendar($data);
ZLog::Write(LOGLEVEL_DEBUG, sprintf("BackendIMAP->create_calendar_dav(): Calendar created with etag '%s' and data <%s>", $etag, $data));
$caldav->Logoff();
}
else {
ZLog::Write(LOGLEVEL_ERROR, "BackendIMAP->create_calendar_dav(): Error connecting with BackendCalDAV");
}
}
}
function delete_calendar_dav($uid) {
ZLog::Write(LOGLEVEL_DEBUG, sprintf("BackendIMAP->delete_calendar_dav('%s'): Deleting calendar event", $uid));
if ($uid === false) {
ZLog::Write(LOGLEVEL_WARN, "BackendIMAP->delete_calendar_dav(): UID not found; report the full calendar object to developers");
}
else {
if (defined('IMAP_MEETING_USE_CALDAV') && IMAP_MEETING_USE_CALDAV) {
$caldav = new BackendCalDAV();
if ($caldav->Logon(Request::GetAuthUser(), Request::GetAuthDomain(), Request::GetAuthPassword())) {
$events = $caldav->FindCalendar($uid);
if (count($events) == 1) {
$href = $events[0]["href"];
ZLog::Write(LOGLEVEL_DEBUG, sprintf("BackendIMAP->delete_calendar_dav(): found event with href '%s', deleting", $href));
// Delete event
$res = $caldav->DeleteCalendar($href);
if ($res) {
ZLog::Write(LOGLEVEL_DEBUG, "BackendIMAP->delete_calendar_dav(): event deleted");
}
else {
ZLog::Write(LOGLEVEL_ERROR, "BackendIMAP->delete_calendar_dav(): error removing event, we will end with zombie events");
}
$caldav->Logoff();
}
else {
ZLog::Write(LOGLEVEL_ERROR, "BackendIMAP->delete_calendar_dav(): event not found, we will end with zombie events");
}
}
else {
ZLog::Write(LOGLEVEL_ERROR, "BackendIMAP->delete_calendar_dav(): Error connecting with BackendCalDAV");
}
}
}
}
function update_calendar_attendee($uid, $mailto, $status) {
ZLog::Write(LOGLEVEL_DEBUG, sprintf("BackendIMAP->update_calendar_attendee('%s', '%s', '%s'): Updating calendar event attendee", $uid, $mailto, $status));
$updated = false;
if ($uid === false) {
ZLog::Write(LOGLEVEL_WARN, "BackendIMAP->update_calendar_attendee(): UID not found; report the full calendar object to developers");
}
else {
if (defined('IMAP_MEETING_USE_CALDAV') && IMAP_MEETING_USE_CALDAV) {
$caldav = new BackendCalDAV();
if ($caldav->Logon(Request::GetAuthUser(), Request::GetAuthDomain(), Request::GetAuthPassword())) {
$events = $caldav->FindCalendar($uid);
if (count($events) == 1) {
$href = $events[0]["href"];
$etag = $events[0]["etag"];
ZLog::Write(LOGLEVEL_DEBUG, sprintf("BackendIMAP->update_calendar_attendee(): found event with href '%s' etag '%s'; updating", $href, $etag));
// Get Attendee status
$old_status = "";
if (strcasecmp($old_status, $status) != 0) {
ZLog::Write(LOGLEVEL_DEBUG, sprintf("BackendIMAP->update_calendar_attendee(): Before <%s>", $events[0]["data"]));
$ical = new iCalComponent();
$ical->ParseFrom($events[0]["data"]);
$ical->SetCPParameterValue("VEVENT", "ATTENDEE", "PARTSTAT", strtoupper($status), $mailto);
ZLog::Write(LOGLEVEL_DEBUG, sprintf("BackendIMAP->update_calendar_attendee(): After <%s>", $ical->Render()));
$etag = $caldav->CreateUpdateCalendar($ical->Render(), $href, $etag);
ZLog::Write(LOGLEVEL_DEBUG, sprintf("BackendIMAP->update_calendar_attendee(): Calendar updated with etag '%s'", $etag));
// Update new status
$updated = true;
}
$caldav->Logoff();
}
else {
ZLog::Write(LOGLEVEL_ERROR, "BackendIMAP->update_calendar_attendee(): event not found or duplicated event");
}
}
else {
ZLog::Write(LOGLEVEL_ERROR, "BackendIMAP->update_calendar_attendee(): Error connecting with BackendCalDAV");
}
}
}
return $updated;
}
/**
* Detect if one message has one VCALENDAR part
*
* @param Mail_mimeDecode $message
* @return boolean
* @access private
*/
function has_calendar_object($message) {
if (is_calendar($message)) {
return true;
}
else {
if(isset($message->parts)) {
for ($i = 0; $i < count($message->parts); $i++) {
if (is_calendar($message->parts[$i])) {
return true;
}
}
}
}
return false;
}
/**
* Detect if the message-part is VCALENDAR
* Content-Type: text/calendar;
*
* @param Mail_mimeDecode $message
* @return boolean
* @access private
*/
function is_calendar($message) {
return isset($message->ctype_primary) && isset($message->ctype_secondary) && $message->ctype_primary == "text" && $message->ctype_secondary == "calendar";
}
/**
* Converts a text/calendar part into SyncMeetingRequest
* This is called on received messages, it's not called for events generated from the mobile
*
* @access private
* @param $part MIME part
* @param $output SyncMail object
* @param $is_sent_folder boolean
*/
function parse_meeting_calendar($part, &$output, $is_sent_folder) {
$ical = new iCalComponent();
$ical->ParseFrom($part->body);
ZLog::Write(LOGLEVEL_WBXML, sprintf("BackendIMAP->parse_meeting_calendar(): %s", $part->body));
// Get UID
$uid = false;
$props = $ical->GetPropertiesByPath("VEVENT/UID");
if (count($props) > 0) {
$uid = $props[0]->Value();
}
$method = false;
$props = $ical->GetPropertiesByPath("VCALENDAR/METHOD");
if (count($props) > 0) {
$method = strtolower($props[0]->Value());
ZLog::Write(LOGLEVEL_DEBUG, sprintf("BackendIMAP->parse_meeting_calendar(): Using method from vcalendar object: %s", $method));
}
else {
if (isset($part->ctype_parameters["method"])) {
$method = strtolower($part->ctype_parameters["method"]);
ZLog::Write(LOGLEVEL_DEBUG, sprintf("BackendIMAP->parse_meeting_calendar(): Using method from mime part object: %s", $method));
}
}
if ($method === false) {
ZLog::Write(LOGLEVEL_WARN, sprintf("BackendIMAP->parse_meeting_calendar() - No method header, please report it to the developers"));
$output->messageclass = "IPM.Appointment";
}
else {
switch ($method) {
case "cancel":
$output->messageclass = "IPM.Schedule.Meeting.Canceled";
$output->meetingrequest->disallownewtimeproposal = 1;
ZLog::Write(LOGLEVEL_DEBUG, "BackendIMAP->parse_meeting_calendar(): Event canceled, removing calendar object");
delete_calendar_dav($uid);
break;
case "counter":
ZLog::Write(LOGLEVEL_DEBUG, "BackendIMAP->parse_meeting_calendar(): Counter received");
$output->messageclass = "IPM.Schedule.Meeting.Resp.Tent";
$output->meetingrequest->disallownewtimeproposal = 0;
break;
case "reply":
ZLog::Write(LOGLEVEL_DEBUG, "BackendIMAP->parse_meeting_calendar(): Reply received");
$props = $ical->GetPropertiesByPath('VEVENT/ATTENDEE');
for ($i = 0; $i < count($props); $i++) {
$mailto = $props[$i]->Value();
$props_params = $props[$i]->Parameters();
$status = strtolower($props_params["PARTSTAT"]);
if (!$is_sent_folder) {
// Only evaluate received replies, not sent
$res = update_calendar_attendee($uid, $mailto, $status);
}
else {
$res = true;
}
if ($res) {
// Only set messageclass for replies changing my calendar object
switch ($status) {
case "accepted":
$output->messageclass = "IPM.Schedule.Meeting.Resp.Pos";
ZLog::Write(LOGLEVEL_DEBUG, "BackendIMAP->parse_meeting_calendar(): Update attendee -> accepted");
break;
case "needs-action":
$output->messageclass = "IPM.Schedule.Meeting.Resp.Tent";
ZLog::Write(LOGLEVEL_DEBUG, "BackendIMAP->parse_meeting_calendar(): Update attendee -> needs-action");
break;
case "tentative":
$output->messageclass = "IPM.Schedule.Meeting.Resp.Tent";
ZLog::Write(LOGLEVEL_DEBUG, "BackendIMAP->parse_meeting_calendar(): Update attendee -> tentative");
break;
case "declined":
$output->messageclass = "IPM.Schedule.Meeting.Resp.Neg";
ZLog::Write(LOGLEVEL_DEBUG, "BackendIMAP->parse_meeting_calendar(): Update attendee -> declined");
break;
default:
ZLog::Write(LOGLEVEL_WARN, sprintf("BackendIMAP->parse_meeting_calendar() - Unknown reply status <%s>, please report it to the developers", $status));
$output->messageclass = "IPM.Appointment";
break;
}
}
}
$output->meetingrequest->disallownewtimeproposal = 1;
break;
case "request":
$output->messageclass = "IPM.Schedule.Meeting.Request";
$output->meetingrequest->disallownewtimeproposal = 0;
ZLog::Write(LOGLEVEL_DEBUG, "BackendIMAP->parse_meeting_calendar(): New request");
// New meeting, we don't create it now, because we need to confirm it first, but if we don't create it we won't see it in the calendar
break;
default:
ZLog::Write(LOGLEVEL_WARN, sprintf("BackendIMAP->parse_meeting_calendar() - Unknown method <%s>, please report it to the developers", strtolower($part->headers["method"])));
$output->messageclass = "IPM.Appointment";
$output->meetingrequest->disallownewtimeproposal = 0;
break;
}
}
$props = $ical->GetPropertiesByPath('VEVENT/DTSTAMP');
if (count($props) == 1) {
$output->meetingrequest->dtstamp = Utils::MakeUTCDate($props[0]->Value());
}
$props = $ical->GetPropertiesByPath('VEVENT/UID');
if (count($props) == 1) {
$output->meetingrequest->globalobjid = $props[0]->Value();
}
$props = $ical->GetPropertiesByPath('VEVENT/DTSTART');
if (count($props) == 1) {
$output->meetingrequest->starttime = Utils::MakeUTCDate($props[0]->Value());
if (strlen($props[0]->Value()) == 8) {
$output->meetingrequest->alldayevent = 1;
}
}
$props = $ical->GetPropertiesByPath('VEVENT/DTEND');
if (count($props) == 1) {
$output->meetingrequest->endtime = Utils::MakeUTCDate($props[0]->Value());
if (strlen($props[0]->Value()) == 8) {
$output->meetingrequest->alldayevent = 1;
}
}
$props = $ical->GetPropertiesByPath('VEVENT/ORGANIZER');
if (count($props) == 1) {
$output->meetingrequest->organizer = str_ireplace("MAILTO:", "", $props[0]->Value());
}
$props = $ical->GetPropertiesByPath('VEVENT/LOCATION');
if (count($props) == 1) {
$output->meetingrequest->location = $props[0]->Value();
}
$props = $ical->GetPropertiesByPath('VEVENT/CLASS');
if (count($props) == 1) {
switch ($props[0]->Value()) {
case "PUBLIC":
$output->meetingrequest->sensitivity = "0";
break;
case "PRIVATE":
$output->meetingrequest->sensitivity = "2";
break;
case "CONFIDENTIAL":
$output->meetingrequest->sensitivity = "3";
break;
default:
ZLog::Write(LOGLEVEL_DEBUG, sprintf("BackendIMAP->parse_meeting_calendar() - No sensitivity class. Using 2"));
$output->meetingrequest->sensitivity = "2";
break;
}
}
// Get $tz from first timezone
$props = $ical->GetPropertiesByPath("VTIMEZONE/TZID");
if (count($props) > 0) {
// TimeZones shouldn't have dots
$tzname = str_replace(".", "", $props[0]->Value());
$tz = TimezoneUtil::GetFullTZFromTZName($tzname);
}
else {
$tz = TimezoneUtil::GetFullTZ();
}
$output->meetingrequest->timezone = base64_encode(TimezoneUtil::GetSyncBlobFromTZ($tz));
// Fixed values
$output->meetingrequest->instancetype = 0;
$output->meetingrequest->responserequested = 1;
$output->meetingrequest->busystatus = 2;
// TODO: reminder
$output->meetingrequest->reminder = "";
}
/**
* Modify a text/calendar part to transform it in a reply
*
* @access private
* @param $part MIME part
* @param $response Response numeric value
* @param $condition_value string
* @return string MIME text/calendar
*/
function reply_meeting_calendar($part, $response, $username) {
$status_attendee = "ACCEPTED"; // 1 or default is ACCEPTED
$status_event = "CONFIRMED";
switch ($response) {
case 1:
$status_attendee = "ACCEPTED";
$status_event = "CONFIRMED";
break;
case 2:
$status_attendee = $status_event = "TENTATIVE";
break;
case 3:
// We won't hit this case ever, because we won't create an event if we are rejecting it
$status_attendee = "DECLINED";
$status_event = "CANCELLED";
break;
}
$ical = new iCalComponent();
$ical->ParseFrom($part->body);
$ical->SetPValue("METHOD", "REPLY");
$ical->SetCPParameterValue("VEVENT", "STATUS", $status_event, null);
// Update my information as attendee, but only mine
$ical->SetCPParameterValue("VEVENT", "ATTENDEE", "PARTSTAT", $status_attendee, sprintf("MAILTO:%s", $username));
$ical->SetCPParameterValue("VEVENT", "ATTENDEE", "RSVP", null, sprintf("MAILTO:%s", $username));
return $ical->Render();
}

View file

@ -1,307 +0,0 @@
<?php
/**
* Add extra parts (not text; inlined or attached parts) to a mimepart object.
*
* @param Mail_mimePart $email reference to the object
* @param array $parts array of parts
*
* @access private
* @return void
*/
function add_extra_sub_parts(&$email, $parts) {
if (isset($parts)) {
foreach ($parts as $part) {
$new_part = null;
// Only if it's an attachment we will add the text parts, because all the inline/no disposition have been already added
if (isset($part->disposition) && $part->disposition == "attachment") {
// it's an attachment
$new_part = add_sub_part($email, $part);
}
else {
if (isset($part->ctype_primary) && $part->ctype_primary != "text" && $part->ctype_primary != "multipart") {
// it's not a text part or a multipart
$new_part = add_sub_part($email, $part);
}
}
if (isset($part->parts)) {
// We add sub-parts to the new part (if any), not to the main message. Recursive calling
if ($new_part === null) {
add_extra_sub_parts($email, $part->parts);
}
else {
add_extra_sub_parts($new_part, $part->parts);
}
}
}
}
}
/**
* Add a subpart to a mimepart object.
*
* @param Mail_mimePart $email reference to the object
* @param object $part message part
*
* @access private
* @return void
*/
function add_sub_part(&$email, $part) {
//http://tools.ietf.org/html/rfc4021
$new_part = null;
$params = array();
$params['content_type'] = '';
if (isset($part) && isset($email)) {
if (isset($part->ctype_primary)) {
$params['content_type'] = $part->ctype_primary;
}
if (isset($part->ctype_secondary)) {
$params['content_type'] .= '/' . $part->ctype_secondary;
}
if (isset($part->ctype_parameters)) {
foreach ($part->ctype_parameters as $k => $v) {
if(strcasecmp($k, 'boundary') != 0) {
$params['content_type'] .= '; ' . $k . '=' . $v;
}
}
}
if (isset($part->disposition)) {
$params['disposition'] = $part->disposition;
}
//FIXME: dfilename => filename
if (isset($part->d_parameters)) {
$params['headers_charset'] = 'utf-8';
foreach ($part->d_parameters as $k => $v) {
$params[$k] = $v;
}
}
foreach ($part->headers as $k => $v) {
switch($k) {
case "content-description":
$params['description'] = $v;
break;
case "content-type":
case "content-disposition":
case "content-transfer-encoding":
// Do nothing, we already did
break;
case "content-id":
$params['cid'] = str_replace('<', '', str_replace('>', '', $v));
break;
default:
$params[$k] = $v;
break;
}
}
// If not exist body, the part will be multipart/alternative, so we don't add encoding
if (!isset($params['encoding']) && isset($part->body)) {
$params['encoding'] = 'base64';
}
// We could not have body; recursive messages
$new_part = $email->addSubPart(isset($part->body) ? $part->body : "", $params);
unset($params);
}
// return the new part
return $new_part;
}
/**
* Add a subpart to a mimepart object.
*
* @param Mail_mimePart $email reference to the object
* @param object $part message part
*
* @access private
* @return void
*/
function change_charset_and_add_subparts(&$email, $part) {
if (isset($part)) {
$new_part = null;
if (isset($part->ctype_parameters['charset'])) {
$part->ctype_parameters['charset'] = 'UTF-8';
$new_part = add_sub_part($email, $part);
}
else {
// We don't add the charset because it could be a non-text part
$new_part = add_sub_part($email, $part);
}
if (isset($part->parts)) {
foreach ($part->parts as $subpart) {
// Subparts are added to the part, not the main message
change_charset_and_add_subparts($new_part, $subpart);
}
}
}
}
/**
* Creates a MIME message from a decoded MIME message, reencoding and fixing the text.
*
* @param array $message array returned from Mail_mimeDecode->decode
*
* @access public
* @return string MIME message
*/
function build_mime_message($message) {
$finalEmail = new Mail_mimePart(isset($message->body) ? $message->body : "", array('headers' => $message->headers));
if (isset($message->parts)) {
foreach ($message->parts as $part) {
change_charset_and_add_subparts($finalEmail, $part);
}
}
$mimeHeaders = Array();
$mimeHeaders['headers'] = Array();
$is_mime = false;
foreach ($message->headers as $key => $value) {
switch($key) {
case 'content-type':
$new_value = $message->ctype_primary . "/" . $message->ctype_secondary;
$is_mime = (strcasecmp($message->ctype_primary, 'multipart') == 0);
if (isset($message->ctype_parameters)) {
foreach ($message->ctype_parameters as $ckey => $cvalue) {
switch($ckey) {
case 'charset':
$new_value .= '; charset="UTF-8"';
break;
case 'boundary':
// Do nothing, we are encoding also the headers
break;
default:
$new_value .= '; ' . $ckey . '="' . $cvalue . '"';
break;
}
}
}
$mimeHeaders['content_type'] = $new_value;
break;
case 'content-transfer-encoding':
if (strcasecmp($value, "base64") == 0 || strcasecmp($value, "binary") == 0) {
$mimeHeaders['encoding'] = "base64";
}
else {
$mimeHeaders['encoding'] = "8bit";
}
break;
case 'content-id':
$mimeHeaders['cid'] = $value;
break;
case 'content-location':
$mimeHeaders['location'] = $value;
break;
case 'content-disposition':
$mimeHeaders['disposition'] = $value;
break;
case 'content-description':
$mimeHeaders['description'] = $value;
break;
default:
if (is_array($value)) {
foreach($value as $v) {
$mimeHeaders['headers'][$key] = $v;
}
}
else {
$mimeHeaders['headers'][$key] = $value;
}
break;
}
}
$finalEmail = new Mail_mimePart(isset($message->body) ? $message->body : "", $mimeHeaders);
unset($mimeHeaders['headers']);
unset($mimeHeaders);
if (isset($message->parts)) {
foreach ($message->parts as $part) {
change_charset_and_add_subparts($finalEmail, $part);
}
}
$boundary = '=_' . md5(rand() . microtime());
$finalEmail = $finalEmail->encode($boundary);
$headers = "";
$mimePart = new Mail_mimePart();
foreach ($finalEmail['headers'] as $key => $value) {
if (is_array($value)) {
foreach ($values as $ikey => $ivalue) {
$headers .= $key . ": " . $mimePart->encodeHeader($key, $ivalue, "utf-8", "base64") . "\n";
}
}
else {
$headers .= $key . ": " . $mimePart->encodeHeader($key, $value, "utf-8", "base64") . "\n";
}
}
unset($mimePart);
if ($is_mime) {
$built_message = "$headers\nThis is a multi-part message in MIME format.\n".$finalEmail['body'];
}
else {
$built_message = "$headers\n".$finalEmail['body'];
}
unset($headers);
unset($finalEmail);
return $built_message;
}
/**
* Detect if the message-part is SMIME
*
* @param Mail_mimeDecode $message
* @return boolean
* @access public
*/
function is_smime($message) {
$res = false;
if (isset($message->ctype_primary) && isset($message->ctype_secondary)) {
$smime_types = array(array("multipart", "signed"), array("application", "pkcs7-mime"), array("application", "x-pkcs7-mime"), array("multipart", "encrypted"));
for ($i = 0; $i < count($smime_types) && !$res; $i++) {
$res = ($message->ctype_primary == $smime_types[$i][0] && $message->ctype_secondary == $smime_types[$i][1]);
}
}
return $res;
}
/**
* Detect if the message-part is SMIME, encrypted but not signed
* #190, KD 2015-06-04
*
* @param Mail_mimeDecode $message
* @return boolean
* @access public
*/
function is_encrypted($message) {
$res = false;
if (is_smime($message) && !($message->ctype_primary == "multipart" && $message->ctype_secondary == "signed")) {
$res = true;
}
return $res;
}
/**
* Detect if the message is multipart.
* #198, KD 2015-06-15
*
* @param Mail_mimeDecode $message
* @return boolean
* @access public
*/
function is_multipart($message) {
return isset($message->ctype_primary) && $message->ctype_primary == "multipart";
}

View file

@ -1,232 +0,0 @@
<?php
/**
* Returns the default value for "From"
*
* @access private
* @return string
*/
function getDefaultFromValue($username, $domain) {
$v = "";
if (defined('IMAP_DEFAULTFROM')) {
switch (IMAP_DEFAULTFROM) {
case 'username':
$v = $username;
break;
case 'domain':
$v = $domain;
break;
case 'ldap':
$v = getIdentityFromLdap($username, $domain, IMAP_FROM_LDAP_FROM, true);
break;
case 'sql':
$v = getIdentityFromSql($username, $domain, IMAP_FROM_SQL_FROM, true);
break;
case 'passwd':
$v = getIdentityFromPasswd($username, $domain, 'FROM', true);
break;
default:
$v = $username . IMAP_DEFAULTFROM;
break;
}
}
return $v;
}
/**
* Return the default value for "FullName"
*
* @access private
* @param string $username Username
* @return string
*/
function getDefaultFullNameValue($username, $domain) {
$v = $username;
if (defined('IMAP_DEFAULTFROM')) {
switch (IMAP_DEFAULTFROM) {
case 'ldap':
$v = getIdentityFromSql($username, $domain, IMAP_FROM_LDAP_FULLNAME, false);
break;
case 'sql':
$v = getIdentityFromSql($username, $domain, IMAP_FROM_SQL_FULLNAME, false);
break;
case 'passwd':
$v = getIdentityFromPasswd($username, $domain, 'FULLNAME', false);
break;
}
}
return $v;
}
/**
* Generate the "From"/"FullName" value stored in a LDAP server
*
* @access private
* @params string $username username value
* @params string $domain domain value
* @params string $identity pattern to fill with ldap values
* @params boolean $encode if the result should be encoded as a header
* @return string
*/
function getIdentityFromLdap($username, $domain, $identity, $encode = true) {
$ret_value = $username;
$ldap_conn = null;
try {
$ldap_conn = ldap_connect(IMAP_FROM_LDAP_SERVER, IMAP_FROM_LDAP_SERVER_PORT);
if ($ldap_conn) {
ZLog::Write(LOGLEVEL_DEBUG, sprintf("BackendIMAP->getIdentityFromLdap() - Connected to LDAP"));
ldap_set_option($ldap_conn, LDAP_OPT_PROTOCOL_VERSION, 3);
ldap_set_option($ldap_conn, LDAP_OPT_REFERRALS, 0);
$ldap_bind = ldap_bind($ldap_conn, IMAP_FROM_LDAP_USER, IMAP_FROM_LDAP_PASSWORD);
if ($ldap_bind) {
ZLog::Write(LOGLEVEL_DEBUG, sprintf("BackendIMAP->getIdentityFromLdap() - Authenticated in LDAP"));
$filter = str_replace('#username', $username, str_replace('#domain', $domain, IMAP_FROM_LDAP_QUERY));
ZLog::Write(LOGLEVEL_DEBUG, sprintf("BackendIMAP->getIdentityFromLdap() - Searching From with filter: %s", $filter));
$search = ldap_search($ldap_conn, IMAP_FROM_LDAP_BASE, $filter, unserialize(IMAP_FROM_LDAP_FIELDS));
$items = ldap_get_entries($ldap_conn, $search);
if ($items['count'] > 0) {
$ret_value = $identity;
ZLog::Write(LOGLEVEL_DEBUG, sprintf("BackendIMAP->getIdentityFromLdap() - Found entry in LDAP. Generating From"));
// We get the first object. It's your responsability to make the query unique
foreach (unserialize(IMAP_FROM_LDAP_FIELDS) as $field) {
$ret_value = str_replace('#'.$field, $items[0][$field][0], $ret_value);
}
if ($encode) {
$ret_value = encodeFrom($ret_value);
}
}
else {
ZLog::Write(LOGLEVEL_DEBUG, sprintf("BackendIMAP->getIdentityFromLdap() - No entry found in LDAP"));
}
}
else {
ZLog::Write(LOGLEVEL_DEBUG, sprintf("BackendIMAP->getIdentityFromLdap() - Not authenticated in LDAP server"));
}
}
else {
ZLog::Write(LOGLEVEL_DEBUG, sprintf("BackendIMAP->getIdentityFromLdap() - Not connected to LDAP server"));
}
}
catch(Exception $ex) {
ZLog::Write(LOGLEVEL_WARN, sprintf("BackendIMAP->getIdentityFromLdap() - Error getting From value from LDAP server: %s", $ex));
}
if ($ldap_conn != null) {
ldap_close($ldap_conn);
}
return $ret_value;
}
/**
* Generate the "From" value stored in a SQL Database
*
* @access private
* @params string $username username value
* @params string $domain domain value
* @return string
*/
function getIdentityFromSql($username, $domain, $identity, $encode = true) {
$ret_value = $username;
$dbh = $sth = $record = null;
try {
$dbh = new PDO(IMAP_FROM_SQL_DSN, IMAP_FROM_SQL_USER, IMAP_FROM_SQL_PASSWORD, unserialize(IMAP_FROM_SQL_OPTIONS));
ZLog::Write(LOGLEVEL_DEBUG, sprintf("BackendIMAP->getIdentityFromSql() - Connected to SQL Database"));
$sql = str_replace('#username', $username, str_replace('#domain', $domain, IMAP_FROM_SQL_QUERY));
ZLog::Write(LOGLEVEL_DEBUG, sprintf("BackendIMAP->getIdentityFromSql() - Searching From with filter: %s", $sql));
$sth = $dbh->prepare($sql);
$sth->execute();
$record = $sth->fetch(PDO::FETCH_ASSOC);
if ($record) {
ZLog::Write(LOGLEVEL_DEBUG, sprintf("BackendIMAP->getIdentityFromSql() - Found entry in SQL Database. Generating From"));
$ret_value = $identity;
foreach (unserialize(IMAP_FROM_SQL_FIELDS) as $field) {
$ret_value = str_replace('#'.$field, $record[$field], $ret_value);
}
if ($encode) {
$ret_value = encodeFrom($ret_value);
}
}
else {
ZLog::Write(LOGLEVEL_DEBUG, sprintf("BackendIMAP->getIdentityFromSql() - No entry found in SQL Database"));
}
}
catch(PDOException $ex) {
ZLog::Write(LOGLEVEL_WARN, sprintf("BackendIMAP->getIdentityFromSql() - Error getting From value from SQL Database: %s", $ex));
}
$dbh = $sth = $record = null;
return $ret_value;
}
/**
* Generate the "From" value from the local posix passwd database
*
* @access private
* @params string $username username value
* @params string $domain domain value
* @return string
*/
function getIdentityFromPasswd($username, $domain, $identity, $encode = true) {
$ret_value = $username;
try {
ZLog::Write(LOGLEVEL_DEBUG, sprintf("BackendIMAP->getIdentityFromPasswd() - Fetching info for user %s", $username));
$local_user = posix_getpwnam($username);
if ($local_user) {
$tmp = $local_user['gecos'];
$tmp = explode(',', $tmp);
$name = $tmp[0];
unset($tmp);
switch ($identity) {
case 'FROM':
if (strlen($domain) > 0) {
$ret_value = sprintf("%s <%s@%s>", $name, $username, $domain);
} else {
ZLog::Write(LOGLEVEL_WARN, sprintf("BackendIMAP->getIdentityFromPasswd() - No domain passed. Cannot construct From address."));
}
break;
case 'FULLNAME':
$ret_value = sprintf("%s", $name);
break;
}
if ($encode) {
$ret_value = encodeFrom($ret_value);
}
} else {
ZLog::Write(LOGLEVEL_DEBUG, sprintf("BackendIMAP->getIdentityFromPasswd() - No entry found in Password database"));
}
}
catch(Exception $ex) {
ZLog::Write(LOGLEVEL_WARN, sprintf("BackendIMAP->getIdentityFromPasswd() - Error getting From value from passwd database: %s", $ex));
}
return $ret_value;
}
/**
* Encode the From value as Base64
*
* @access private
* @param string $from From value
* @return string
*/
function encodeFrom($from) {
$items = explode("<", $from);
$name = trim($items[0]);
return "=?UTF-8?B?" . base64_encode($name) . "?= <" . $items[1];
}

View file

@ -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

View file

@ -1,58 +0,0 @@
<?php
/***********************************************
* File : config.php
* Project : Z-Push
* Descr : LDAP backend configuration file
*
* Created : 27.11.2012
*
* Copyright 2007 - 2015 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 <http://www.gnu.org/licenses/>.
*
* Consult LICENSE file for details
************************************************/
// **********************
// BackendLDAP settings
// **********************
// Server address
define('LDAP_SERVER', 'localhost');
// Server Port
define('LDAP_SERVER_PORT', '389');
// LDAP USER DN
define('LDAP_USER_DN', 'uid=%u,ou=mailaccount,dc=phppush,dc=com');
// LDAP BASE DNS
define('LDAP_BASE_DNS', 'Contacts:ou=addressbook,uid=%u,ou=mailaccount,dc=phppush,dc=com'); //Multiple values separator is |

View file

@ -1,590 +0,0 @@
<?php
/***********************************************
* File : ldap.php
* Project : PHP-Push
* Descr : This backend is based on
* 'BackendDiff' and implements an
* (Open)LDAP interface
*
* Created : 07.04.2012
*
* Copyright 2012 - 2014 Jean-Louis Dupond
* Jean-Louis Dupond released this code as AGPLv3 here: https://github.com/dupondje/PHP-Push-2/issues/93
* Copyright 2015 - Francisco Miguel Biete
*
* 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 <http://www.gnu.org/licenses/>.
*
* Consult LICENSE file for details
************************************************/
// config file
require_once("backend/ldap/config.php");
class BackendLDAP extends BackendDiff {
private $ldap_link;
private $user;
public function Logon($username, $domain, $password) {
$this->user = $username;
$user_dn = str_replace('%u', $username, LDAP_USER_DN);
$this->ldap_link = ldap_connect(LDAP_SERVER, LDAP_SERVER_PORT);
ldap_set_option($this->ldap_link, LDAP_OPT_PROTOCOL_VERSION, 3);
if (ldap_bind($this->ldap_link, $user_dn, $password)) {
ZLog::Write(LOGLEVEL_INFO, sprintf("BackendLDAP->Logon(): User '%s' is authenticated on LDAP", $username));
return true;
}
else {
ZLog::Write(LOGLEVEL_INFO, sprintf("BackendLDAP->Logon(): User '%s' is not authenticated on LDAP. Error: ", $username, ldap_error($this->ldap_link)));
return false;
}
}
public function Logoff() {
if (ldap_unbind($this->ldap_link)) {
ZLog::Write(LOGLEVEL_INFO, sprintf("BackendLDAP->Logoff(): Disconnection successfull."));
}
else {
ZLog::Write(LOGLEVEL_INFO, sprintf("BackendLDAP->Logoff(): Disconnection failed. Error: %s", ldap_error($this->ldap_link)));
}
return true;
}
public function SendMail($sm) {
return false;
}
public function GetAttachmentData($attname) {
return false;
}
public function GetWasteBasket() {
return false;
}
public function GetFolderList() {
ZLog::Write(LOGLEVEL_DEBUG, sprintf("BackendLDAP->GetFolderList(): Getting all folders."));
$contacts = array();
$dns = explode("|", LDAP_BASE_DNS);
foreach ($dns as $dn) {
$name = explode(":", $dn);
$folder = $this->StatFolder($name[0]);
$contacts[] = $folder;
}
return $contacts;
}
public function GetFolder($id) {
ZLog::Write(LOGLEVEL_DEBUG, sprintf("BackendLDAP->GetFolder('%s')", $id));
$folder = new SyncFolder();
$folder->serverid = $id;
$folder->parentid = "0";
$folder->displayname = $id;
$folder->type = SYNC_FOLDER_TYPE_CONTACT;
return $folder;
}
public function StatFolder($id) {
ZLog::Write(LOGLEVEL_DEBUG, sprintf("BackendLDAP->StatFolder('%s')", $id));
$folder = $this->GetFolder($id);
$stat = array();
$stat["id"] = $id;
$stat["parent"] = $folder->parentid;
$stat["mod"] = $folder->displayname;
return $stat;
}
public function ChangeFolder($folderid, $oldid, $displayname, $type) {
ZLog::Write(LOGLEVEL_DEBUG, sprintf("BackendLDAP->ChangeFolder('%s','%s','%s','%s')", $folderid, $oldid, $displayname, $type));
return false;
}
public function DeleteFolder($id, $parentid) {
ZLog::Write(LOGLEVEL_DEBUG, sprintf("BackendLDAP->DeleteFolder('%s','%s')", $id, $parentid));
return false;
}
public function GetMessageList($folderid, $cutoffdate) {
ZLog::Write(LOGLEVEL_DEBUG, sprintf("BackendLDAP->GetMessageList('%s','%s')", $folderid, $cutoffdate));
$cutoff = date("YmdHis\Z", $cutoffdate);
$filter = sprintf('(modifyTimestamp>=%s)', $cutoff);
$attributes = array("entryUUID", "modifyTimestamp");
$messages = array();
$base_dns = explode("|", LDAP_BASE_DNS);
foreach ($base_dns as $base_dn) {
$folder = explode(":", $base_dn);
if ($folder[0] == $folderid) {
$base_dn = str_replace('%u', $this->user, $folder[1]);
$results = ldap_list($this->ldap_link, $base_dn, $filter, $attributes);
ZLog::Write(LOGLEVEL_DEBUG, sprintf("BackendLDAP->GetMessageList(): Got %s contacts in base_dn '%s'.", ldap_count_entries($this->ldap_link, $results), $base_dn));
$entries = ldap_get_entries($this->ldap_link, $results);
for ($i = 0; $i < $entries["count"]; $i++) {
$message = array();
$message["id"] = $entries[$i]["entryuuid"][0];
$message["mod"] = $entries[$i]["modifytimestamp"][0];
$message["flags"] = "1";
$messages[] = $message;
}
}
}
return $messages;
}
public function GetMessage($folderid, $id, $contentparameters) {
ZLog::Write(LOGLEVEL_DEBUG, sprintf("BackendLDAP->GetMessage('%s','%s')", $folderid, $id));
$truncsize = Utils::GetTruncSize($contentparameters->GetTruncation());
$base_dns = explode("|", LDAP_BASE_DNS);
foreach ($base_dns as $base_dn) {
$folder = explode(":", $base_dn);
if ($folder[0] == $folderid) {
$base_dn = str_replace('%u', $this->user, $folder[1]);
$result_id = ldap_list($this->ldap_link, $base_dn, "(entryUUID=".$id.")");
if ($result_id) {
$entry_id = ldap_first_entry($this->ldap_link, $result_id);
if ($entry_id) {
return $this->_ParseLDAPMessage($result_id, $entry_id, $truncsize);
}
}
}
}
}
private function _ParseLDAPMessage($result_id, $entry_id, $truncsize = -1) {
$contact = new SyncContact();
$values = ldap_get_attributes($this->ldap_link, $entry_id);
for ($i = 0; $i < $values["count"]; $i++) {
$name = $values[$i];
$value = $values[$name][0];
switch ($name) {
//person
case "cn":
case "fileAs":
$contact->fileas = $value;
break;
case "sn":
$contact->lastname = $value;
break;
//inetOrgPerson
case "departmentNumber":
$contact->department = $value;
break;
case "givenName":
$contact->firstname = $value;
break;
case "homePhone":
$contact->homephonenumber = $value;
if ($values[$name]["count"] >= 2) {
$contact->home2phonenumber = $values[$name][1];
}
break;
case "jpegPhoto":
$contact->picture = base64_encode($value);
break;
case "labeledURI":
$contact->webpage = $value;
break;
case "mail":
$contact->email1address = $value;
if ($values[$name]["count"] >= 2) {
$contact->email2address = $values[$name][1];
}
if ($values[$name]["count"] >= 3) {
$contact->email3address = $values[$name][2];
}
break;
case "mobile":
$contact->mobilephonenumber = $value;
break;
case "o":
$contact->companyname = $value;
break;
case "pager":
$contact->pagernumber = $value;
break;
case "secretary":
case "assistantName":
$contact->assistantname = $value;
break;
//organizationalPerson
case "l":
$contact->businesscity = $value;
break;
case "ou":
$contact->department = $value;
break;
case "physicalDeliveryOfficeName":
$contact->officelocation = $value;
break;
case "postalCode":
$contact->businesspostalcode = $value;
break;
case "st":
$contact->businessstate = $value;
break;
case "street":
$contact->businessstreet = $value;
break;
case "telephoneNumber":
$contact->businessphonenumber = $value;
if ($values[$name]["count"] >= 2) {
$contact->business2phonenumber = $values[$name][1];
}
break;
case "title":
$contact->title = $value;
break;
case "description":
case "note":
if (Request::GetProtocolVersion() >= 12.0) {
$contact->asbody = new SyncBaseBody();
$contact->asbody->type = SYNC_BODYPREFERENCE_PLAIN;
$contact->asbody->data = $value;
if ($truncsize > 0 && $truncsize < strlen($contact->asbody->data)) {
$contact->asbody->truncated = 1;
$contact->asbody->data = Utils::Utf8_truncate($contact->asbody->data, $truncsize);
}
else {
$contact->asbody->truncated = 0;
}
$contact->asbody->estimatedDataSize = strlen($contact->asbody->data);
}
else {
$contact->body = $value;
if ($truncsize > 0 && $truncsize < strlen($contact->body)) {
$contact->bodytruncated = 1;
$contact->body = Utils::Utf8_truncate($contact->body, $truncsize);
}
else {
$contact->bodytruncated = 0;
}
$contact->bodysize = strlen($contact->body);
}
break;
case "assistantPhone":
$contact->assistnamephonenumber = $value;
break;
case "birthDate":
$contact->birthday = $value;
break;
case "anniversary":
$contact->anniversary = $value;
break;
case "businessRole":
$contact->jobtitle = $value;
break;
case "carPhone":
$contact->carphonenumber = $value;
break;
case "facsimileTelephoneNumber":
$contact->businessfaxnumber = $value;
break;
case "homeFacsimileTelephoneNumber":
$contact->homefaxnumber = $value;
break;
case "spouseName":
$contact->spouse = $value;
break;
case "managerName":
$contact->managername = $value;
break;
case "radio":
$contact->radiophonenumber = $value;
break;
}
}
return $contact;
}
public function StatMessage($folderid, $id) {
ZLog::Write(LOGLEVEL_DEBUG, sprintf("BackendLDAP->StatMessage('%s','%s')", $folderid, $id));
$base_dns = explode("|", LDAP_BASE_DNS);
foreach ($base_dns as $base_dn) {
$folder = explode(":", $base_dn);
if ($folder[0] == $folderid) {
$base_dn = str_replace('%u', $this->user, $folder[1]);
$result_id = ldap_list($this->ldap_link, $base_dn, "(entryUUID=".$id.")", array("modifyTimestamp"));
if ($result_id) {
$entry_id = ldap_first_entry($this->ldap_link, $result_id);
if ($entry_id) {
$mod = ldap_get_values($this->ldap_link, $entry_id, "modifyTimestamp");
$message = array();
$message["id"] = $id;
$message["mod"] = $mod[0];
$message["flags"] = "1";
return $message;
}
}
}
}
}
public function ChangeMessage($folderid, $id, $message, $contentParameters) {
ZLog::Write(LOGLEVEL_DEBUG, sprintf("BackendLDAP->ChangeMessage('%s','%s')", $folderid, $id));
$base_dns = explode("|", LDAP_BASE_DNS);
foreach ($base_dns as $base_dn) {
$folder = explode(":", $base_dn);
if ($folder[0] == $folderid) {
$base_dn = str_replace('%u', $this->user, $folder[1]);
$ldap_attributes = $this->_GenerateLDAPArray($message);
$result_id = ldap_list($this->ldap_link, $base_dn, "(entryUUID=".$id.")", array("modifyTimestamp"));
if ($result_id) {
$entry_id = ldap_first_entry($this->ldap_link, $result_id);
if ($entry_id) {
$dn = ldap_get_dn($this->ldap_link, $entry_id);
// We cannot ldap_modify objectClass, but we can use ldap_mod_replace
$ldap_classes = array();
$ldap_classes['objectclass'] = Array("top", "person", "inetOrgPerson", "organizationalPerson", "evolutionPerson");
$mode = ldap_mod_replace($this->ldap_link, $dn, $ldap_classes);
$mod = ldap_modify($this->ldap_link, $dn, $ldap_attributes);
if (!$mod) {
return false;
}
return $this->StatMessage($folderid, $id);
}
else {
$uid = time() . mt_rand(100000, 999999);
$dn = "uid=" . $uid . "," . $base_dn;
$add = ldap_add($this->ldap_link, $dn, $ldap_attributes);
if (!$add) {
return false;
}
$result = ldap_read($this->ldap_link, $dn, "objectClass=*", array("entryUUID"));
$entry = ldap_first_entry($this->ldap_link, $result);
$values = ldap_get_values($this->ldap_link, $entry, "entryUUID");
$entryuuid = $values[0];
return $this->StatMessage($folderid, $entryuuid);
}
}
}
}
return false;
}
private function _GenerateLDAPArray($message) {
$ldap = array();
//Set the Object Class
$ldap["objectClass"] = Array("top", "person", "inetOrgPerson", "organizationalPerson", "evolutionPerson");
//Parse Data
if ($message->fileas) {
$ldap["cn"] = $message->fileas;
$ldap["fileAs"] = $message->fileas;
}
if ($message->lastname) {
$ldap["sn"] = $message->lastname;
}
if ($message->department) {
$ldap["departmentNumber"] = $message->department;
}
if ($message->firstname) {
$ldap["givenName"] = $message->firstname;
}
if ($message->homephonenumber) {
$ldap["homePhone"][0] = $message->homephonenumber;
}
if ($message->home2phonenumber) {
$ldap["homePhone"][1] = $message->home2phonenumber;
}
if ($message->picture) {
$ldap["jpegPhoto"] = base64_decode($message->picture);
}
if ($message->webpage) {
$ldap["labeledURI"] = $message->webpage;
}
if ($message->email1address) {
$ldap["mail"][] = $message->email1address;
}
if ($message->email2address) {
$ldap["mail"][] = $message->email2address;
}
if ($message->email3address) {
$ldap["mail"][] = $message->email3address;
}
if ($message->mobilephonenumber) {
$ldap["mobile"] = $message->mobilephonenumber;
}
if ($message->companyname) {
$ldap["o"] = $message->companyname;
}
if ($message->pagernumber) {
$ldap["pager"] = $message->pagernumber;
}
if ($message->assistantname) {
$ldap["secretary"] = $message->assistantname;
$ldap["assistantName"] = $message->assistantname;
}
if ($message->businesscity) {
$ldap["l"] = $message->businesscity;
}
if ($message->department) {
$ldap["ou"] = $message->department;
}
if ($message->officelocation) {
$ldap["physicalDeliveryOfficeName"] = $message->officelocation;
}
if ($message->businesspostalcode) {
$ldap["postalCode"] = $message->businesspostalcode;
}
if ($message->businessstate) {
$ldap["st"] = $message->businessstate;
}
if ($message->businessstreet) {
$ldap["street"] = $message->businessstreet;
}
if ($message->businessphonenumber) {
$ldap["telephoneNumber"][] = $message->businessphonenumber;
}
if ($message->business2phonenumber) {
$ldap["telephoneNumber"][] = $message->business2phonenumber;
}
if ($message->title) {
$ldap["title"] = $message->title;
}
if ($message->body) {
$ldap["description"] = $message->body;
}
if ($message->asbody) {
$ldap["description"] = $message->asbody->data;
}
if ($message->assistnamephonenumber) {
$ldap["assistantPhone"] = $message->assistnamephonenumber;
}
if ($message->birthday) {
$ldap["birthDate"] = $message->birthday;
}
if ($message->anniversary) {
$ldap["anniversary"] = $message->anniversary;
}
if ($message->jobtitle) {
$ldap["businessRole"] = $message->jobtitle;
}
if ($message->carphonenumber) {
$ldap["carPhone"] = $message->carphonenumber;
}
if ($message->businessfaxnumber) {
$ldap["facsimileTelephoneNumber"] = $message->businessfaxnumber;
}
if ($message->homefaxnumber) {
$ldap["homeFacsimileTelephoneNumber"] = $message->homefaxnumber;
}
if ($message->spouse) {
$ldap["spouseName"] = $message->spouse;
}
if ($message->managername) {
$ldap["managerName"] = $message->managername;
}
if ($message->radiophonenumber) {
$ldap["radio"] = $message->radiophonenumber;
}
return $ldap;
}
public function SetReadFlag($folderid, $id, $flags, $contentParameters) {
return false;
}
public function DeleteMessage($folderid, $id, $contentParameters) {
ZLog::Write(LOGLEVEL_DEBUG, sprintf("BackendLDAP->DeleteMessage('%s','%s')", $folderid, $id));
$base_dns = explode("|", LDAP_BASE_DNS);
foreach ($base_dns as $base_dn) {
$folder = explode(":", $base_dn);
if ($folder[0] == $folderid) {
$base_dn = str_replace('%u', $this->user, $folder[1]);
$result_id = ldap_list($this->ldap_link, $base_dn, "(entryUUID=".$id.")", array("entryUUID"));
if ($result_id) {
$entry_id = ldap_first_entry($this->ldap_link, $result_id);
if ($entry_id) {
$dn = ldap_get_dn($this->ldap_link, $entry_id);
return ldap_delete($this->ldap_link, $dn);
}
}
}
}
return false;
}
public function MoveMessage($folderid, $id, $newfolderid, $contentParameters) {
ZLog::Write(LOGLEVEL_DEBUG, sprintf("BackendLDAP->MoveMessage('%s','%s', '%s')", $folderid, $id, $newfolderid));
$base_dns = explode("|", LDAP_BASE_DNS);
$old = "";
$new = "";
foreach ($base_dns as $base_dn) {
$folder = explode(":", $base_dn);
if ($folder[0] == $folderid) {
$old = str_replace('%u', $this->user, $folder[1]);
}
if ($folder[0] == $newfolderid) {
$new = str_replace('%u', $this->user, $folder[1]);
}
}
$result_id = ldap_list($this->ldap_link, $old, "(entryUUID=".$id.")", array("entryUUID"));
if ($result_id) {
$entry_id = ldap_first_entry($this->ldap_link, $result_id);
if ($entry_id) {
$dn = ldap_get_dn($this->ldap_link, $entry_id);
$newdn = ldap_explode_dn($dn, 0);
return ldap_rename($this->ldap_link, $dn, $newdn[0], true);
}
}
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;
}
}

View file

@ -1,49 +0,0 @@
<?php
/***********************************************
* File : config.php
* Project : Z-Push
* Descr : Maildir backend configuration file
*
* Created : 27.11.2012
*
* 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 <http://www.gnu.org/licenses/>.
*
* Consult LICENSE file for details
************************************************/
// ************************
// BackendMaildir settings
// ************************
define('MAILDIR_BASE', '/tmp');
define('MAILDIR_SUBDIR', 'Maildir');

View file

@ -1,716 +0,0 @@
<?php
/***********************************************
* File : maildir.php
* Project : Z-Push
* Descr : This backend is based on
* 'BackendDiff' which handles the
* intricacies of generating
* differentials from static
* snapshots. This means that the
* implementation here needs no
* state information, and can simply
* return the current state of the
* messages. The diffbackend will
* then compare the current state
* to the known last state of the PDA
* and generate change increments
* from that.
*
* Created : 01.10.2007
*
* 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 <http://www.gnu.org/licenses/>.
*
* Consult LICENSE file for details
************************************************/
// config file
require_once("backend/maildir/config.php");
class BackendMaildir extends BackendDiff {
/**----------------------------------------------------------------------------------------------------------
* default backend methods
*/
/**
* 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) {
return true;
}
/**
* Logs off
*
* @access public
* @return boolean
*/
public function Logoff() {
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
*
* @access public
* @return string
*/
public function GetWasteBasket() {
return false;
}
/**
* Returns the content of the named attachment as stream. The passed attachment identifier is
* the exact string that is returned in the 'AttName' property of an SyncAttachment.
* Any information necessary to find the attachment must be encoded in that 'attname' property.
* Data is written directly (with print $data;)
*
* @param string $attname
*
* @access public
* @return SyncItemOperationsAttachment
* @throws StatusException
*/
public function GetAttachmentData($attname) {
list($id, $part) = explode(":", $attname);
$fn = $this->findMessage($id);
if ($fn == false)
throw new StatusException(sprintf("BackendMaildir->GetAttachmentData('%s'): Error, requested message/attachment can not be found", $attname), SYNC_ITEMOPERATIONSSTATUS_INVALIDATT);
// Parse e-mail
$rfc822 = file_get_contents($this->getPath() . "/$fn");
$message = Mail_mimeDecode::decode(array('decode_headers' => true, 'decode_bodies' => true, 'include_bodies' => true, 'input' => $rfc822, 'crlf' => "\n", 'charset' => 'utf-8'));
$attachment = new SyncItemOperationsAttachment();
$attachment->data = StringStreamWrapper::Open($message->parts[$part]->body);
if (isset($message->parts[$part]->ctype_primary) && isset($message->parts[$part]->ctype_secondary))
$attachment->contenttype = $message->parts[$part]->ctype_primary .'/'.$message->parts[$part]->ctype_secondary;
return $attachment;
}
/**----------------------------------------------------------------------------------------------------------
* 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() {
$folders = array();
$inbox = array();
$inbox["id"] = "root";
$inbox["parent"] = "0";
$inbox["mod"] = "Inbox";
$folders[]=$inbox;
$sub = array();
$sub["id"] = "sub";
$sub["parent"] = "root";
$sub["mod"] = "Sub";
// $folders[]=$sub;
return $folders;
}
/**
* Returns an actual SyncFolder object
*
* @param string $id id of the folder
*
* @access public
* @return object SyncFolder with information
*/
public function GetFolder($id) {
if($id == "root") {
$inbox = new SyncFolder();
$inbox->serverid = $id;
$inbox->parentid = "0"; // Root
$inbox->displayname = "Inbox";
$inbox->type = SYNC_FOLDER_TYPE_INBOX;
return $inbox;
} else if($id == "sub") {
$inbox = new SyncFolder();
$inbox->serverid = $id;
$inbox->parentid = "root";
$inbox->displayname = "Sub";
$inbox->type = SYNC_FOLDER_TYPE_OTHER;
return $inbox;
} else {
return false;
}
}
/**
* 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) {
$folder = $this->GetFolder($id);
$stat = array();
$stat["id"] = $id;
$stat["parent"] = $folder->parentid;
$stat["mod"] = $folder->displayname;
return $stat;
}
/**
* Creates or modifies a folder
* not implemented
*
* @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
*
* @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) {
$this->moveNewToCur();
if($folderid != "root")
return false;
// return stats of all messages in a dir. We can do this faster than
// just calling statMessage() on each message; We still need fstat()
// information though, so listing 10000 messages is going to be
// rather slow (depending on filesystem, etc)
// we also have to filter by the specified cutoffdate so only the
// last X days are retrieved. Normally, this would mean that we'd
// have to open each message, get the Received: header, and check
// whether that is in the filter range. Because this is much too slow, we
// are depending on the creation date of the message instead, which should
// normally be just about the same, unless you just did some kind of import.
$messages = array();
$dirname = $this->getPath();
$dir = opendir($dirname);
if(!$dir)
return false;
while($entry = readdir($dir)) {
if($entry{0} == ".")
continue;
$message = array();
$stat = stat("$dirname/$entry");
if($stat["mtime"] < $cutoffdate) {
// message is out of range for curoffdate, ignore it
continue;
}
$message["mod"] = $stat["mtime"];
$matches = array();
// Flags according to http://cr.yp.to/proto/maildir.html (pretty authoritative - qmail author's website)
if(!preg_match("/([^:]+):2,([PRSTDF]*)/",$entry,$matches))
continue;
$message["id"] = $matches[1];
$message["flags"] = 0;
if(strpos($matches[2],"S") !== false) {
$message["flags"] |= 1; // 'seen' aka 'read' is the only flag we want to know about
}
array_push($messages, $message);
}
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, $truncsize, $mimesupport = 0) {
if($folderid != 'root')
return false;
$fn = $this->findMessage($id);
// Get flags, etc
$stat = $this->StatMessage($folderid, $id);
// Parse e-mail
$rfc822 = file_get_contents($this->getPath() . "/" . $fn);
$message = Mail_mimeDecode::decode(array('decode_headers' => true, 'decode_bodies' => true, 'include_bodies' => true, 'input' => $rfc822, 'crlf' => "\n", 'charset' => 'utf-8'));
$output = new SyncMail();
$output->body = str_replace("\n", "\r\n", $this->getBody($message));
$output->bodysize = strlen($output->body);
$output->bodytruncated = 0; // We don't implement truncation in this backend
$output->datereceived = $this->parseReceivedDate($message->headers["received"][0]);
$output->messageclass = "IPM.Note";
$output->subject = $message->headers["subject"];
$output->read = $stat["flags"];
$output->from = $message->headers["from"];
$Mail_RFC822 = new Mail_RFC822();
$toaddr = $ccaddr = $replytoaddr = array();
if(isset($message->headers["to"]))
$toaddr = $Mail_RFC822->parseAddressList($message->headers["to"]);
if(isset($message->headers["cc"]))
$ccaddr = $Mail_RFC822->parseAddressList($message->headers["cc"]);
if(isset($message->headers["reply_to"]))
$replytoaddr = $Mail_RFC822->parseAddressList($message->headers["reply_to"]);
$output->to = array();
$output->cc = array();
$output->reply_to = array();
foreach(array("to" => $toaddr, "cc" => $ccaddr, "reply_to" => $replytoaddr) as $type => $addrlist) {
foreach($addrlist as $addr) {
$address = $addr->mailbox . "@" . $addr->host;
$name = $addr->personal;
if (!isset($output->displayto) && $name != "")
$output->displayto = $name;
if($name == "" || $name == $address)
$fulladdr = w2u($address);
else {
if (substr($name, 0, 1) != '"' && substr($name, -1) != '"') {
$fulladdr = "\"" . w2u($name) ."\" <" . w2u($address) . ">";
}
else {
$fulladdr = w2u($name) ." <" . w2u($address) . ">";
}
}
array_push($output->$type, $fulladdr);
}
}
// convert mime-importance to AS-importance
if (isset($message->headers["x-priority"])) {
$mimeImportance = preg_replace("/\D+/", "", $message->headers["x-priority"]);
if ($mimeImportance > 3)
$output->importance = 0;
if ($mimeImportance == 3)
$output->importance = 1;
if ($mimeImportance < 3)
$output->importance = 2;
}
// Attachments are only searched in the top-level part
$n = 0;
if(isset($message->parts)) {
foreach($message->parts as $part) {
if($part->ctype_primary == "application") {
$attachment = new SyncAttachment();
$attachment->attsize = strlen($part->body);
if(isset($part->d_parameters['filename']))
$attname = $part->d_parameters['filename'];
else if(isset($part->ctype_parameters['name']))
$attname = $part->ctype_parameters['name'];
else if(isset($part->headers['content-description']))
$attname = $part->headers['content-description'];
else $attname = "unknown attachment";
$attachment->displayname = $attname;
$attachment->attname = $id . ":" . $n;
$attachment->attmethod = 1;
$attachment->attoid = isset($part->headers['content-id']) ? $part->headers['content-id'] : "";
array_push($output->attachments, $attachment);
}
$n++;
}
}
return $output;
}
/**
* 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) {
$dirname = $this->getPath();
$fn = $this->findMessage($id);
if(!$fn)
return false;
$stat = stat("$dirname/$fn");
$entry = array();
$entry["id"] = $id;
$entry["flags"] = 0;
if(strpos($fn,"S"))
$entry["flags"] |= 1;
$entry["mod"] = $stat["mtime"];
return $entry;
}
/**
* 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) {
// TODO SyncInterval check + ContentParameters
// see https://jira.zarafa.com/browse/ZP-258 for details
// before changing the message, it should be checked if the message is in the SyncInterval
// to determine the cutoffdate use Utils::GetCutOffDate($contentparameters->GetFilterType());
// if the message is not in the interval an StatusException with code SYNC_STATUS_SYNCCANNOTBECOMPLETED should be thrown
return false;
}
/**
* Changes the 'read' flag of a message on disk
*
* @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) {
if($folderid != 'root')
return false;
// TODO SyncInterval check + ContentParameters
// see https://jira.zarafa.com/browse/ZP-258 for details
// before setting the read flag, it should be checked if the message is in the SyncInterval
// to determine the cutoffdate use Utils::GetCutOffDate($contentparameters->GetFilterType());
// if the message is not in the interval an StatusException with code SYNC_STATUS_OBJECTNOTFOUND should be thrown
$fn = $this->findMessage($id);
if(!$fn)
return true; // message may have been deleted
if(!preg_match("/([^:]+):2,([PRSTDF]*)/",$fn,$matches))
return false;
// remove 'seen' (S) flag
if(!$flags) {
$newflags = str_replace("S","",$matches[2]);
} else {
// make sure we don't double add the 'S' flag
$newflags = str_replace("S","",$matches[2]) . "S";
}
$newfn = $matches[1] . ":2," . $newflags;
// rename if required
if($fn != $newfn)
rename($this->getPath() ."/$fn", $this->getPath() . "/$newfn");
return true;
}
/**
* 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) {
if($folderid != 'root')
return false;
// TODO SyncInterval check + ContentParameters
// see https://jira.zarafa.com/browse/ZP-258 for details
// before deleting the message, it should be checked if the message is in the SyncInterval
// to determine the cutoffdate use Utils::GetCutOffDate($contentparameters->GetFilterType());
// if the message is not in the interval an StatusException with code SYNC_STATUS_OBJECTNOTFOUND should be thrown
$fn = $this->findMessage($id);
if(!$fn)
return true; // success because message has been deleted already
if(!unlink($this->getPath() . "/$fn")) {
return true; // success - message may have been deleted in the mean time (since findMessage)
}
return true;
}
/**
* Called when the user moves an item on the PDA from one folder to another
* not implemented
*
* @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;
}
/**----------------------------------------------------------------------------------------------------------
* private maildir-specific internals
*/
/**
* Searches for the message
*
* @param string $id id of the message
*
* @access private
* @return string
*/
private function findMessage($id) {
// We could use 'this->folderid' for path info but we currently
// only support a single INBOX. We also have to use a glob '*'
// because we don't know the flags of the message we're looking for.
$dirname = $this->getPath();
$dir = opendir($dirname);
while($entry = readdir($dir)) {
if(strpos($entry,$id) === 0)
return $entry;
}
return false; // not found
}
/**
* Parses the message and return only the plaintext body
*
* @param string $message html message
*
* @access private
* @return string plaintext message
*/
private function getBody($message) {
$body = "";
$htmlbody = "";
$this->getBodyRecursive($message, "plain", $body);
if(!isset($body) || $body === "") {
$this->getBodyRecursive($message, "html", $body);
// remove css-style tags
$body = preg_replace("/<style.*?<\/style>/is", "", $body);
// remove all other html
$body = strip_tags($body);
}
return $body;
}
/**
* Get all parts in the message with specified type and concatenate them together, unless the
* Content-Disposition is 'attachment', in which case the text is apparently an attachment
*
* @param string $message mimedecode message(part)
* @param string $message message subtype
* @param string &$body body reference
*
* @access private
* @return
*/
private function getBodyRecursive($message, $subtype, &$body) {
if(!isset($message->ctype_primary)) return;
if(strcasecmp($message->ctype_primary,"text")==0 && strcasecmp($message->ctype_secondary,$subtype)==0 && isset($message->body))
$body .= $message->body;
if(strcasecmp($message->ctype_primary,"multipart")==0 && isset($message->parts) && is_array($message->parts)) {
foreach($message->parts as $part) {
if(!isset($part->disposition) || strcasecmp($part->disposition,"attachment")) {
$this->getBodyRecursive($part, $subtype, $body);
}
}
}
}
/**
* Parses the received date
*
* @param string $received received date string
*
* @access private
* @return long
*/
private function parseReceivedDate($received) {
$pos = strpos($received, ";");
if(!$pos)
return false;
$datestr = substr($received, $pos+1);
$datestr = ltrim($datestr);
return strtotime($datestr);
}
/**
* Moves everything in Maildir/new/* to Maildir/cur/
*
* @access private
* @return
*/
private function moveNewToCur() {
$newdirname = MAILDIR_BASE . "/" . $this->store . "/" . MAILDIR_SUBDIR . "/new";
$newdir = opendir($newdirname);
while($newentry = readdir($newdir)) {
if($newentry{0} == ".")
continue;
// link/unlink == move. This is the way to move the message according to cr.yp.to
link($newdirname . "/" . $newentry, $this->getPath() . "/" . $newentry . ":2,");
unlink($newdirname . "/" . $newentry);
}
}
/**
* The path we're working on
*
* @access private
* @return string
*/
private function getPath() {
return MAILDIR_BASE . "/" . $this->store . "/" . MAILDIR_SUBDIR . "/cur";
}
}

View file

@ -1,74 +0,0 @@
<?php
/***********************************************
* File : searchldap/config.php
* Project : Z-Push
* Descr : configuration file for the
* BackendSearchLDAP backend.
*
* Created : 03.08.2010
*
* 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 <http://www.gnu.org/licenses/>.
*
* Consult LICENSE file for details
************************************************/
// LDAP host and port
define("LDAP_HOST", "ldap://127.0.0.1/");
define("LDAP_PORT", "389");
// Set USER and PASSWORD if not using anonymous bind
define("ANONYMOUS_BIND", true);
define("LDAP_BIND_USER", "cn=searchuser,dc=test,dc=net");
define("LDAP_BIND_PASSWORD", "");
// Search base & filter
// the SEARCHVALUE string is substituded by the value inserted into the search field
define("LDAP_SEARCH_BASE", "ou=global,dc=test,dc=net");
define("LDAP_SEARCH_FILTER", "(|(cn=*SEARCHVALUE*)(mail=*SEARCHVALUE*))");
// LDAP field mapping.
// values correspond to an inetOrgPerson class
global $ldap_field_map;
$ldap_field_map = array(
SYNC_GAL_DISPLAYNAME => 'cn',
SYNC_GAL_PHONE => 'telephonenumber',
SYNC_GAL_OFFICE => '',
SYNC_GAL_TITLE => 'title',
SYNC_GAL_COMPANY => 'ou',
SYNC_GAL_ALIAS => 'uid',
SYNC_GAL_FIRSTNAME => 'givenname',
SYNC_GAL_LASTNAME => 'sn',
SYNC_GAL_HOMEPHONE => 'homephone',
SYNC_GAL_MOBILEPHONE => 'mobile',
SYNC_GAL_EMAILADDRESS => 'mail',
);

View file

@ -1,197 +0,0 @@
<?php
/***********************************************
* File : searchLDAP.php
* Project : Z-Push
* Descr : A ISearchProvider implementation to
* query a ldap server for GAL
* information.
*
* Created : 03.08.2010
*
* 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 <http://www.gnu.org/licenses/>.
*
* Consult LICENSE file for details
************************************************/
require_once("backend/searchldap/config.php");
class BackendSearchLDAP implements ISearchProvider {
private $connection;
/**
* Initializes the backend to perform the search
* Connects to the LDAP server using the values from the configuration
*
*
* @access public
* @return
* @throws StatusException
*/
public function BackendSearchLDAP() {
if (!function_exists("ldap_connect"))
throw new StatusException("BackendSearchLDAP(): php-ldap is not installed. Search aborted.", SYNC_SEARCHSTATUS_STORE_SERVERERROR, null, LOGLEVEL_FATAL);
// connect to LDAP
$this->connection = @ldap_connect(LDAP_HOST, LDAP_PORT);
@ldap_set_option($this->connection, LDAP_OPT_PROTOCOL_VERSION, 3);
// Authenticate
if (constant('ANONYMOUS_BIND') === true) {
if(! @ldap_bind($this->connection)) {
$this->connection = false;
throw new StatusException("BackendSearchLDAP(): Could not bind anonymously to server! Search aborted.", SYNC_SEARCHSTATUS_STORE_CONNECTIONFAILED, null, LOGLEVEL_ERROR);
}
}
else if (constant('LDAP_BIND_USER') != "") {
if(! @ldap_bind($this->connection, LDAP_BIND_USER, LDAP_BIND_PASSWORD)) {
$this->connection = false;
throw new StatusException(sprintf("BackendSearchLDAP(): Could not bind to server with user '%s' and specified password! Search aborted.", LDAP_BIND_USER), SYNC_SEARCHSTATUS_STORE_ACCESSDENIED, null, LOGLEVEL_ERROR);
}
}
else {
// it would be possible to use the users login and password to authenticate on the LDAP server
// the main $backend has to keep these values so they could be used here
$this->connection = false;
throw new StatusException("BackendSearchLDAP(): neither anonymous nor default bind enabled. Other options not implemented.", SYNC_SEARCHSTATUS_STORE_CONNECTIONFAILED, null, LOGLEVEL_ERROR);
}
}
/**
* 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) {
return ($searchtype == ISearchProvider::SEARCH_GAL);
}
/**
* 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) {
global $ldap_field_map;
if (isset($this->connection) && $this->connection !== false) {
$searchfilter = str_replace("SEARCHVALUE", $searchquery, LDAP_SEARCH_FILTER);
$result = @ldap_search($this->connection, LDAP_SEARCH_BASE, $searchfilter);
if (!$result) {
ZLog::Write(LOGLEVEL_ERROR, "BackendSearchLDAP: Error in search query. Search aborted");
return false;
}
// get entry data as array
$searchresult = ldap_get_entries($this->connection, $result);
// 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 = $searchresult['count'];
//do not return more results as requested in range
$querylimit = (($rangeend + 1) < $querycnt) ? ($rangeend + 1) : $querycnt;
$items['range'] = $rangestart.'-'.($querylimit-1);
$items['searchtotal'] = $querycnt;
$rc = 0;
for ($i = $rangestart; $i < $querylimit; $i++) {
foreach ($ldap_field_map as $key=>$value ) {
if (isset($searchresult[$i][$value])) {
if (is_array($searchresult[$i][$value]))
$items[$rc][$key] = $searchresult[$i][$value][0];
else
$items[$rc][$key] = $searchresult[$i][$value];
}
}
$rc++;
}
return $items;
}
else
return false;
}
/**
* Searches for the emails on the server
*
* @param ContentParameter $cpo
*
* @return array
*/
public function GetMailboxSearchResults($cpo) {
return array();
}
/**
* Terminates a search for a given PID
*
* @param int $pid
*
* @return boolean
*/
public function TerminateSearch($pid) {
return true;
}
/**
* Disconnects from LDAP
*
* @access public
* @return boolean
*/
public function Disconnect() {
if ($this->connection)
@ldap_close($this->connection);
return true;
}
}

View file

@ -1,48 +0,0 @@
<?php
/***********************************************
* File : config.php
* Project : Z-Push
* Descr : VCardDir backend configuration file
*
* Created : 27.11.2012
*
* 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 <http://www.gnu.org/licenses/>.
*
* Consult LICENSE file for details
************************************************/
// **********************
// BackendVCardDir settings
// **********************
define('VCARDDIR_DIR', '/home/%u/.kde/share/apps/kabc/stdvcf');

View file

@ -1,678 +0,0 @@
<?php
/***********************************************
* File : vcarddir.php
* Project : Z-Push
* Descr : This backend is for vcard directories.
*
* Created : 01.10.2007
*
* 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 <http://www.gnu.org/licenses/>.
*
* Consult LICENSE file for details
************************************************/
// config file
require_once("backend/vcarddir/config.php");
class BackendVCardDir extends BackendDiff {
/**----------------------------------------------------------------------------------------------------------
* default backend methods
*/
/**
* 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) {
return true;
}
/**
* Logs off
*
* @access public
* @return boolean
*/
public function Logoff() {
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
*
* @access public
* @return string
*/
public function GetWasteBasket() {
return false;
}
/**
* Returns the content of the named attachment as stream
* not implemented
*
* @param string $attname
*
* @access public
* @return SyncItemOperationsAttachment
* @throws StatusException
*/
public function GetAttachmentData($attname) {
return false;
}
/**----------------------------------------------------------------------------------------------------------
* 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, 'VCDir::GetFolderList()');
$contacts = array();
$folder = $this->StatFolder("root");
$contacts[] = $folder;
return $contacts;
}
/**
* 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, 'VCDir::GetFolder('.$id.')');
if($id == "root") {
$folder = new SyncFolder();
$folder->serverid = $id;
$folder->parentid = "0";
$folder->displayname = "Contacts";
$folder->type = SYNC_FOLDER_TYPE_CONTACT;
return $folder;
} else return false;
}
/**
* 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, 'VCDir::StatFolder('.$id.')');
$folder = $this->GetFolder($id);
$stat = array();
$stat["id"] = $id;
$stat["parent"] = $folder->parentid;
$stat["mod"] = $folder->displayname;
return $stat;
}
/**
* Creates or modifies a folder
* not implemented
*
* @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
*
* @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, 'VCDir::GetMessageList('.$folderid.')');
$messages = array();
$dir = opendir($this->getPath());
if(!$dir)
return false;
while($entry = readdir($dir)) {
if(is_dir($this->getPath() .'/'.$entry))
continue;
$message = array();
$message["id"] = $entry;
$stat = stat($this->getPath() .'/'.$entry);
$message["mod"] = $stat["mtime"];
$message["flags"] = 1; // always 'read'
$messages[] = $message;
}
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, 'VCDir::GetMessage('.$folderid.', '.$id.', ..)');
if($folderid != "root")
return;
$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 = file_get_contents($this->getPath() . "/" . $id);
$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('/(?<!\\\\)(\;)/i', $field, -1, PREG_SPLIT_NO_EMPTY);
$type = strtolower(array_shift($fieldparts));
$fieldvalue = array();
foreach ($fieldparts as $fieldpart) {
if(preg_match('/([^=]+)=(.+)/', $fieldpart, $matches)){
if(!in_array(strtolower($matches[1]),array('value','type','encoding','language')))
continue;
if(isset($fieldvalue[strtolower($matches[1])]) && is_array($fieldvalue[strtolower($matches[1])])){
$fieldvalue[strtolower($matches[1])] = array_merge($fieldvalue[strtolower($matches[1])], preg_split('/(?<!\\\\)(\,)/i', $matches[2], -1, PREG_SPLIT_NO_EMPTY));
}else{
$fieldvalue[strtolower($matches[1])] = preg_split('/(?<!\\\\)(\,)/i', $matches[2], -1, PREG_SPLIT_NO_EMPTY);
}
}else{
if(!isset($types[strtolower($fieldpart)]))
continue;
$fieldvalue[$types[strtolower($fieldpart)]][] = $fieldpart;
}
}
//
switch ($type) {
case 'categories':
//case 'nickname':
$val = preg_split('/(?<!\\\\)(\,)/i', $value);
$val = array_map("w2ui", $val);
break;
default:
$val = preg_split('/(?<!\\\\)(\;)/i', $value);
break;
}
if(isset($fieldvalue['encoding'][0])){
switch(strtolower($fieldvalue['encoding'][0])){
case 'q':
case 'quoted-printable':
foreach($val as $i => $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 = w2ui($adr['val'][2]);
}
if(!empty($adr['val'][3])){
$b=$a.'city';
$message->$b = w2ui($adr['val'][3]);
}
if(!empty($adr['val'][4])){
$b=$a.'state';
$message->$b = w2ui($adr['val'][4]);
}
if(!empty($adr['val'][5])){
$b=$a.'postalcode';
$message->$b = w2ui($adr['val'][5]);
}
if(!empty($adr['val'][6])){
$b=$a.'country';
$message->$b = w2ui($adr['val'][6]);
}
}
}
if(!empty($vcard['fn'][0]['val'][0]))
$message->fileas = w2ui($vcard['fn'][0]['val'][0]);
if(!empty($vcard['n'][0]['val'][0]))
$message->lastname = w2ui($vcard['n'][0]['val'][0]);
if(!empty($vcard['n'][0]['val'][1]))
$message->firstname = w2ui($vcard['n'][0]['val'][1]);
if(!empty($vcard['n'][0]['val'][2]))
$message->middlename = w2ui($vcard['n'][0]['val'][2]);
if(!empty($vcard['n'][0]['val'][3]))
$message->title = w2ui($vcard['n'][0]['val'][3]);
if(!empty($vcard['n'][0]['val'][4]))
$message->suffix = w2ui($vcard['n'][0]['val'][4]);
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 = w2ui($vcard['org'][0]['val'][0]);
if(!empty($vcard['note'][0]['val'][0])){
$message->body = w2ui($vcard['note'][0]['val'][0]);
$message->bodysize = strlen($vcard['note'][0]['val'][0]);
$message->bodytruncated = 0;
}
if(!empty($vcard['role'][0]['val'][0]))
$message->jobtitle = w2ui($vcard['role'][0]['val'][0]);//$vcard['title'][0]['val'][0]
if(!empty($vcard['url'][0]['val'][0]))
$message->webpage = w2ui($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;
}
/**
* 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, 'VCDir::StatMessage('.$folderid.', '.$id.')');
if($folderid != "root")
return false;
$stat = stat($this->getPath() . "/" . $id);
$message = array();
$message["mod"] = $stat["mtime"];
$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, 'VCDir::ChangeMessage('.$folderid.', '.$id.', ..)');
$mapping = array(
'fileas' => 'FN',
'lastname;firstname;middlename;title;suffix' => 'N',
'email1address' => 'EMAIL;INTERNET',
'email2address' => 'EMAIL;INTERNET',
'email3address' => 'EMAIL;INTERNET',
'businessphonenumber' => 'TEL;WORK',
'business2phonenumber' => 'TEL;WORK',
'businessfaxnumber' => 'TEL;WORK;FAX',
'homephonenumber' => 'TEL;HOME',
'home2phonenumber' => 'TEL;HOME',
'homefaxnumber' => 'TEL;HOME;FAX',
'mobilephonenumber' => 'TEL;CELL',
'carphonenumber' => 'TEL;CAR',
'pagernumber' => 'TEL;PAGER',
';;businessstreet;businesscity;businessstate;businesspostalcode;businesscountry' => 'ADR;WORK',
';;homestreet;homecity;homestate;homepostalcode;homecountry' => 'ADR;HOME',
';;otherstreet;othercity;otherstate;otherpostalcode;othercountry' => 'ADR',
'companyname' => 'ORG',
'body' => 'NOTE',
'jobtitle' => 'ROLE',
'webpage' => 'URL',
);
$data = "BEGIN:VCARD\nVERSION:2.1\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(empty($val))
continue;
$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(',', $this->escape($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";
// not supported: anniversary, assistantname, assistnamephonenumber, children, department, officelocation, radiophonenumber, spouse, rtf
if(!$id){
if(!empty($message->fileas)){
$name = u2wi($message->fileas);
}elseif(!empty($message->lastname)){
$name = $name = u2wi($message->lastname);
}elseif(!empty($message->firstname)){
$name = $name = u2wi($message->firstname);
}elseif(!empty($message->companyname)){
$name = $name = u2wi($message->companyname);
}else{
$name = 'unknown';
}
$name = preg_replace('/[^a-z0-9 _-]/i', '', $name);
$id = $name.'.vcf';
$i = 0;
while(file_exists($this->getPath().'/'.$id)){
$i++;
$id = $name.$i.'.vcf';
}
}
file_put_contents($this->getPath().'/'.$id, $data);
return $this->StatMessage($folderid, $id);
}
/**
* Changes the 'read' flag of a message on disk
*
* @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) {
return unlink($this->getPath() . '/' . $id);
}
/**
* Called when the user moves an item on the PDA from one folder to another
* not implemented
*
* @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;
}
/**----------------------------------------------------------------------------------------------------------
* private vcard-specific internals
*/
/**
* The path we're working on
*
* @access private
* @return string
*/
private function getPath() {
return str_replace('%u', $this->store, VCARDDIR_DIR);
}
/**
* Escapes a string
*
* @param string $data string to be escaped
*
* @access private
* @return string
*/
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 u2wi($data);
}
/**
* Un-escapes a string
*
* @param string $data string to be un-escaped
*
* @access private
* @return string
*/
function unescape($data){
$data = str_replace(array('\\\\', '\\;', '\\,', '\\n','\\N'),array('\\', ';', ',', "\n", "\n"),$data);
return $data;
}
}

View file

@ -1,49 +0,0 @@
<?php
/***********************************************
* File : config.php
* Project : Z-Push
* Descr : VCardDir backend configuration file
*
* Created : 27.11.2012
*
* 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 <http://www.gnu.org/licenses/>.
*
* Consult LICENSE file for details
************************************************/
// ************************
// BackendZarafa settings
// ************************
// Defines the server to which we want to connect
define('MAPI_SERVER', 'file:///var/run/zarafa');

View file

@ -1,297 +0,0 @@
<?php
/***********************************************
* File : exporter.php
* Project : Z-Push
* Descr : This is a generic class that is
* used by both the proxy importer
* (for outgoing messages) and our
* local importer (for incoming
* messages). Basically all shared
* conversion data for converting
* to and from MAPI objects is in here.
*
* Created : 14.02.2011
*
* Copyright 2007 - 2013, 2015 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 <http://www.gnu.org/licenses/>.
*
* Consult LICENSE file for details
************************************************/
/**
* This is our ICS exporter which requests the actual exporter from ICS and makes sure
* that the ImportProxies are used.
*/
class ExportChangesICS implements IExportChanges{
private $folderid;
private $store;
private $session;
private $restriction;
private $contentparameters;
private $flags;
private $exporterflags;
private $exporter;
/**
* Constructor
*
* @param mapisession $session
* @param mapistore $store
* @param string (opt)
*
* @access public
* @throws StatusException
*/
public function ExportChangesICS($session, $store, $folderid = false) {
// Open a hierarchy or a contents exporter depending on whether a folderid was specified
$this->session = $session;
$this->folderid = $folderid;
$this->store = $store;
$this->restriction = false;
try {
if($folderid) {
$entryid = mapi_msgstore_entryidfromsourcekey($store, $folderid);
}
else {
$storeprops = mapi_getprops($this->store, array(PR_IPM_SUBTREE_ENTRYID));
$entryid = $storeprops[PR_IPM_SUBTREE_ENTRYID];
}
$folder = false;
if ($entryid)
$folder = mapi_msgstore_openentry($this->store, $entryid);
// Get the actual ICS exporter
if($folderid) {
if ($folder)
$this->exporter = mapi_openproperty($folder, PR_CONTENTS_SYNCHRONIZER, IID_IExchangeExportChanges, 0 , 0);
else
$this->exporter = false;
}
else {
$this->exporter = mapi_openproperty($folder, PR_HIERARCHY_SYNCHRONIZER, IID_IExchangeExportChanges, 0 , 0);
}
}
catch (MAPIException $me) {
$this->exporter = false;
// We return the general error SYNC_FSSTATUS_CODEUNKNOWN (12) which is also SYNC_STATUS_FOLDERHIERARCHYCHANGED (12)
// if this happened while doing content sync, the mobile will try to resync the folderhierarchy
throw new StatusException(sprintf("ExportChangesICS('%s','%s','%s'): Error, unable to open folder: 0x%X", $session, $store, Utils::PrintAsString($folderid), mapi_last_hresult()), SYNC_FSSTATUS_CODEUNKNOWN);
}
}
/**
* Configures the exporter
*
* @param string $state
* @param int $flags
*
* @access public
* @return boolean
* @throws StatusException
*/
public function Config($state, $flags = 0) {
$this->exporterflags = 0;
$this->flags = $flags;
// this should never happen
if ($this->exporter === false || is_array($state))
throw new StatusException("ExportChangesICS->Config(): Error, exporter not available", SYNC_FSSTATUS_CODEUNKNOWN, null, LOGLEVEL_ERROR);
// change exporterflags if we are doing a ContentExport
if($this->folderid) {
$this->exporterflags |= SYNC_NORMAL | SYNC_READ_STATE;
// Initial sync, we don't want deleted items. If the initial sync is chunked
// we check the change ID of the syncstate (0 at initial sync)
// On subsequent syncs, we do want to receive delete events.
if(strlen($state) == 0 || bin2hex(substr($state,4,4)) == "00000000") {
if (!($this->flags & BACKEND_DISCARD_DATA))
ZLog::Write(LOGLEVEL_DEBUG, "ExportChangesICS->Config(): synching inital data");
$this->exporterflags |= SYNC_NO_SOFT_DELETIONS | SYNC_NO_DELETIONS;
}
}
if($this->flags & BACKEND_DISCARD_DATA)
$this->exporterflags |= SYNC_CATCHUP;
// Put the state information in a stream that can be used by ICS
$stream = mapi_stream_create();
if(strlen($state) == 0)
$state = hex2bin("0000000000000000");
if (!($this->flags & BACKEND_DISCARD_DATA))
ZLog::Write(LOGLEVEL_DEBUG, sprintf("ExportChangesICS->Config() initialized with state: 0x%s", bin2hex($state)));
mapi_stream_write($stream, $state);
$this->statestream = $stream;
}
/**
* Configures additional parameters used for content synchronization
*
* @param ContentParameters $contentparameters
*
* @access public
* @return boolean
* @throws StatusException
*/
public function ConfigContentParameters($contentparameters){
$filtertype = $contentparameters->GetFilterType();
switch($contentparameters->GetContentClass()) {
case "Email":
$this->restriction = ($filtertype || !Utils::CheckMapiExtVersion('7')) ? MAPIUtils::GetEmailRestriction(Utils::GetCutOffDate($filtertype)) : false;
break;
case "Calendar":
$this->restriction = ($filtertype || !Utils::CheckMapiExtVersion('7')) ? MAPIUtils::GetCalendarRestriction($this->store, Utils::GetCutOffDate($filtertype)) : false;
break;
default:
case "Contacts":
case "Tasks":
$this->restriction = false;
break;
}
$this->contentParameters = $contentparameters;
}
/**
* Sets the importer the exporter will sent it's changes to
* and initializes the Exporter
*
* @param object &$importer Implementation of IImportChanges
*
* @access public
* @return boolean
* @throws StatusException
*/
public function InitializeExporter(&$importer) {
// Because we're using ICS, we need to wrap the given importer to make it suitable to pass
// to ICS. We do this in two steps: first, wrap the importer with our own PHP importer class
// which removes all MAPI dependency, and then wrap that class with a C++ wrapper so we can
// pass it to ICS
// this should never happen!
if($this->exporter === false || !isset($this->statestream) || !isset($this->flags) || !isset($this->exporterflags) ||
($this->folderid && !isset($this->contentParameters)) )
throw new StatusException("ExportChangesICS->InitializeExporter(): Error, exporter or essential data not available", SYNC_FSSTATUS_CODEUNKNOWN, null, LOGLEVEL_ERROR);
// PHP wrapper
$phpwrapper = new PHPWrapper($this->session, $this->store, $importer, $this->folderid);
// with a folderid we are going to get content
if($this->folderid) {
$phpwrapper->ConfigContentParameters($this->contentParameters);
// ICS c++ wrapper
$mapiimporter = mapi_wrap_importcontentschanges($phpwrapper);
$includeprops = false;
}
else {
$mapiimporter = mapi_wrap_importhierarchychanges($phpwrapper);
$includeprops = array(PR_SOURCE_KEY, PR_DISPLAY_NAME);
}
if (!$mapiimporter)
throw new StatusException(sprintf("ExportChangesICS->InitializeExporter(): Error, mapi_wrap_import_*_changes() failed: 0x%X", mapi_last_hresult()), SYNC_FSSTATUS_CODEUNKNOWN, null, LOGLEVEL_WARN);
$ret = mapi_exportchanges_config($this->exporter, $this->statestream, $this->exporterflags, $mapiimporter, $this->restriction, $includeprops, false, 1);
if(!$ret)
throw new StatusException(sprintf("ExportChangesICS->InitializeExporter(): Error, mapi_exportchanges_config() failed: 0x%X", mapi_last_hresult()), SYNC_FSSTATUS_CODEUNKNOWN, null, LOGLEVEL_WARN);
$changes = mapi_exportchanges_getchangecount($this->exporter);
if($changes || !($this->flags & BACKEND_DISCARD_DATA))
ZLog::Write(LOGLEVEL_DEBUG, sprintf("ExportChangesICS->InitializeExporter() successfully. %d changes ready to sync for '%s'.", $changes, ($this->folderid)?bin2hex($this->folderid) : 'hierarchy'));
return $ret;
}
/**
* Reads the current state from the Exporter
*
* @access public
* @return string
* @throws StatusException
*/
public function GetState() {
$error = false;
if(!isset($this->statestream) || $this->exporter === false)
$error = true;
if($error === true || mapi_exportchanges_updatestate($this->exporter, $this->statestream) != true )
throw new StatusException(sprintf("ExportChangesICS->GetState(): Error, state not available or unable to update: 0x%X", mapi_last_hresult()), (($this->folderid)?SYNC_STATUS_FOLDERHIERARCHYCHANGED:SYNC_FSSTATUS_CODEUNKNOWN), null, LOGLEVEL_WARN);
mapi_stream_seek($this->statestream, 0, STREAM_SEEK_SET);
$state = "";
while(true) {
$data = mapi_stream_read($this->statestream, 4096);
if(strlen($data))
$state .= $data;
else
break;
}
return $state;
}
/**
* Returns the amount of changes to be exported
*
* @access public
* @return int
*/
public function GetChangeCount() {
if ($this->exporter)
return mapi_exportchanges_getchangecount($this->exporter);
else
return 0;
}
/**
* Synchronizes a change
*
* @access public
* @return array
*/
public function Synchronize() {
if ($this->exporter) {
return mapi_exportchanges_synchronize($this->exporter);
}
return false;
}
}

View file

@ -1,199 +0,0 @@
<?php
/***********************************************
* File : z_ical.php
* Project : Z-Push
* Descr : This is a very basic iCalendar parser
* used to process incoming meeting requests
* and responses.
*
* Created : 01.12.2008
*
* 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 <http://www.gnu.org/licenses/>.
*
* Consult LICENSE file for details
************************************************/
class ICalParser{
private $props;
/**
* Constructor
*
* @param mapistore $store
* @param array &$props properties to be set
*
* @access public
*/
public function ICalParser(&$store, &$props){
$this->props = $props;
}
/**
* Function reads calendar part and puts mapi properties into an array.
*
* @param string $ical the ical data
* @param array &$mapiprops mapi properties
*
* @access public
*/
public function ExtractProps($ical, &$mapiprops) {
//mapping between partstat in ical and MAPI Meeting Response classes as well as icons
$aClassMap = array(
"ACCEPTED" => array("class" => "IPM.Schedule.Meeting.Resp.Pos", "icon" => 0x405),
"DECLINED" => array("class" => "IPM.Schedule.Meeting.Resp.Neg", "icon" => 0x406),
"TENTATIVE" => array("class" => "IPM.Schedule.Meeting.Resp.Tent", "icon" => 0x407),
"NEEDS-ACTION" => array("class" => "IPM.Schedule.Meeting.Request", "icon" => 0x404), //iphone
"REQ-PARTICIPANT" => array("class" => "IPM.Schedule.Meeting.Request", "icon" => 0x404), //nokia
);
$aical = preg_split("/[\n]/", $ical);
$elemcount = count($aical);
$i=0;
$nextline = $aical[0];
//last element is empty
while ($i < $elemcount - 1) {
$line = $nextline;
$nextline = $aical[$i+1];
//if a line starts with a space or a tab it belongs to the previous line
while (strlen($nextline) > 0 && ($nextline{0} == " " || $nextline{0} == "\t")) {
$line = rtrim($line) . substr($nextline, 1);
$nextline = $aical[++$i + 1];
}
$line = rtrim($line);
switch (strtoupper($line)) {
case "BEGIN:VCALENDAR":
case "BEGIN:VEVENT":
case "END:VEVENT":
case "END:VCALENDAR":
break;
default:
unset ($field, $data, $prop_pos, $property);
if (preg_match ("/([^:]+):(.*)/", $line, $line)){
$field = $line[1];
$data = $line[2];
$property = $field;
$prop_pos = strpos($property,';');
if ($prop_pos !== false) $property = substr($property, 0, $prop_pos);
$property = strtoupper($property);
switch ($property) {
case 'DTSTART':
$data = $this->getTimestampFromStreamerDate($data);
$mapiprops[$this->props["starttime"]] = $mapiprops[$this->props["commonstart"]] = $mapiprops[$this->props["clipstart"]] = $mapiprops[PR_START_DATE] = $data;
break;
case 'DTEND':
$data = $this->getTimestampFromStreamerDate($data);
$mapiprops[$this->props["endtime"]] = $mapiprops[$this->props["commonend"]] = $mapiprops[$this->props["recurrenceend"]] = $mapiprops[PR_END_DATE] = $data;
break;
case 'UID':
$mapiprops[$this->props["goidtag"]] = $mapiprops[$this->props["goid2tag"]] = Utils::GetOLUidFromICalUid($data);
break;
case 'ATTENDEE':
$fields = explode(";", $field);
foreach ($fields as $field) {
$prop_pos = strpos($field, '=');
if ($prop_pos !== false) {
switch (substr($field, 0, $prop_pos)) {
case 'PARTSTAT' : $partstat = substr($field, $prop_pos+1); break;
case 'CN' : $cn = substr($field, $prop_pos+1); break;
case 'ROLE' : $role = substr($field, $prop_pos+1); break;
case 'RSVP' : $rsvp = substr($field, $prop_pos+1); break;
}
}
}
if (isset($partstat) && isset($aClassMap[$partstat]) &&
(!isset($mapiprops[PR_MESSAGE_CLASS]) || $mapiprops[PR_MESSAGE_CLASS] == "IPM.Schedule.Meeting.Request")) {
$mapiprops[PR_MESSAGE_CLASS] = $aClassMap[$partstat]['class'];
$mapiprops[PR_ICON_INDEX] = $aClassMap[$partstat]['icon'];
}
// START ADDED dw2412 to support meeting requests on HTC Android Mail App
elseif (isset($role) && isset($aClassMap[$role]) &&
(!isset($mapiprops[PR_MESSAGE_CLASS]) || $mapiprops[PR_MESSAGE_CLASS] == "IPM.Schedule.Meeting.Request")) {
$mapiprops[PR_MESSAGE_CLASS] = $aClassMap[$role]['class'];
$mapiprops[PR_ICON_INDEX] = $aClassMap[$role]['icon'];
}
// END ADDED dw2412 to support meeting requests on HTC Android Mail App
if (!isset($cn)) $cn = "";
$data = str_replace ("MAILTO:", "", $data);
$attendee[] = array ('name' => stripslashes($cn), 'email' => stripslashes($data));
break;
case 'ORGANIZER':
$field = str_replace("ORGANIZER;CN=", "", $field);
$data = str_replace ("MAILTO:", "", $data);
$organizer[] = array ('name' => stripslashes($field), 'email' => stripslashes($data));
break;
case 'LOCATION':
$data = str_replace("\\n", "<br />", $data);
$data = str_replace("\\t", "&nbsp;", $data);
$data = str_replace("\\r", "<br />", $data);
$data = stripslashes($data);
$mapiprops[$this->props["tneflocation"]] = $mapiprops[$this->props["location"]] = $data;
break;
}
}
break;
}
$i++;
}
$mapiprops[$this->props["usetnef"]] = true;
}
/**
* Converts an YYYYMMDDTHHMMSSZ kind of string into an unixtimestamp
*
* @param string $data
*
* @access private
* @return long
*/
private function getTimestampFromStreamerDate ($data) {
$data = str_replace('Z', '', $data);
$data = str_replace('T', '', $data);
preg_match ('/([0-9]{4})([0-9]{2})([0-9]{2})([0-9]{0,2})([0-9]{0,2})([0-9]{0,2})/', $data, $regs);
if ($regs[1] < 1970) {
$regs[1] = '1971';
}
return gmmktime($regs[4], $regs[5], $regs[6], $regs[2], $regs[3], $regs[1]);
}
}

View file

@ -1,696 +0,0 @@
<?php
/***********************************************
* File : importer.php
* Project : Z-Push
* Descr : This is a generic class that is
* used by both the proxy importer
* (for outgoing messages) and our
* local importer (for incoming
* messages). Basically all shared
* conversion data for converting
* to and from MAPI objects is in here.
*
* Created : 14.02.2011
*
* 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 <http://www.gnu.org/licenses/>.
*
* Consult LICENSE file for details
************************************************/
/**
* This is our local importer. Tt receives data from the PDA, for contents and hierarchy changes.
* It must therefore receive the incoming data and convert it into MAPI objects, and then send
* them to the ICS importer to do the actual writing of the object.
* The creation of folders is fairly trivial, because folders that are created on
* the PDA are always e-mail folders.
*/
class ImportChangesICS implements IImportChanges {
private $folderid;
private $store;
private $session;
private $flags;
private $statestream;
private $importer;
private $memChanges;
private $mapiprovider;
private $conflictsLoaded;
private $conflictsContentParameters;
private $conflictsState;
private $cutoffdate;
private $contentClass;
/**
* Constructor
*
* @param mapisession $session
* @param mapistore $store
* @param string $folderid (opt)
*
* @access public
* @throws StatusException
*/
public function ImportChangesICS($session, $store, $folderid = false) {
$this->session = $session;
$this->store = $store;
$this->folderid = $folderid;
$this->conflictsLoaded = false;
$this->cutoffdate = false;
$this->contentClass = false;
if ($folderid) {
$entryid = mapi_msgstore_entryidfromsourcekey($store, $folderid);
}
else {
$storeprops = mapi_getprops($store, array(PR_IPM_SUBTREE_ENTRYID));
$entryid = $storeprops[PR_IPM_SUBTREE_ENTRYID];
}
$folder = false;
if ($entryid)
$folder = mapi_msgstore_openentry($store, $entryid);
if(!$folder) {
$this->importer = false;
// We throw an general error SYNC_FSSTATUS_CODEUNKNOWN (12) which is also SYNC_STATUS_FOLDERHIERARCHYCHANGED (12)
// if this happened while doing content sync, the mobile will try to resync the folderhierarchy
throw new StatusException(sprintf("ImportChangesICS('%s','%s','%s'): Error, unable to open folder: 0x%X", $session, $store, Utils::PrintAsString($folderid), mapi_last_hresult()), SYNC_FSSTATUS_CODEUNKNOWN);
}
$this->mapiprovider = new MAPIProvider($this->session, $this->store);
if ($folderid)
$this->importer = mapi_openproperty($folder, PR_COLLECTOR, IID_IExchangeImportContentsChanges, 0 , 0);
else
$this->importer = mapi_openproperty($folder, PR_COLLECTOR, IID_IExchangeImportHierarchyChanges, 0 , 0);
}
/**
* Initializes the importer
*
* @param string $state
* @param int $flags
*
* @access public
* @return boolean
* @throws StatusException
*/
public function Config($state, $flags = 0) {
$this->flags = $flags;
// this should never happen
if ($this->importer === false)
throw new StatusException("ImportChangesICS->Config(): Error, importer not available", SYNC_FSSTATUS_CODEUNKNOWN, null, LOGLEVEL_ERROR);
// Put the state information in a stream that can be used by ICS
$stream = mapi_stream_create();
if(strlen($state) == 0)
$state = hex2bin("0000000000000000");
ZLog::Write(LOGLEVEL_DEBUG, sprintf("ImportChangesICS->Config(): initializing importer with state: 0x%s", bin2hex($state)));
mapi_stream_write($stream, $state);
$this->statestream = $stream;
if ($this->folderid !== false) {
// possible conflicting messages will be cached here
$this->memChanges = new ChangesMemoryWrapper();
$stat = mapi_importcontentschanges_config($this->importer, $stream, $flags);
}
else
$stat = mapi_importhierarchychanges_config($this->importer, $stream, $flags);
if (!$stat)
throw new StatusException(sprintf("ImportChangesICS->Config(): Error, mapi_import_*_changes_config() failed: 0x%X", mapi_last_hresult()), SYNC_FSSTATUS_CODEUNKNOWN, null, LOGLEVEL_WARN);
return $stat;
}
/**
* Configures additional parameters for content selection
*
* @param ContentParameters $contentparameters
*
* @access public
* @return boolean
* @throws StatusException
*/
public function ConfigContentParameters($contentparameters) {
$filtertype = $contentparameters->GetFilterType();
switch($contentparameters->GetContentClass()) {
case "Email":
$this->cutoffdate = ($filtertype) ? Utils::GetCutOffDate($filtertype) : false;
break;
case "Calendar":
$this->cutoffdate = ($filtertype) ? Utils::GetCutOffDate($filtertype) : false;
break;
default:
case "Contacts":
case "Tasks":
$this->cutoffdate = false;
break;
}
$this->contentClass = $contentparameters->GetContentClass();
}
/**
* Reads state from the Importer
*
* @access public
* @return string
* @throws StatusException
*/
public function GetState() {
$error = false;
if(!isset($this->statestream) || $this->importer === false)
$error = true;
if ($error === false && $this->folderid !== false && function_exists("mapi_importcontentschanges_updatestate"))
if(mapi_importcontentschanges_updatestate($this->importer, $this->statestream) != true)
$error = true;
if ($error == true)
throw new StatusException(sprintf("ImportChangesICS->GetState(): Error, state not available or unable to update: 0x%X", mapi_last_hresult()), (($this->folderid)?SYNC_STATUS_FOLDERHIERARCHYCHANGED:SYNC_FSSTATUS_CODEUNKNOWN), null, LOGLEVEL_WARN);
mapi_stream_seek($this->statestream, 0, STREAM_SEEK_SET);
$state = "";
while(true) {
$data = mapi_stream_read($this->statestream, 4096);
if(strlen($data))
$state .= $data;
else
break;
}
return $state;
}
/**
* Checks if a message is in the synchronization interval (window)
* if a filter (e.g. Sync items two weeks back) or limits this synchronization.
* These checks only apply to Emails and Appointments only, Contacts, Tasks and Notes do not have time restrictions.
*
* @param string $messageid the message id to be checked
*
* @access private
* @return boolean
*/
private function isMessageInSyncInterval($messageid) {
// if there is no restriciton we do not need to check
if ($this->cutoffdate === false)
return true;
ZLog::Write(LOGLEVEL_DEBUG, sprintf("ImportChangesICS->isMessageInSyncInterval('%s'): cut off date is: %s", $messageid, $this->cutoffdate));
$entryid = mapi_msgstore_entryidfromsourcekey($this->store, $this->folderid, hex2bin($messageid));
if(!$entryid) {
ZLog::Write(LOGLEVEL_WARN, sprintf("ImportChangesICS->isMessageInSyncInterval('%s'): Error, unable to resolve message id: 0x%X", $messageid, mapi_last_hresult()));
return false;
}
$mapimessage = mapi_msgstore_openentry($this->store, $entryid);
if(!$mapimessage) {
ZLog::Write(LOGLEVEL_WARN, sprintf("ImportChangesICS->isMessageInSyncInterval('%s'): Error, unable to open entry id: 0x%X", $messageid, mapi_last_hresult()));
return false;
}
if ($this->contentClass == "Email")
return MAPIUtils::IsInEmailSyncInterval($this->store, $mapimessage, $this->cutoffdate);
elseif ($this->contentClass == "Calendar")
return MAPIUtils::IsInCalendarSyncInterval($this->store, $mapimessage, $this->cutoffdate);
return true;
}
/**----------------------------------------------------------------------------------------------------------
* Methods for ContentsExporter
*/
/**
* 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 (!isset($this->session) || !isset($this->store) || !isset($this->folderid))
throw new StatusException("ImportChangesICS->LoadConflicts(): Error, can not load changes for conflict detection. Session, store or folder information not available", SYNC_STATUS_SERVERERROR);
// save data to load changes later if necessary
$this->conflictsLoaded = false;
$this->conflictsContentParameters = $contentparameters;
$this->conflictsState = $state;
ZLog::Write(LOGLEVEL_DEBUG, "ImportChangesICS->LoadConflicts(): will be loaded later if necessary");
return true;
}
/**
* Potential conflicts are only loaded when really necessary,
* e.g. on ADD or MODIFY
*
* @access private
* @return
*/
private function lazyLoadConflicts() {
if (!isset($this->session) || !isset($this->store) || !isset($this->folderid) ||
!isset($this->conflictsContentParameters) || $this->conflictsState === false) {
ZLog::Write(LOGLEVEL_WARN, "ImportChangesICS->lazyLoadConflicts(): can not load potential conflicting changes in lazymode for conflict detection. Missing information");
return false;
}
if (!$this->conflictsLoaded) {
ZLog::Write(LOGLEVEL_DEBUG, "ImportChangesICS->lazyLoadConflicts(): loading..");
// configure an exporter so we can detect conflicts
$exporter = new ExportChangesICS($this->session, $this->store, $this->folderid);
$exporter->Config($this->conflictsState);
$exporter->ConfigContentParameters($this->conflictsContentParameters);
$exporter->InitializeExporter($this->memChanges);
// monitor how long it takes to export potential conflicts
// if this takes "too long" we cancel this operation!
$potConflicts = $exporter->GetChangeCount();
$started = time();
$exported = 0;
while(is_array($exporter->Synchronize())) {
$exported++;
// stop if this takes more than 15 seconds and there are more than 5 changes still to be exported
// within 20 seconds this should be finished or it will not be performed
if ((time() - $started) > 15 && ($potConflicts - $exported) > 5 ) {
ZLog::Write(LOGLEVEL_WARN, sprintf("ImportChangesICS->lazyLoadConflicts(): conflict detection cancelled as operation is too slow. In %d seconds only %d from %d changes were processed.",(time() - $started), $exported, $potConflicts));
$this->conflictsLoaded = true;
return;
}
}
$this->conflictsLoaded = true;
}
}
/**
* Imports a single message
*
* @param string $id
* @param SyncObject $message
*
* @access public
* @return boolean/string - failure / id of message
* @throws StatusException
*/
public function ImportMessageChange($id, $message) {
$parentsourcekey = $this->folderid;
if($id)
$sourcekey = hex2bin($id);
$flags = 0;
$props = array();
$props[PR_PARENT_SOURCE_KEY] = $parentsourcekey;
// set the PR_SOURCE_KEY if available or mark it as new message
if($id) {
$props[PR_SOURCE_KEY] = $sourcekey;
// on editing an existing message, check if it is in the synchronization interval
if (!$this->isMessageInSyncInterval($id))
throw new StatusException(sprintf("ImportChangesICS->ImportMessageChange('%s','%s'): Message is outside the sync interval. Data not saved.", $id, get_class($message)), SYNC_STATUS_SYNCCANNOTBECOMPLETED);
// check for conflicts
$this->lazyLoadConflicts();
if($this->memChanges->IsChanged($id)) {
if ($this->flags & SYNC_CONFLICT_OVERWRITE_PIM) {
// in these cases the status SYNC_STATUS_CONFLICTCLIENTSERVEROBJECT should be returned, so the mobile client can inform the end user
throw new StatusException(sprintf("ImportChangesICS->ImportMessageChange('%s','%s'): Conflict detected. Data from PIM will be dropped! Server overwrites PIM. User is informed.", $id, get_class($message)), SYNC_STATUS_CONFLICTCLIENTSERVEROBJECT, null, LOGLEVEL_INFO);
return false;
}
else
ZLog::Write(LOGLEVEL_INFO, sprintf("ImportChangesICS->ImportMessageChange('%s','%s'): Conflict detected. Data from Server will be dropped! PIM overwrites server.", $id, get_class($message)));
}
if($this->memChanges->IsDeleted($id)) {
ZLog::Write(LOGLEVEL_INFO, sprintf("ImportChangesICS->ImportMessageChange('%s','%s'): Conflict detected. Data from PIM will be dropped! Object was deleted on server.", $id, get_class($message)));
return false;
}
}
else
$flags = SYNC_NEW_MESSAGE;
if(mapi_importcontentschanges_importmessagechange($this->importer, $props, $flags, $mapimessage)) {
$this->mapiprovider->SetMessage($mapimessage, $message);
mapi_message_savechanges($mapimessage);
if (mapi_last_hresult())
throw new StatusException(sprintf("ImportChangesICS->ImportMessageChange('%s','%s'): Error, mapi_message_savechanges() failed: 0x%X", $id, get_class($message), mapi_last_hresult()), SYNC_STATUS_SYNCCANNOTBECOMPLETED);
$sourcekeyprops = mapi_getprops($mapimessage, array (PR_SOURCE_KEY));
return bin2hex($sourcekeyprops[PR_SOURCE_KEY]);
}
else
throw new StatusException(sprintf("ImportChangesICS->ImportMessageChange('%s','%s'): Error updating object: 0x%X", $id, get_class($message), mapi_last_hresult()), SYNC_STATUS_OBJECTNOTFOUND);
}
/**
* Imports a deletion. This may conflict if the local object has been modified
*
* @param string $id
*
* @access public
* @return boolean
* @throws StatusException
*/
public function ImportMessageDeletion($id) {
// check if the message is in the current syncinterval
if (!$this->isMessageInSyncInterval($id))
throw new StatusException(sprintf("ImportChangesICS->ImportMessageDeletion('%s'): Message is outside the sync interval and so far not deleted.", $id), SYNC_STATUS_OBJECTNOTFOUND);
// check for conflicts
$this->lazyLoadConflicts();
if($this->memChanges->IsChanged($id)) {
ZLog::Write(LOGLEVEL_INFO, sprintf("ImportChangesICS->ImportMessageDeletion('%s'): Conflict detected. Data from Server will be dropped! PIM deleted object.", $id));
}
elseif($this->memChanges->IsDeleted($id)) {
ZLog::Write(LOGLEVEL_INFO, sprintf("ImportChangesICS->ImportMessageDeletion('%s'): Conflict detected. Data is already deleted. Request will be ignored.", $id));
return true;
}
// do a 'soft' delete so people can un-delete if necessary
if(mapi_importcontentschanges_importmessagedeletion($this->importer, 1, array(hex2bin($id))))
throw new StatusException(sprintf("ImportChangesICS->ImportMessageDeletion('%s'): Error updating object: 0x%X", $id, mapi_last_hresult()), SYNC_STATUS_OBJECTNOTFOUND);
return true;
}
/**
* Imports a change in 'read' flag
* This can never conflict
*
* @param string $id
* @param int $flags - read/unread
*
* @access public
* @return boolean
* @throws StatusException
*/
public function ImportMessageReadFlag($id, $flags) {
// check if the message is in the current syncinterval
if (!$this->isMessageInSyncInterval($id))
throw new StatusException(sprintf("ImportChangesICS->ImportMessageReadFlag('%s','%d'): Message is outside the sync interval. Flags not updated.", $id, $flags), SYNC_STATUS_OBJECTNOTFOUND);
// check for conflicts
/*
* Checking for conflicts is correct at this point, but is a very expensive operation.
* If the message was deleted, only an error will be shown.
*
$this->lazyLoadConflicts();
if($this->memChanges->IsDeleted($id)) {
ZLog::Write(LOGLEVEL_INFO, sprintf("ImportChangesICS->ImportMessageReadFlag('%s'): Conflict detected. Data is already deleted. Request will be ignored.", $id));
return true;
}
*/
$readstate = array ( "sourcekey" => hex2bin($id), "flags" => $flags);
if(!mapi_importcontentschanges_importperuserreadstatechange($this->importer, array($readstate) ))
throw new StatusException(sprintf("ImportChangesICS->ImportMessageReadFlag('%s','%d'): Error setting read state: 0x%X", $id, $flags, mapi_last_hresult()), SYNC_STATUS_OBJECTNOTFOUND);
return true;
}
/**
* Imports a move of a message. This occurs when a user moves an item to another folder
*
* Normally, we would implement this via the 'offical' importmessagemove() function on the ICS importer,
* but the Zarafa importer does not support this. Therefore we currently implement it via a standard mapi
* call. This causes a mirror 'add/delete' to be sent to the PDA at the next sync.
* Manfred, 2010-10-21. For some mobiles import was causing duplicate messages in the destination folder
* (Mantis #202). Therefore we will create a new message in the destination folder, copy properties
* of the source message to the new one and then delete the source message.
*
* @param string $id
* @param string $newfolder destination folder
*
* @access public
* @return boolean
* @throws StatusException
*/
public function ImportMessageMove($id, $newfolder) {
if (strtolower($newfolder) == strtolower(bin2hex($this->folderid)) )
throw new StatusException(sprintf("ImportChangesICS->ImportMessageMove('%s','%s'): Error, source and destination are equal", $id, $newfolder), SYNC_MOVEITEMSSTATUS_SAMESOURCEANDDEST);
// Get the entryid of the message we're moving
$entryid = mapi_msgstore_entryidfromsourcekey($this->store, $this->folderid, hex2bin($id));
if(!$entryid)
throw new StatusException(sprintf("ImportChangesICS->ImportMessageMove('%s','%s'): Error, unable to resolve source message id", $id, $newfolder), SYNC_MOVEITEMSSTATUS_INVALIDSOURCEID);
//open the source message
$srcmessage = mapi_msgstore_openentry($this->store, $entryid);
if (!$srcmessage) {
$code = SYNC_MOVEITEMSSTATUS_INVALIDSOURCEID;
// if we move to the trash and the source message is not found, we can also just tell the mobile that we successfully moved to avoid errors (ZP-624)
if ($newfolder == ZPush::GetBackend()->GetWasteBasket()) {
$code = SYNC_MOVEITEMSSTATUS_SUCCESS;
}
throw new StatusException(sprintf("ImportChangesICS->ImportMessageMove('%s','%s'): Error, unable to open source message: 0x%X", $id, $newfolder, mapi_last_hresult()), $code);
}
// check if the source message is in the current syncinterval
if (!$this->isMessageInSyncInterval($id))
throw new StatusException(sprintf("ImportChangesICS->ImportMessageMove('%s','%s'): Source message is outside the sync interval. Move not performed.", $id, $newfolder), SYNC_MOVEITEMSSTATUS_INVALIDSOURCEID);
// get correct mapi store for the destination folder
$dststore = ZPush::GetBackend()->GetMAPIStoreForFolderId(ZPush::GetAdditionalSyncFolderStore($newfolder), $newfolder);
if ($dststore === false)
throw new StatusException(sprintf("ImportChangesICS->ImportMessageMove('%s','%s'): Error, unable to open store of destination folder", $id, $newfolder), SYNC_MOVEITEMSSTATUS_INVALIDDESTID);
$dstentryid = mapi_msgstore_entryidfromsourcekey($dststore, hex2bin($newfolder));
if(!$dstentryid)
throw new StatusException(sprintf("ImportChangesICS->ImportMessageMove('%s','%s'): Error, unable to resolve destination folder", $id, $newfolder), SYNC_MOVEITEMSSTATUS_INVALIDDESTID);
$dstfolder = mapi_msgstore_openentry($dststore, $dstentryid);
if(!$dstfolder)
throw new StatusException(sprintf("ImportChangesICS->ImportMessageMove('%s','%s'): Error, unable to open destination folder", $id, $newfolder), SYNC_MOVEITEMSSTATUS_INVALIDDESTID);
$newmessage = mapi_folder_createmessage($dstfolder);
if (!$newmessage)
throw new StatusException(sprintf("ImportChangesICS->ImportMessageMove('%s','%s'): Error, unable to create message in destination folder: 0x%X", $id, $newfolder, mapi_last_hresult()), SYNC_MOVEITEMSSTATUS_INVALIDDESTID);
// Copy message
mapi_copyto($srcmessage, array(), array(), $newmessage);
if (mapi_last_hresult())
throw new StatusException(sprintf("ImportChangesICS->ImportMessageMove('%s','%s'): Error, copy to destination message failed: 0x%X", $id, $newfolder, mapi_last_hresult()), SYNC_MOVEITEMSSTATUS_CANNOTMOVE);
$srcfolderentryid = mapi_msgstore_entryidfromsourcekey($this->store, $this->folderid);
if(!$srcfolderentryid)
throw new StatusException(sprintf("ImportChangesICS->ImportMessageMove('%s','%s'): Error, unable to resolve source folder", $id, $newfolder), SYNC_MOVEITEMSSTATUS_INVALIDSOURCEID);
$srcfolder = mapi_msgstore_openentry($this->store, $srcfolderentryid);
if (!$srcfolder)
throw new StatusException(sprintf("ImportChangesICS->ImportMessageMove('%s','%s'): Error, unable to open source folder: 0x%X", $id, $newfolder, mapi_last_hresult()), SYNC_MOVEITEMSSTATUS_INVALIDSOURCEID);
// Save changes
mapi_savechanges($newmessage);
if (mapi_last_hresult())
throw new StatusException(sprintf("ImportChangesICS->ImportMessageMove('%s','%s'): Error, mapi_savechanges() failed: 0x%X", $id, $newfolder, mapi_last_hresult()), SYNC_MOVEITEMSSTATUS_CANNOTMOVE);
// Delete the old message
if (!mapi_folder_deletemessages($srcfolder, array($entryid)))
throw new StatusException(sprintf("ImportChangesICS->ImportMessageMove('%s','%s'): Error, delete of source message failed: 0x%X. Possible duplicates.", $id, $newfolder, mapi_last_hresult()), SYNC_MOVEITEMSSTATUS_SOURCEORDESTLOCKED);
$sourcekeyprops = mapi_getprops($newmessage, array (PR_SOURCE_KEY));
if (isset($sourcekeyprops[PR_SOURCE_KEY]) && $sourcekeyprops[PR_SOURCE_KEY])
return bin2hex($sourcekeyprops[PR_SOURCE_KEY]);
return false;
}
/**----------------------------------------------------------------------------------------------------------
* Methods for HierarchyExporter
*/
/**
* Imports a change on a folder
*
* @param object $folder SyncFolder
*
* @access public
* @return string id of the folder
* @throws StatusException
*/
public function ImportFolderChange($folder) {
$id = isset($folder->serverid)?$folder->serverid:false;
$parent = $folder->parentid;
$displayname = u2wi($folder->displayname);
$type = $folder->type;
if (Utils::IsSystemFolder($type))
throw new StatusException(sprintf("ImportChangesICS->ImportFolderChange('%s','%s','%s'): Error, system folder can not be created/modified", Utils::PrintAsString($folder->serverid), $folder->parentid, $displayname), SYNC_FSSTATUS_SYSTEMFOLDER);
// create a new folder if $id is not set
if (!$id) {
// the root folder is "0" - get IPM_SUBTREE
if ($parent == "0") {
$parentprops = mapi_getprops($this->store, array(PR_IPM_SUBTREE_ENTRYID));
if (isset($parentprops[PR_IPM_SUBTREE_ENTRYID]))
$parentfentryid = $parentprops[PR_IPM_SUBTREE_ENTRYID];
}
else
$parentfentryid = mapi_msgstore_entryidfromsourcekey($this->store, hex2bin($parent));
if (!$parentfentryid)
throw new StatusException(sprintf("ImportChangesICS->ImportFolderChange('%s','%s','%s'): Error, unable to open parent folder (no entry id)", Utils::PrintAsString(false), $folder->parentid, $displayname), SYNC_FSSTATUS_PARENTNOTFOUND);
$parentfolder = mapi_msgstore_openentry($this->store, $parentfentryid);
if (!$parentfolder)
throw new StatusException(sprintf("ImportChangesICS->ImportFolderChange('%s','%s','%s'): Error, unable to open parent folder (open entry)", Utils::PrintAsString(false), $folder->parentid, $displayname), SYNC_FSSTATUS_PARENTNOTFOUND);
// mapi_folder_createfolder() fails if a folder with this name already exists -> MAPI_E_COLLISION
$newfolder = mapi_folder_createfolder($parentfolder, $displayname, "");
if (mapi_last_hresult())
throw new StatusException(sprintf("ImportChangesICS->ImportFolderChange('%s','%s','%s'): Error, mapi_folder_createfolder() failed: 0x%X", Utils::PrintAsString(false), $folder->parentid, $displayname, mapi_last_hresult()), SYNC_FSSTATUS_FOLDEREXISTS);
mapi_setprops($newfolder, array(PR_CONTAINER_CLASS => MAPIUtils::GetContainerClassFromFolderType($type)));
$props = mapi_getprops($newfolder, array(PR_SOURCE_KEY));
if (isset($props[PR_SOURCE_KEY])) {
$sourcekey = bin2hex($props[PR_SOURCE_KEY]);
ZLog::Write(LOGLEVEL_DEBUG, sprintf("Created folder '%s' with id: '%s'", $displayname, $sourcekey));
return $sourcekey;
}
else
throw new StatusException(sprintf("ImportChangesICS->ImportFolderChange('%s','%s','%s'): Error, folder created but PR_SOURCE_KEY not available: 0x%X", Utils::PrintAsString($folder->serverid), $folder->parentid, $displayname, mapi_last_hresult()), SYNC_FSSTATUS_SERVERERROR);
return false;
}
// open folder for update
$entryid = mapi_msgstore_entryidfromsourcekey($this->store, hex2bin($id));
if (!$entryid)
throw new StatusException(sprintf("ImportChangesICS->ImportFolderChange('%s','%s','%s'): Error, unable to open folder (no entry id): 0x%X", Utils::PrintAsString($folder->serverid), $folder->parentid, $displayname, mapi_last_hresult()), SYNC_FSSTATUS_PARENTNOTFOUND);
// check if this is a MAPI default folder
if ($this->mapiprovider->IsMAPIDefaultFolder($entryid))
throw new StatusException(sprintf("ImportChangesICS->ImportFolderChange('%s','%s','%s'): Error, MAPI default folder can not be created/modified", Utils::PrintAsString($folder->serverid), $folder->parentid, $displayname), SYNC_FSSTATUS_SYSTEMFOLDER);
$mfolder = mapi_msgstore_openentry($this->store, $entryid);
if (!$mfolder)
throw new StatusException(sprintf("ImportChangesICS->ImportFolderChange('%s','%s','%s'): Error, unable to open folder (open entry): 0x%X", Utils::PrintAsString($folder->serverid), $folder->parentid, $displayname, mapi_last_hresult()), SYNC_FSSTATUS_PARENTNOTFOUND);
$props = mapi_getprops($mfolder, array(PR_SOURCE_KEY, PR_PARENT_SOURCE_KEY, PR_DISPLAY_NAME, PR_CONTAINER_CLASS));
if (!isset($props[PR_SOURCE_KEY]) || !isset($props[PR_PARENT_SOURCE_KEY]) || !isset($props[PR_DISPLAY_NAME]) || !isset($props[PR_CONTAINER_CLASS]))
throw new StatusException(sprintf("ImportChangesICS->ImportFolderChange('%s','%s','%s'): Error, folder data not available: 0x%X", Utils::PrintAsString($folder->serverid), $folder->parentid, $displayname, mapi_last_hresult()), SYNC_FSSTATUS_SERVERERROR);
// get the real parent source key from mapi
if ($parent == "0") {
$parentprops = mapi_getprops($this->store, array(PR_IPM_SUBTREE_ENTRYID));
$parentfentryid = $parentprops[PR_IPM_SUBTREE_ENTRYID];
$mapifolder = mapi_msgstore_openentry($this->store, $parentfentryid);
$rootfolderprops = mapi_getprops($mapifolder, array(PR_SOURCE_KEY));
$parent = bin2hex($rootfolderprops[PR_SOURCE_KEY]);
ZLog::Write(LOGLEVEL_DEBUG, sprintf("ImportChangesICS->ImportFolderChange(): resolved AS parent '0' to sourcekey '%s'", $parent));
}
// a changed parent id means that the folder should be moved
if (bin2hex($props[PR_PARENT_SOURCE_KEY]) !== $parent) {
$sourceparentfentryid = mapi_msgstore_entryidfromsourcekey($this->store, $props[PR_PARENT_SOURCE_KEY]);
if(!$sourceparentfentryid)
throw new StatusException(sprintf("ImportChangesICS->ImportFolderChange('%s','%s','%s'): Error, unable to open parent source folder (no entry id): 0x%X", Utils::PrintAsString($folder->serverid), $folder->parentid, $displayname, mapi_last_hresult()), SYNC_FSSTATUS_PARENTNOTFOUND);
$sourceparentfolder = mapi_msgstore_openentry($this->store, $sourceparentfentryid);
if(!$sourceparentfolder)
throw new StatusException(sprintf("ImportChangesICS->ImportFolderChange('%s','%s','%s'): Error, unable to open parent source folder (open entry): 0x%X", Utils::PrintAsString($folder->serverid), $folder->parentid, $displayname, mapi_last_hresult()), SYNC_FSSTATUS_PARENTNOTFOUND);
$destparentfentryid = mapi_msgstore_entryidfromsourcekey($this->store, hex2bin($parent));
if(!$sourceparentfentryid)
throw new StatusException(sprintf("ImportChangesICS->ImportFolderChange('%s','%s','%s'): Error, unable to open destination folder (no entry id): 0x%X", Utils::PrintAsString($folder->serverid), $folder->parentid, $displayname, mapi_last_hresult()), SYNC_FSSTATUS_SERVERERROR);
$destfolder = mapi_msgstore_openentry($this->store, $destparentfentryid);
if(!$destfolder)
throw new StatusException(sprintf("ImportChangesICS->ImportFolderChange('%s','%s','%s'): Error, unable to open destination folder (open entry): 0x%X", Utils::PrintAsString($folder->serverid), $folder->parentid, $displayname, mapi_last_hresult()), SYNC_FSSTATUS_SERVERERROR);
// mapi_folder_copyfolder() fails if a folder with this name already exists -> MAPI_E_COLLISION
if(! mapi_folder_copyfolder($sourceparentfolder, $entryid, $destfolder, $displayname, FOLDER_MOVE))
throw new StatusException(sprintf("ImportChangesICS->ImportFolderChange('%s','%s','%s'): Error, unable to move folder: 0x%X", Utils::PrintAsString($folder->serverid), $folder->parentid, $displayname, mapi_last_hresult()), SYNC_FSSTATUS_FOLDEREXISTS);
$folderProps = mapi_getprops($mfolder, array(PR_SOURCE_KEY));
return $folderProps[PR_SOURCE_KEY];
}
// update the display name
$props = array(PR_DISPLAY_NAME => $displayname);
mapi_setprops($mfolder, $props);
mapi_savechanges($mfolder);
if (mapi_last_hresult())
throw new StatusException(sprintf("ImportChangesICS->ImportFolderChange('%s','%s','%s'): Error, mapi_savechanges() failed: 0x%X", Utils::PrintAsString($folder->serverid), $folder->parentid, $displayname, mapi_last_hresult()), SYNC_FSSTATUS_SERVERERROR);
ZLog::Write(LOGLEVEL_DEBUG, "Imported changes for folder: $id");
return $id;
}
/**
* Imports a folder deletion
*
* @param string $id
* @param string $parent id is ignored in ICS
*
* @access public
* @return int SYNC_FOLDERHIERARCHY_STATUS
* @throws StatusException
*/
public function ImportFolderDeletion($id, $parent = false) {
ZLog::Write(LOGLEVEL_DEBUG, sprintf("ImportChangesICS->ImportFolderDeletion('%s','%s'): importing folder deletetion", $id, $parent));
$folderentryid = mapi_msgstore_entryidfromsourcekey($this->store, hex2bin($id));
if(!$folderentryid)
throw new StatusException(sprintf("ImportChangesICS->ImportFolderDeletion('%s','%s'): Error, unable to resolve folder", $id, $parent, mapi_last_hresult()), SYNC_FSSTATUS_FOLDERDOESNOTEXIST);
// get the folder type from the MAPIProvider
$type = $this->mapiprovider->GetFolderType($folderentryid);
if (Utils::IsSystemFolder($type) || $this->mapiprovider->IsMAPIDefaultFolder($folderentryid))
throw new StatusException(sprintf("ImportChangesICS->ImportFolderDeletion('%s','%s'): Error deleting system/default folder", $id, $parent), SYNC_FSSTATUS_SYSTEMFOLDER);
$ret = mapi_importhierarchychanges_importfolderdeletion ($this->importer, 0, array(PR_SOURCE_KEY => hex2bin($id)));
if (!$ret)
throw new StatusException(sprintf("ImportChangesICS->ImportFolderDeletion('%s','%s'): Error deleting folder: 0x%X", $id, $parent, mapi_last_hresult()), SYNC_FSSTATUS_SERVERERROR);
return $ret;
}
}

View file

@ -1,192 +0,0 @@
#!/usr/bin/php
<?php
/***********************************************
* File : listfolders.php
* Project : Z-Push
* Descr : This is a small command line
* tool to list folders of a user
* store or public folder available
* for synchronization.
*
* Created : 06.05.2011
*
* 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 <http://www.gnu.org/licenses/>.
*
* Consult LICENSE file for details
************************************************/
define("PHP_MAPI_PATH", "/usr/share/php/mapi/");
define('MAPI_SERVER', 'file:///var/run/zarafa');
define('SSLCERT_FILE', null);
define('SSLCERT_PASS', null);
$supported_classes = array (
"IPF.Note" => "SYNC_FOLDER_TYPE_USER_MAIL",
"IPF.Task" => "SYNC_FOLDER_TYPE_USER_TASK",
"IPF.Appointment" => "SYNC_FOLDER_TYPE_USER_APPOINTMENT",
"IPF.Contact" => "SYNC_FOLDER_TYPE_USER_CONTACT",
"IPF.StickyNote" => "SYNC_FOLDER_TYPE_USER_NOTE"
);
main();
function main() {
listfolders_configure();
listfolders_handle();
}
function listfolders_configure() {
if (php_sapi_name() != "cli") {
printf("This script should not be called in a browser. Called from: %s\n", php_sapi_name());
exit(1);
}
if (!function_exists("getopt")) {
echo "PHP Function 'getopt()' not found. Please check your PHP version and settings.\n";
exit(1);
}
require(PHP_MAPI_PATH.'mapi.util.php');
require(PHP_MAPI_PATH.'mapidefs.php');
require(PHP_MAPI_PATH.'mapicode.php');
require(PHP_MAPI_PATH.'mapitags.php');
require(PHP_MAPI_PATH.'mapiguid.php');
}
function listfolders_handle() {
$shortoptions = "l:h:u:p:c:";
$options = getopt($shortoptions);
$mapi = MAPI_SERVER;
$sslcert_file = SSLCERT_FILE;
$sslcert_pass = SSLCERT_PASS;
$user = "SYSTEM";
$pass = "";
if (isset($options['h']))
$mapi = $options['h'];
// accept a remote user
if (isset($options['u']) && isset($options['p'])) {
$user = $options['u'];
$pass = $options['p'];
}
// accept a certificate and passwort for login
else if (isset($options['c']) && isset($options['p'])) {
$sslcert_file = $options['c'];
$sslcert_pass = $options['p'];
}
$zarafaAdmin = listfolders_zarafa_admin_setup($mapi, $user, $pass, $sslcert_file, $sslcert_pass);
if (isset($zarafaAdmin['adminStore']) && isset($options['l'])) {
listfolders_getlist($zarafaAdmin['adminStore'], $zarafaAdmin['session'], trim($options['l']));
}
else {
echo "Usage:\nlistfolders.php [actions] [options]\n\nActions: [-l username]\n\t-l username\tlist folders of user, for public folder use 'SYSTEM'\n\nGlobal options: [-h path] [[-u remoteuser] [-p password]] [[-c certificate_path] [-p password]]\n\t-h path\t\tconnect through <path>, e.g. file:///var/run/socket or https://10.0.0.1:237/zarafa\n\t-u remoteuser\tlogin as authenticated administration user\n\t-c certificate\tlogin with a ssl certificate located in this location, e.g. /etc/zarafa/ssl/client.pem\n\t-p password\tpassword of the remoteuser or certificate\n\n";
}
}
function listfolders_zarafa_admin_setup ($mapi, $user, $pass, $sslcert_file, $sslcert_pass) {
$session = @mapi_logon_zarafa($user, $pass, $mapi, $sslcert_file, $sslcert_pass);
if (!$session) {
echo "User '$user' could not login. The script will exit. Errorcode: 0x". sprintf("%x", mapi_last_hresult()) . "\n";
exit(1);
}
$stores = @mapi_getmsgstorestable($session);
$storeslist = @mapi_table_queryallrows($stores);
$adminStore = @mapi_openmsgstore($session, $storeslist[0][PR_ENTRYID]);
$zarafauserinfo = @mapi_zarafa_getuser_by_name($adminStore, $user);
$admin = (isset($zarafauserinfo['admin']) && $zarafauserinfo['admin'])?true:false;
if (!$stores || !$storeslist || !$adminStore || !$admin) {
echo "There was error trying to log in as admin or retrieving admin info. The script will exit.\n";
exit(1);
}
return array("session" => $session, "adminStore" => $adminStore);
}
function listfolders_getlist ($adminStore, $session, $user) {
global $supported_classes;
if (strtoupper($user) == 'SYSTEM') {
// Find the public store store
$storestables = @mapi_getmsgstorestable($session);
$result = @mapi_last_hresult();
if ($result == NOERROR){
$rows = @mapi_table_queryallrows($storestables, array(PR_ENTRYID, PR_MDB_PROVIDER));
foreach($rows as $row) {
if (isset($row[PR_MDB_PROVIDER]) && $row[PR_MDB_PROVIDER] == ZARAFA_STORE_PUBLIC_GUID) {
if (!isset($row[PR_ENTRYID])) {
echo "Public folder are not available.\nIf this is a multi-tenancy system, use -u and -p and login with an admin user of the company.\nThe script will exit.\n";
exit (1);
}
$entryid = $row[PR_ENTRYID];
break;
}
}
}
}
else
$entryid = @mapi_msgstore_createentryid($adminStore, $user);
$userStore = @mapi_openmsgstore($session, $entryid);
$hresult = mapi_last_hresult();
// Cache the store for later use
if($hresult != NOERROR) {
echo "Could not open store for '$user'. The script will exit.\n";
exit (1);
}
$folder = @mapi_msgstore_openentry($userStore);
$h_table = @mapi_folder_gethierarchytable($folder, CONVENIENT_DEPTH);
$subfolders = @mapi_table_queryallrows($h_table, array(PR_ENTRYID, PR_DISPLAY_NAME, PR_CONTAINER_CLASS, PR_SOURCE_KEY));
echo "Available folders in store '$user':\n" . str_repeat("-", 50) . "\n";
foreach($subfolders as $folder) {
if (isset($folder[PR_CONTAINER_CLASS]) && array_key_exists($folder[PR_CONTAINER_CLASS], $supported_classes)) {
echo "Folder name:\t". $folder[PR_DISPLAY_NAME] . "\n";
echo "Folder ID:\t". bin2hex($folder[PR_SOURCE_KEY]) . "\n";
echo "Type:\t\t". $supported_classes[$folder[PR_CONTAINER_CLASS]] . "\n";
echo "\n";
}
}
}

View file

@ -1,225 +0,0 @@
<?php
/*
* Copyright 2005 - 2013 Zarafa B.V.
*
* 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. 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 "Zarafa" 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 "Zarafa" or "Zarafa Server", you may only do so if you
* have a written permission by Zarafa B.V. (to acquire a permission
* please contact Zarafa at trademark@zarafa.com).
*
* The interactive user interface of the software displays an attribution
* notice containing the term "Zarafa" and/or the logo of Zarafa.
* Interactive user interfaces of unmodified and modified versions must
* display Appropriate Legal Notices according to sec. 5 of the GNU
* Affero General Public License, version 3, when you propagate
* unmodified or modified versions of the Program. In accordance with
* sec. 7 b) of the GNU Affero General Public License, version 3, these
* Appropriate Legal Notices must retain the logo of Zarafa or display
* the words "Initial Development by Zarafa" if the display of the logo
* is not reasonably feasible for technical reasons. The use of the logo
* of Zarafa in Legal Notices is allowed for unmodified and modified
* versions of the software.
*
* 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 <http://www.gnu.org/licenses/>.
*
*/
/**
* Defines a base exception class for all custom exceptions, so every exceptions that
* is thrown/caught by this application should extend this base class and make use of it.
* it removes some peculiarities between different versions of PHP and exception handling.
*
* Some basic function of Exception class
* getMessage()- message of exception
* getCode() - code of exception
* getFile() - source filename
* getLine() - source line
* getTrace() - n array of the backtrace()
* getTraceAsString() - formated string of trace
*/
class BaseException extends Exception
{
/**
* Reference of previous exception, only used for PHP < 5.3
* can't use $previous here as its a private variable of parent class
*/
private $_previous = null;
/**
* Base name of the file, so we don't have to use static path of the file
*/
private $baseFile = null;
/**
* Flag to check if exception is already handled or not
*/
public $isHandled = false;
/**
* The exception message to show at client side.
*/
public $displayMessage = null;
/**
* Construct the exception
*
* @param string $errorMessage
* @param int $code
* @param Exception $previous
* @param string $displayMessage
* @return void
*/
public function __construct($errorMessage, $code = 0, Exception $previous = null, $displayMessage = null) {
// assign display message
$this->displayMessage = $displayMessage;
if (version_compare(PHP_VERSION, '5.3.0', '<')) {
parent::__construct($errorMessage, (int) $code);
// set previous exception
if(!is_null($previous)) {
$this->_previous = $previous;
}
} else {
parent::__construct($errorMessage, (int) $code, $previous);
}
}
/**
* Overloading of final methods to get rid of incompatibilities between different PHP versions.
*
* @param string $method
* @param array $args
* @return mixed
*/
public function __call($method, array $args)
{
if ('getprevious' == strtolower($method)) {
return $this->_getPrevious();
}
return null;
}
/**
* @return string returns file name and line number combined where exception occured.
*/
public function getFileLine()
{
return $this->getBaseFile() . ':' . $this->getLine();
}
/**
* @return string returns message that should be sent to client to display
*/
public function getDisplayMessage()
{
if(!is_null($this->displayMessage)) {
return $this->displayMessage;
}
return $this->getMessage();
}
/**
* Function sets display message of an exception that will be sent to the client side
* to show it to user.
* @param string $message display message.
*/
public function setDisplayMessage($message)
{
$this->displayMessage = $message;
}
/**
* Function sets a flag in exception class to indicate that exception is already handled
* so if it is caught again in the top level of function stack then we have to silently
* ignore it.
*/
public function setHandled()
{
$this->isHandled = true;
}
/**
* @return string returns base path of the file where exception occured.
*/
public function getBaseFile()
{
if(is_null($this->baseFile)) {
$this->baseFile = basename(parent::getFile());
}
return $this->baseFile;
}
/**
* Function will check for PHP version if it is greater than 5.3 then we can use its default implementation
* otherwise we have to use our own implementation of chanining functionality.
*
* @return Exception returns previous exception
*/
public function _getPrevious()
{
if (version_compare(PHP_VERSION, '5.3.0', '<')) {
return $this->_previous;
} else {
return parent::getPrevious();
}
}
/**
* String representation of the exception, also handles previous exception.
*
* @return string
*/
public function __toString()
{
if (version_compare(PHP_VERSION, '5.3.0', '<')) {
if (($e = $this->getPrevious()) !== null) {
return $e->__toString()
. "\n\nNext "
. parent::__toString();
}
}
return parent::__toString();
}
/**
* Name of the class of exception.
*
* @return string
*/
public function getName()
{
return get_class($this);
}
// @TODO getTrace and getTraceAsString
}

File diff suppressed because it is too large Load diff

View file

@ -1,396 +0,0 @@
<?php
/*
* Copyright 2005 - 2013 Zarafa B.V.
*
* 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. 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 "Zarafa" 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 "Zarafa" or "Zarafa Server", you may only do so if you
* have a written permission by Zarafa B.V. (to acquire a permission
* please contact Zarafa at trademark@zarafa.com).
*
* The interactive user interface of the software displays an attribution
* notice containing the term "Zarafa" and/or the logo of Zarafa.
* Interactive user interfaces of unmodified and modified versions must
* display Appropriate Legal Notices according to sec. 5 of the GNU
* Affero General Public License, version 3, when you propagate
* unmodified or modified versions of the Program. In accordance with
* sec. 7 b) of the GNU Affero General Public License, version 3, these
* Appropriate Legal Notices must retain the logo of Zarafa or display
* the words "Initial Development by Zarafa" if the display of the logo
* is not reasonably feasible for technical reasons. The use of the logo
* of Zarafa in Legal Notices is allowed for unmodified and modified
* versions of the software.
*
* 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 <http://www.gnu.org/licenses/>.
*
*/
include_once('backend/zarafa/mapi/class.recurrence.php');
class FreeBusyPublish {
var $session;
var $calendar;
var $entryid;
var $starttime;
var $length;
var $store;
var $proptags;
/**
* Constuctor
*
* @param mapi_session $session MAPI Session
* @param mapi_folder $calendar Calendar to publish
* @param string $entryid AddressBook Entry ID for the user we're publishing for
*/
function FreeBusyPublish($session, $store, $calendar, $entryid)
{
$properties["entryid"] = PR_ENTRYID;
$properties["parent_entryid"] = PR_PARENT_ENTRYID;
$properties["message_class"] = PR_MESSAGE_CLASS;
$properties["icon_index"] = PR_ICON_INDEX;
$properties["subject"] = PR_SUBJECT;
$properties["display_to"] = PR_DISPLAY_TO;
$properties["importance"] = PR_IMPORTANCE;
$properties["sensitivity"] = PR_SENSITIVITY;
$properties["startdate"] = "PT_SYSTIME:PSETID_Appointment:0x820d";
$properties["duedate"] = "PT_SYSTIME:PSETID_Appointment:0x820e";
$properties["recurring"] = "PT_BOOLEAN:PSETID_Appointment:0x8223";
$properties["recurring_data"] = "PT_BINARY:PSETID_Appointment:0x8216";
$properties["busystatus"] = "PT_LONG:PSETID_Appointment:0x8205";
$properties["label"] = "PT_LONG:PSETID_Appointment:0x8214";
$properties["alldayevent"] = "PT_BOOLEAN:PSETID_Appointment:0x8215";
$properties["private"] = "PT_BOOLEAN:PSETID_Common:0x8506";
$properties["meeting"] = "PT_LONG:PSETID_Appointment:0x8217";
$properties["startdate_recurring"] = "PT_SYSTIME:PSETID_Appointment:0x8235";
$properties["enddate_recurring"] = "PT_SYSTIME:PSETID_Appointment:0x8236";
$properties["location"] = "PT_STRING8:PSETID_Appointment:0x8208";
$properties["duration"] = "PT_LONG:PSETID_Appointment:0x8213";
$properties["responsestatus"] = "PT_LONG:PSETID_Appointment:0x8218";
$properties["reminder"] = "PT_BOOLEAN:PSETID_Common:0x8503";
$properties["reminder_minutes"] = "PT_LONG:PSETID_Common:0x8501";
$properties["contacts"] = "PT_MV_STRING8:PSETID_Common:0x853a";
$properties["contacts_string"] = "PT_STRING8:PSETID_Common:0x8586";
$properties["categories"] = "PT_MV_STRING8:PS_PUBLIC_STRINGS:Keywords";
$properties["reminder_time"] = "PT_SYSTIME:PSETID_Common:0x8502";
$properties["commonstart"] = "PT_SYSTIME:PSETID_Common:0x8516";
$properties["commonend"] = "PT_SYSTIME:PSETID_Common:0x8517";
$properties["basedate"] = "PT_SYSTIME:PSETID_Appointment:0x8228";
$properties["timezone_data"] = "PT_BINARY:PSETID_Appointment:0x8233";
$this->proptags = getPropIdsFromStrings($store, $properties);
$this->session = $session;
$this->calendar = $calendar;
$this->entryid = $entryid;
$this->store = $store;
}
/**
* Publishes the infomation
* @paam timestamp $starttime Time from which to publish data (usually now)
* @paam integer $length Amount of seconds from $starttime we should publish
*/
function publishFB($starttime, $length) {
$start = $starttime;
$end = $starttime + $length;
// Get all the items in the calendar that we need
$calendaritems = Array();
$restrict = Array(RES_OR,
Array(
// OR
// (item[start] >= start && item[start] <= end)
Array(RES_AND,
Array(
Array(RES_PROPERTY,
Array(RELOP => RELOP_GE,
ULPROPTAG => $this->proptags["startdate"],
VALUE => $start
)
),
Array(RES_PROPERTY,
Array(RELOP => RELOP_LE,
ULPROPTAG => $this->proptags["startdate"],
VALUE => $end
)
)
)
),
// OR
// (item[end] >= start && item[end] <= end)
Array(RES_AND,
Array(
Array(RES_PROPERTY,
Array(RELOP => RELOP_GE,
ULPROPTAG => $this->proptags["duedate"],
VALUE => $start
)
),
Array(RES_PROPERTY,
Array(RELOP => RELOP_LE,
ULPROPTAG => $this->proptags["duedate"],
VALUE => $end
)
)
)
),
// OR
// (item[start] < start && item[end] > end)
Array(RES_AND,
Array(
Array(RES_PROPERTY,
Array(RELOP => RELOP_LT,
ULPROPTAG => $this->proptags["startdate"],
VALUE => $start
)
),
Array(RES_PROPERTY,
Array(RELOP => RELOP_GT,
ULPROPTAG => $this->proptags["duedate"],
VALUE => $end
)
)
)
),
// OR
Array(RES_OR,
Array(
// OR
// (EXIST(ecurrence_enddate_property) && item[isRecurring] == true && item[end] >= start)
Array(RES_AND,
Array(
Array(RES_EXIST,
Array(ULPROPTAG => $this->proptags["enddate_recurring"],
)
),
Array(RES_PROPERTY,
Array(RELOP => RELOP_EQ,
ULPROPTAG => $this->proptags["recurring"],
VALUE => true
)
),
Array(RES_PROPERTY,
Array(RELOP => RELOP_GE,
ULPROPTAG => $this->proptags["enddate_recurring"],
VALUE => $start
)
)
)
),
// OR
// (!EXIST(ecurrence_enddate_property) && item[isRecurring] == true && item[start] <= end)
Array(RES_AND,
Array(
Array(RES_NOT,
Array(
Array(RES_EXIST,
Array(ULPROPTAG => $this->proptags["enddate_recurring"]
)
)
)
),
Array(RES_PROPERTY,
Array(RELOP => RELOP_LE,
ULPROPTAG => $this->proptags["startdate"],
VALUE => $end
)
),
Array(RES_PROPERTY,
Array(RELOP => RELOP_EQ,
ULPROPTAG => $this->proptags["recurring"],
VALUE => true
)
)
)
)
)
) // EXISTS OR
)
); // global OR
$contents = mapi_folder_getcontentstable($this->calendar);
mapi_table_restrict($contents, $restrict);
while(1) {
$rows = mapi_table_queryrows($contents, array_values($this->proptags), 0, 50);
if(!is_array($rows))
break;
if(empty($rows))
break;
foreach ($rows as $row) {
$occurrences = Array();
if(isset($row[$this->proptags['recurring']]) && $row[$this->proptags['recurring']]) {
$recur = new Recurrence($this->store, $row);
$occurrences = $recur->getItems($starttime, $starttime + $length);
} else {
$occurrences[] = $row;
}
$calendaritems = array_merge($calendaritems, $occurrences);
}
}
// $calendaritems now contains all the calendar items in the specified time
// frame. We now need to merge these into a flat array of begin/end/status
// objects. This also filters out all the 'free' items (status 0)
$freebusy = $this->mergeItemsFB($calendaritems);
// $freebusy now contains the start, end and status of all items, merged.
// Get the FB interface
try {
$fbsupport = mapi_freebusysupport_open($this->session, $this->store);
} catch (MAPIException $e) {
if($e->getCode() == MAPI_E_NOT_FOUND) {
$e->setHandled();
if(function_exists("dump")) {
dump("Error in opening freebusysupport object.");
}
}
}
// Open updater for this user
if(isset($fbsupport) && $fbsupport) {
$updaters = mapi_freebusysupport_loadupdate($fbsupport, Array($this->entryid));
$updater = $updaters[0];
// Send the data
mapi_freebusyupdate_reset($updater);
mapi_freebusyupdate_publish($updater, $freebusy);
mapi_freebusyupdate_savechanges($updater, $start-24*60*60, $end);
// We're finished
mapi_freebusysupport_close($fbsupport);
}
else
ZLog::Write(LOGLEVEL_WARN, "FreeBusyPublish is not available");
}
/**
* Sorts by timestamp, if equal, then end before start
*/
function cmp($a, $b)
{
if ($a["time"] == $b["time"]) {
if($a["type"] < $b["type"])
return 1;
if($a["type"] > $b["type"])
return -1;
return 0;
}
return ($a["time"] > $b["time"] ? 1 : -1);
}
/**
* Function mergeItems
* @author Steve Hardy
*/
function mergeItemsFB($items)
{
$merged = Array();
$timestamps = Array();
$csubj = Array();
$cbusy = Array();
$level = 0;
$laststart = null;
foreach($items as $item)
{
$ts["type"] = 0;
$ts["time"] = $item[$this->proptags["startdate"]];
$ts["subject"] = $item[PR_SUBJECT];
$ts["status"] = (isset($item[$this->proptags["busystatus"]])) ? $item[$this->proptags["busystatus"]] : 0; //ZP-197
$timestamps[] = $ts;
$ts["type"] = 1;
$ts["time"] = $item[$this->proptags["duedate"]];
$ts["subject"] = $item[PR_SUBJECT];
$ts["status"] = (isset($item[$this->proptags["busystatus"]])) ? $item[$this->proptags["busystatus"]] : 0; //ZP-197
$timestamps[] = $ts;
}
usort($timestamps, Array($this, "cmp"));
$laststart = 0; // seb added
foreach($timestamps as $ts)
{
switch ($ts["type"])
{
case 0: // Start
if ($level != 0 && $laststart != $ts["time"])
{
$newitem["start"] = $laststart;
$newitem["end"] = $ts["time"];
$newitem["subject"] = join(",", $csubj);
$newitem["status"] = !empty($cbusy) ? max($cbusy) : 0;
if($newitem["status"] > 0)
$merged[] = $newitem;
}
$level++;
$csubj[] = $ts["subject"];
$cbusy[] = $ts["status"];
$laststart = $ts["time"];
break;
case 1: // End
if ($laststart != $ts["time"])
{
$newitem["start"] = $laststart;
$newitem["end"] = $ts["time"];
$newitem["subject"] = join(",", $csubj);
$newitem["status"] = !empty($cbusy) ? max($cbusy) : 0;
if($newitem["status"] > 0)
$merged[] = $newitem;
}
$level--;
array_splice($csubj, array_search($ts["subject"], $csubj, 1), 1);
array_splice($cbusy, array_search($ts["status"], $cbusy, 1), 1);
$laststart = $ts["time"];
break;
}
}
return $merged;
}
}

View file

@ -1,106 +0,0 @@
<?php
/*
* Copyright 2005 - 2013 Zarafa B.V.
*
* 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. 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 "Zarafa" 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 "Zarafa" or "Zarafa Server", you may only do so if you
* have a written permission by Zarafa B.V. (to acquire a permission
* please contact Zarafa at trademark@zarafa.com).
*
* The interactive user interface of the software displays an attribution
* notice containing the term "Zarafa" and/or the logo of Zarafa.
* Interactive user interfaces of unmodified and modified versions must
* display Appropriate Legal Notices according to sec. 5 of the GNU
* Affero General Public License, version 3, when you propagate
* unmodified or modified versions of the Program. In accordance with
* sec. 7 b) of the GNU Affero General Public License, version 3, these
* Appropriate Legal Notices must retain the logo of Zarafa or display
* the words "Initial Development by Zarafa" if the display of the logo
* is not reasonably feasible for technical reasons. The use of the logo
* of Zarafa in Legal Notices is allowed for unmodified and modified
* versions of the software.
*
* 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 <http://www.gnu.org/licenses/>.
*
*/
/**
* MAPIException
* if enabled using mapi_enable_exceptions then php-ext can throw exceptions when
* any error occurs in mapi calls. this exception will only be thrown when severity bit is set in
* error code that means it will be thrown only for mapi errors not for mapi warnings.
*/
// FatalException will trigger a HTTP return code 500 to the mobile
class MAPIException extends FatalException
{
/**
* Function will return display message of exception if its set by the calle.
* if it is not set then we are generating some default display messages based
* on mapi error code.
* @return string returns error-message that should be sent to client to display.
*/
public function getDisplayMessage()
{
if(!empty($this->displayMessage))
return $this->displayMessage;
switch($this->getCode())
{
case MAPI_E_NO_ACCESS:
return _("You have insufficient privileges to open this object.");
case MAPI_E_LOGON_FAILED:
case MAPI_E_UNCONFIGURED:
return _("Logon Failed. Please check your username/password.");
case MAPI_E_NETWORK_ERROR:
return _("Can not connect to Zarafa server.");
case MAPI_E_UNKNOWN_ENTRYID:
return _("Can not open object with provided id.");
case MAPI_E_NO_RECIPIENTS:
return _("There are no recipients in the message.");
case MAPI_E_NOT_FOUND:
return _("Can not find object.");
case MAPI_E_INTERFACE_NOT_SUPPORTED:
case MAPI_E_INVALID_PARAMETER:
case MAPI_E_INVALID_ENTRYID:
case MAPI_E_INVALID_OBJECT:
case MAPI_E_TOO_COMPLEX:
case MAPI_E_CORRUPT_DATA:
case MAPI_E_END_OF_SESSION:
case MAPI_E_AMBIGUOUS_RECIP:
case MAPI_E_COLLISION:
case MAPI_E_UNCONFIGURED:
default :
return sprintf(_("Unknown MAPI Error: %s"), get_mapi_error_name($this->getCode()));
}
}
}
// Tell the PHP extension which exception class to instantiate
if (function_exists('mapi_enable_exceptions')) {
//mapi_enable_exceptions("mapiexception");
}

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

View file

@ -1,463 +0,0 @@
<?php
/*
* Copyright 2005 - 2013 Zarafa B.V.
*
* 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. 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 "Zarafa" 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 "Zarafa" or "Zarafa Server", you may only do so if you
* have a written permission by Zarafa B.V. (to acquire a permission
* please contact Zarafa at trademark@zarafa.com).
*
* The interactive user interface of the software displays an attribution
* notice containing the term "Zarafa" and/or the logo of Zarafa.
* Interactive user interfaces of unmodified and modified versions must
* display Appropriate Legal Notices according to sec. 5 of the GNU
* Affero General Public License, version 3, when you propagate
* unmodified or modified versions of the Program. In accordance with
* sec. 7 b) of the GNU Affero General Public License, version 3, these
* Appropriate Legal Notices must retain the logo of Zarafa or display
* the words "Initial Development by Zarafa" if the display of the logo
* is not reasonably feasible for technical reasons. The use of the logo
* of Zarafa in Legal Notices is allowed for unmodified and modified
* versions of the software.
*
* 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 <http://www.gnu.org/licenses/>.
*
*/
require_once("backend/zarafa/mapi/class.baserecurrence.php");
class TaskRecurrence extends BaseRecurrence
{
/**
* Timezone info which is always false for task
*/
var $tz = false;
function TaskRecurrence($store, $message)
{
$this->store = $store;
$this->message = $message;
$properties = array();
$properties["entryid"] = PR_ENTRYID;
$properties["parent_entryid"] = PR_PARENT_ENTRYID;
$properties["icon_index"] = PR_ICON_INDEX;
$properties["message_class"] = PR_MESSAGE_CLASS;
$properties["message_flags"] = PR_MESSAGE_FLAGS;
$properties["subject"] = PR_SUBJECT;
$properties["importance"] = PR_IMPORTANCE;
$properties["sensitivity"] = PR_SENSITIVITY;
$properties["last_modification_time"] = PR_LAST_MODIFICATION_TIME;
$properties["status"] = "PT_LONG:PSETID_Task:0x8101";
$properties["percent_complete"] = "PT_DOUBLE:PSETID_Task:0x8102";
$properties["startdate"] = "PT_SYSTIME:PSETID_Task:0x8104";
$properties["duedate"] = "PT_SYSTIME:PSETID_Task:0x8105";
$properties["reset_reminder"] = "PT_BOOLEAN:PSETID_Task:0x8107";
$properties["dead_occurrence"] = "PT_BOOLEAN:PSETID_Task:0x8109";
$properties["datecompleted"] = "PT_SYSTIME:PSETID_Task:0x810f";
$properties["recurring_data"] = "PT_BINARY:PSETID_Task:0x8116";
$properties["actualwork"] = "PT_LONG:PSETID_Task:0x8110";
$properties["totalwork"] = "PT_LONG:PSETID_Task:0x8111";
$properties["complete"] = "PT_BOOLEAN:PSETID_Task:0x811c";
$properties["task_f_creator"] = "PT_BOOLEAN:PSETID_Task:0x811e";
$properties["owner"] = "PT_STRING8:PSETID_Task:0x811f";
$properties["recurring"] = "PT_BOOLEAN:PSETID_Task:0x8126";
$properties["reminder_minutes"] = "PT_LONG:PSETID_Common:0x8501";
$properties["reminder_time"] = "PT_SYSTIME:PSETID_Common:0x8502";
$properties["reminder"] = "PT_BOOLEAN:PSETID_Common:0x8503";
$properties["private"] = "PT_BOOLEAN:PSETID_Common:0x8506";
$properties["contacts"] = "PT_MV_STRING8:PSETID_Common:0x853a";
$properties["contacts_string"] = "PT_STRING8:PSETID_Common:0x8586";
$properties["categories"] = "PT_MV_STRING8:PS_PUBLIC_STRINGS:Keywords";
$properties["commonstart"] = "PT_SYSTIME:PSETID_Common:0x8516";
$properties["commonend"] = "PT_SYSTIME:PSETID_Common:0x8517";
$properties["commonassign"] = "PT_LONG:PSETID_Common:0x8518";
$properties["flagdueby"] = "PT_SYSTIME:PSETID_Common:0x8560";
$properties["side_effects"] = "PT_LONG:PSETID_Common:0x8510";
$properties["reminder"] = "PT_BOOLEAN:PSETID_Common:0x8503";
$properties["reminder_minutes"] = "PT_LONG:PSETID_Common:0x8501";
$this->proptags = getPropIdsFromStrings($store, $properties);
parent::BaseRecurrence($store, $message, $properties);
}
/**
* Function which saves recurrence and also regenerates task if necessary.
*@param array $recur new recurrence properties
*@return array of properties of regenerated task else false
*/
function setRecurrence(&$recur)
{
$this->recur = $recur;
$this->action =& $recur;
if(!isset($this->recur["changed_occurences"]))
$this->recur["changed_occurences"] = Array();
if(!isset($this->recur["deleted_occurences"]))
$this->recur["deleted_occurences"] = Array();
if (!isset($this->recur['startocc'])) $this->recur['startocc'] = 0;
if (!isset($this->recur['endocc'])) $this->recur['endocc'] = 0;
// Save recurrence because we need proper startrecurrdate and endrecurrdate
$this->saveRecurrence();
// Update $this->recur with proper startrecurrdate and endrecurrdate updated after saveing recurrence
$msgProps = mapi_getprops($this->message, array($this->proptags['recurring_data']));
$recurring_data = $this->parseRecurrence($msgProps[$this->proptags['recurring_data']]);
foreach($recurring_data as $key => $value) {
$this->recur[$key] = $value;
}
$this->setFirstOccurrence();
// Let's see if next occurrence has to be generated
return $this->moveToNextOccurrence();
}
/**
* Sets task object to first occurrence if startdate/duedate of task object is different from first occurrence
*/
function setFirstOccurrence()
{
// Check if it is already the first occurrence
if($this->action['start'] == $this->recur["start"]){
return;
}else{
$items = $this->getNextOccurrence();
$props = array();
$props[$this->proptags['startdate']] = $items[$this->proptags['startdate']];
$props[$this->proptags['commonstart']] = $items[$this->proptags['startdate']];
$props[$this->proptags['duedate']] = $items[$this->proptags['duedate']];
$props[$this->proptags['commonend']] = $items[$this->proptags['duedate']];
mapi_setprops($this->message, $props);
}
}
/**
* Function which creates new task as current occurrence and moves the
* existing task to next occurrence.
*
*@param array $recur $action from client
*@return boolean if moving to next occurrence succeed then it returns
* properties of either newly created task or existing task ELSE
* false because that was last occurrence
*/
function moveToNextOccurrence()
{
$result = false;
/**
* Every recurring task should have a 'duedate'. If a recurring task is created with no start/end date
* then we create first two occurrence separately and for first occurrence recurrence has ended.
*/
if ((empty($this->action['startdate']) && empty($this->action['duedate']))
|| ($this->action['complete'] == 1) || (isset($this->action['deleteOccurrence']) && $this->action['deleteOccurrence'])){
$nextOccurrence = $this->getNextOccurrence();
$result = mapi_getprops($this->message, array(PR_ENTRYID, PR_PARENT_ENTRYID, PR_STORE_ENTRYID));
$props = array();
if ($nextOccurrence) {
if (!isset($this->action['deleteOccurrence'])) {
// Create current occurrence as separate task
$result = $this->regenerateTask($this->action['complete']);
}
// Set reminder for next occurrence
$this->setReminder($nextOccurrence);
// Update properties for next occurrence
$this->action['duedate'] = $props[$this->proptags['duedate']] = $nextOccurrence[$this->proptags['duedate']];
$this->action['commonend'] = $props[$this->proptags['commonend']] = $nextOccurrence[$this->proptags['duedate']];
$this->action['startdate'] = $props[$this->proptags['startdate']] = $nextOccurrence[$this->proptags['startdate']];
$this->action['commonstart'] = $props[$this->proptags['commonstart']] = $nextOccurrence[$this->proptags['startdate']];
// If current task as been mark as 'Complete' then next occurrence should be uncomplete.
if (isset($this->action['complete']) && $this->action['complete'] == 1) {
$this->action['status'] = $props[$this->proptags["status"]] = olTaskNotStarted;
$this->action['complete'] = $props[$this->proptags["complete"]] = false;
$this->action['percent_complete'] = $props[$this->proptags["percent_complete"]] = 0;
}
$props[$this->proptags["dead_occurrence"]] = false;
} else {
if (isset($this->action['deleteOccurrence']) && $this->action['deleteOccurrence'])
return false;
// Didn't get next occurrence, probably this is the last one, so recurrence ends here
$props[$this->proptags["dead_occurrence"]] = true;
$props[$this->proptags["datecompleted"]] = $this->action['datecompleted'];
$props[$this->proptags["task_f_creator"]] = true;
//OL props
$props[$this->proptags["side_effects"]] = 1296;
$props[$this->proptags["icon_index"]] = 1280;
}
mapi_setprops($this->message, $props);
}
return $result;
}
/**
* Function which return properties of next occurrence
*@return array startdate/enddate of next occurrence
*/
function getNextOccurrence()
{
if ($this->recur) {
$items = array();
//@TODO: fix start of range
$start = isset($this->messageprops[$this->proptags["duedate"]]) ? $this->messageprops[$this->proptags["duedate"]] : $this->action['start'];
$dayend = ($this->recur['term'] == 0x23) ? 0x7fffffff : $this->dayStartOf($this->recur["end"]);
// Fix recur object
$this->recur['startocc'] = 0;
$this->recur['endocc'] = 0;
// Retrieve next occurrence
$items = $this->getItems($start, $dayend, 1);
return !empty($items) ? $items[0] : false;
}
}
/**
* Function which clones current occurrence and sets appropriate properties.
* The original recurring item is moved to next occurrence.
*@param boolean $markComplete true if existing occurrence has to be mark complete else false.
*/
function regenerateTask($markComplete)
{
// Get all properties
$taskItemProps = mapi_getprops($this->message);
if (isset($this->action["subject"])) $taskItemProps[$this->proptags["subject"]] = $this->action["subject"];
if (isset($this->action["importance"])) $taskItemProps[$this->proptags["importance"]] = $this->action["importance"];
if (isset($this->action["startdate"])) {
$taskItemProps[$this->proptags["startdate"]] = $this->action["startdate"];
$taskItemProps[$this->proptags["commonstart"]] = $this->action["startdate"];
}
if (isset($this->action["duedate"])) {
$taskItemProps[$this->proptags["duedate"]] = $this->action["duedate"];
$taskItemProps[$this->proptags["commonend"]] = $this->action["duedate"];
}
$folder = mapi_msgstore_openentry($this->store, $taskItemProps[PR_PARENT_ENTRYID]);
$newMessage = mapi_folder_createmessage($folder);
$taskItemProps[$this->proptags["status"]] = $markComplete ? olTaskComplete : olTaskNotStarted;
$taskItemProps[$this->proptags["complete"]] = $markComplete;
$taskItemProps[$this->proptags["percent_complete"]] = $markComplete ? 1 : 0;
// This occurrence has been marked as 'Complete' so disable reminder
if ($markComplete) {
$taskItemProps[$this->proptags["reset_reminder"]] = false;
$taskItemProps[$this->proptags["reminder"]] = false;
$taskItemProps[$this->proptags["datecompleted"]] = $this->action["datecompleted"];
unset($this->action[$this->proptags['datecompleted']]);
}
// Recurrence ends for this item
$taskItemProps[$this->proptags["dead_occurrence"]] = true;
$taskItemProps[$this->proptags["task_f_creator"]] = true;
//OL props
$taskItemProps[$this->proptags["side_effects"]] = 1296;
$taskItemProps[$this->proptags["icon_index"]] = 1280;
// Copy recipients
$recipienttable = mapi_message_getrecipienttable($this->message);
$recipients = mapi_table_queryallrows($recipienttable, array(PR_ENTRYID, PR_DISPLAY_NAME, PR_EMAIL_ADDRESS, PR_RECIPIENT_ENTRYID, PR_RECIPIENT_TYPE, PR_SEND_INTERNET_ENCODING, PR_SEND_RICH_INFO, PR_RECIPIENT_DISPLAY_NAME, PR_ADDRTYPE, PR_DISPLAY_TYPE, PR_RECIPIENT_TRACKSTATUS, PR_RECIPIENT_TRACKSTATUS_TIME, PR_RECIPIENT_FLAGS, PR_ROWID));
$copy_to_recipientTable = mapi_message_getrecipienttable($newMessage);
$copy_to_recipientRows = mapi_table_queryallrows($copy_to_recipientTable, array(PR_ROWID));
foreach($copy_to_recipientRows as $recipient) {
mapi_message_modifyrecipients($newMessage, MODRECIP_REMOVE, array($recipient));
}
mapi_message_modifyrecipients($newMessage, MODRECIP_ADD, $recipients);
// Copy attachments
$attachmentTable = mapi_message_getattachmenttable($this->message);
if($attachmentTable) {
$attachments = mapi_table_queryallrows($attachmentTable, array(PR_ATTACH_NUM, PR_ATTACH_SIZE, PR_ATTACH_LONG_FILENAME, PR_ATTACHMENT_HIDDEN, PR_DISPLAY_NAME, PR_ATTACH_METHOD));
foreach($attachments as $attach_props){
$attach_old = mapi_message_openattach($this->message, (int) $attach_props[PR_ATTACH_NUM]);
$attach_newResourceMsg = mapi_message_createattach($newMessage);
mapi_copyto($attach_old, array(), array(), $attach_newResourceMsg, 0);
mapi_savechanges($attach_newResourceMsg);
}
}
mapi_setprops($newMessage, $taskItemProps);
mapi_savechanges($newMessage);
// Update body of original message
$msgbody = mapi_message_openproperty($this->message, PR_BODY);
$msgbody = trim($this->windows1252_to_utf8($msgbody), "\0");
$separator = "------------\r\n";
if (!empty($msgbody) && strrpos($msgbody, $separator) === false) {
$msgbody = $separator . $msgbody;
$stream = mapi_openpropertytostream($this->message, PR_BODY, MAPI_CREATE | MAPI_MODIFY);
mapi_stream_setsize($stream, strlen($msgbody));
mapi_stream_write($stream, $msgbody);
mapi_stream_commit($stream);
}
// We need these properties to notify client
return mapi_getprops($newMessage, array(PR_ENTRYID, PR_PARENT_ENTRYID, PR_STORE_ENTRYID));
}
/**
* processOccurrenceItem, adds an item to a list of occurrences, but only if the
* resulting occurrence starts or ends in the interval <$start, $end>
* @param array $items reference to the array to be added to
* @param date $start start of timeframe in GMT TIME
* @param date $end end of timeframe in GMT TIME
* @param date $basedate (hour/sec/min assumed to be 00:00:00) in LOCAL TIME OF THE OCCURRENCE
*/
function processOccurrenceItem(&$items, $start, $end, $now)
{
if ($now > $start) {
$newItem = array();
$newItem[$this->proptags['startdate']] = $now;
// If startdate and enddate are set on task, then slide enddate according to duration
if (isset($this->messageprops[$this->proptags["startdate"]]) && isset($this->messageprops[$this->proptags["duedate"]])) {
$newItem[$this->proptags['duedate']] = $newItem[$this->proptags['startdate']] + ($this->messageprops[$this->proptags["duedate"]] - $this->messageprops[$this->proptags["startdate"]]);
} else {
$newItem[$this->proptags['duedate']] = $newItem[$this->proptags['startdate']];
}
$items[] = $newItem;
}
}
/**
* Function which marks existing occurrence to 'Complete'
*@param array $recur array action from client
*@return array of properties of regenerated task else false
*/
function markOccurrenceComplete(&$recur)
{
// Fix timezone object
$this->tz = false;
$this->action =& $recur;
$dead_occurrence = isset($this->messageprops[$this->proptags['dead_occurrence']]) ? $this->messageprops[$this->proptags['dead_occurrence']] : false;
if (!$dead_occurrence) {
return $this->moveToNextOccurrence();
}
return false;
}
/**
* Function which sets reminder on recurring task after existing occurrence has been deleted or marked complete.
*@param array $nextOccurrence properties of next occurrence
*/
function setReminder($nextOccurrence)
{
$props = array();
if ($nextOccurrence) {
// Check if reminder is reset. Default is 'false'
$reset_reminder = isset($this->messageprops[$this->proptags['reset_reminder']]) ? $this->messageprops[$this->proptags['reset_reminder']] : false;
$reminder = $this->messageprops[$this->proptags['reminder']];
// Either reminder was already set OR reminder was set but was dismissed bty user
if ($reminder || $reset_reminder) {
// Reminder can be set at any time either before or after the duedate, so get duration between the reminder time and duedate
$reminder_time = isset($this->messageprops[$this->proptags['reminder_time']]) ? $this->messageprops[$this->proptags['reminder_time']] : 0;
$reminder_difference = isset($this->messageprops[$this->proptags['duedate']]) ? $this->messageprops[$this->proptags['duedate']] : 0;
$reminder_difference = $reminder_difference - $reminder_time;
// Apply duration to next calculated duedate
$next_reminder_time = $nextOccurrence[$this->proptags['duedate']] - $reminder_difference;
$props[$this->proptags['reminder_time']] = $next_reminder_time;
$props[$this->proptags['flagdueby']] = $next_reminder_time;
$this->action['reminder'] = $props[$this->proptags['reminder']] = true;
}
} else {
// Didn't get next occurrence, probably this is the last occurrence
$props[$this->proptags['reminder']] = false;
$props[$this->proptags['reset_reminder']] = false;
}
if (!empty($props))
mapi_setprops($this->message, $props);
}
/**
* Function which recurring task to next occurrence.
* It simply doesn't regenerate task
@param array $action
*/
function deleteOccurrence($action)
{
$this->tz = false;
$this->action = $action;
$result = $this->moveToNextOccurrence();
mapi_savechanges($this->message);
return $result;
}
/**
* Convert from windows-1252 encoded string to UTF-8 string
*
* The same conversion rules as utf8_to_windows1252 apply.
*
* @param string $string the Windows-1252 string to convert
* @return string UTF-8 representation of the string
*/
function windows1252_to_utf8($string)
{
if (function_exists("iconv")){
return iconv("Windows-1252", "UTF-8//TRANSLIT", $string);
}else{
return utf8_encode($string); // no euro support here
}
}
}

File diff suppressed because it is too large Load diff

View file

@ -1,338 +0,0 @@
<?php
/*
* Copyright 2005 - 2013 Zarafa B.V.
*
* 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. 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 "Zarafa" 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 "Zarafa" or "Zarafa Server", you may only do so if you
* have a written permission by Zarafa B.V. (to acquire a permission
* please contact Zarafa at trademark@zarafa.com).
*
* The interactive user interface of the software displays an attribution
* notice containing the term "Zarafa" and/or the logo of Zarafa.
* Interactive user interfaces of unmodified and modified versions must
* display Appropriate Legal Notices according to sec. 5 of the GNU
* Affero General Public License, version 3, when you propagate
* unmodified or modified versions of the Program. In accordance with
* sec. 7 b) of the GNU Affero General Public License, version 3, these
* Appropriate Legal Notices must retain the logo of Zarafa or display
* the words "Initial Development by Zarafa" if the display of the logo
* is not reasonably feasible for technical reasons. The use of the logo
* of Zarafa in Legal Notices is allowed for unmodified and modified
* versions of the software.
*
* 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 <http://www.gnu.org/licenses/>.
*
*/
/**
* Function to make a MAPIGUID from a php string.
* The C++ definition for the GUID is:
* typedef struct _GUID
* {
* unsigned long Data1;
* unsigned short Data2;
* unsigned short Data3;
* unsigned char Data4[8];
* } GUID;
*
* A GUID is normally represented in the following form:
* {00062008-0000-0000-C000-000000000046}
*
* @param String GUID
*/
function makeGuid($guid)
{
// remove the { and } from the string and explode it into an array
$guidArray = explode('-', substr($guid, 1,strlen($guid)-2));
// convert to hex!
$data1[0] = intval(substr($guidArray[0], 0, 4),16); // we need to split the unsigned long
$data1[1] = intval(substr($guidArray[0], 4, 4),16);
$data2 = intval($guidArray[1], 16);
$data3 = intval($guidArray[2], 16);
$data4[0] = intval(substr($guidArray[3], 0, 2),16);
$data4[1] = intval(substr($guidArray[3], 2, 2),16);
for($i=0; $i < 6; $i++)
{
$data4[] = intval(substr($guidArray[4], $i*2, 2),16);
}
return pack("vvvvCCCCCCCC", $data1[1], $data1[0], $data2, $data3, $data4[0],$data4[1],$data4[2],$data4[3],$data4[4],$data4[5],$data4[6],$data4[7]);
}
/**
* Function to get a human readable string from a MAPI error code
*
*@param int $errcode the MAPI error code, if not given, we use mapi_last_hresult
*@return string The defined name for the MAPI error code
*/
function get_mapi_error_name($errcode=null)
{
if ($errcode === null){
$errcode = mapi_last_hresult();
}
if ($errcode !== 0) {
// get_defined_constants(true) is preferred, but crashes PHP
// https://bugs.php.net/bug.php?id=61156
$allConstants = get_defined_constants();
foreach ($allConstants as $key => $value) {
/**
* If PHP encounters a number beyond the bounds of the integer type,
* it will be interpreted as a float instead, so when comparing these error codes
* we have to manually typecast value to integer, so float will be converted in integer,
* but still its out of bound for integer limit so it will be auto adjusted to minus value
*/
if ($errcode == (int) $value) {
// Check that we have an actual MAPI error or warning definition
$prefix = substr($key, 0, 7);
if ($prefix == "MAPI_E_" || $prefix == "MAPI_W_") {
return $key;
}
}
}
} else {
return "NOERROR";
}
// error code not found, return hex value (this is a fix for 64-bit systems, we can't use the dechex() function for this)
$result = unpack("H*", pack("N", $errcode));
return "0x" . $result[1];
}
/**
* Parses properties from an array of strings. Each "string" may be either an ULONG, which is a direct property ID,
* or a string with format "PT_TYPE:{GUID}:StringId" or "PT_TYPE:{GUID}:0xXXXX" for named
* properties.
*
* @returns array of properties
*/
function getPropIdsFromStrings($store, $mapping)
{
$props = array();
$ids = array("name"=>array(), "id"=>array(), "guid"=>array(), "type"=>array()); // this array stores all the information needed to retrieve a named property
$num = 0;
// caching
$guids = array();
foreach($mapping as $name=>$val){
if(is_string($val)) {
$split = explode(":", $val);
if(count($split) != 3){ // invalid string, ignore
trigger_error(sprintf("Invalid property: %s \"%s\"",$name,$val), E_USER_NOTICE);
continue;
}
if(substr($split[2], 0, 2) == "0x") {
$id = hexdec(substr($split[2], 2));
} else {
$id = $split[2];
}
// have we used this guid before?
if (!defined($split[1])){
if (!array_key_exists($split[1], $guids)){
$guids[$split[1]] = makeguid($split[1]);
}
$guid = $guids[$split[1]];
}else{
$guid = constant($split[1]);
}
// temp store info about named prop, so we have to call mapi_getidsfromnames just one time
$ids["name"][$num] = $name;
$ids["id"][$num] = $id;
$ids["guid"][$num] = $guid;
$ids["type"][$num] = $split[0];
$num++;
}else{
// not a named property
$props[$name] = $val;
}
}
if (empty($ids["id"])){
return $props;
}
// get the ids
$named = mapi_getidsfromnames($store, $ids["id"], $ids["guid"]);
foreach($named as $num=>$prop){
$props[$ids["name"][$num]] = mapi_prop_tag(constant($ids["type"][$num]), mapi_prop_id($prop));
}
return $props;
}
/**
* Check wether a call to mapi_getprops returned errors for some properties.
* mapi_getprops function tries to get values of properties requested but somehow if
* if a property value can not be fetched then it changes type of property tag as PT_ERROR
* and returns error for that particular property, probable errors
* that can be returned as value can be MAPI_E_NOT_FOUND, MAPI_E_NOT_ENOUGH_MEMORY
*
* @param long $property Property to check for error
* @param Array $propArray An array of properties
* @return mixed Gives back false when there is no error, if there is, gives the error
*/
function propIsError($property, $propArray)
{
if (array_key_exists(mapi_prop_tag(PT_ERROR, mapi_prop_id($property)), $propArray)) {
return $propArray[mapi_prop_tag(PT_ERROR, mapi_prop_id($property))];
} else {
return false;
}
}
/******** Macro Functions for PR_DISPLAY_TYPE_EX values *********/
/**
* check addressbook object is a remote mailuser
*/
function DTE_IS_REMOTE_VALID($value) {
return !!($value & DTE_FLAG_REMOTE_VALID);
}
/**
* check addressbook object is able to receive permissions
*/
function DTE_IS_ACL_CAPABLE($value) {
return !!($value & DTE_FLAG_ACL_CAPABLE);
}
function DTE_REMOTE($value) {
return (($value & DTE_MASK_REMOTE) >> 8);
}
function DTE_LOCAL($value) {
return ($value & DTE_MASK_LOCAL);
}
/**
* Note: Static function, more like a utility function.
*
* Gets all the items (including recurring items) in the specified calendar in the given timeframe. Items are
* included as a whole if they overlap the interval <$start, $end> (non-inclusive). This means that if the interval
* is <08:00 - 14:00>, the item [6:00 - 8:00> is NOT included, nor is the item [14:00 - 16:00>. However, the item
* [7:00 - 9:00> is included as a whole, and is NOT capped to [8:00 - 9:00>.
*
* @param $store resource The store in which the calendar resides
* @param $calendar resource The calendar to get the items from
* @param $viewstart int Timestamp of beginning of view window
* @param $viewend int Timestamp of end of view window
* @param $propsrequested array Array of properties to return
* @param $rows array Array of rowdata as if they were returned directly from mapi_table_queryrows. Each recurring item is
* expanded so that it seems that there are only many single appointments in the table.
*/
function getCalendarItems($store, $calendar, $viewstart, $viewend, $propsrequested){
$result = array();
$properties = getPropIdsFromStrings($store, Array( "duedate" => "PT_SYSTIME:PSETID_Appointment:0x820e",
"startdate" => "PT_SYSTIME:PSETID_Appointment:0x820d",
"enddate_recurring" => "PT_SYSTIME:PSETID_Appointment:0x8236",
"recurring" => "PT_BOOLEAN:PSETID_Appointment:0x8223",
"recurring_data" => "PT_BINARY:PSETID_Appointment:0x8216",
"timezone_data" => "PT_BINARY:PSETID_Appointment:0x8233",
"label" => "PT_LONG:PSETID_Appointment:0x8214"
));
// Create a restriction that will discard rows of appointments that are definitely not in our
// requested time frame
$table = mapi_folder_getcontentstable($calendar);
$restriction =
// OR
Array(RES_OR,
Array(
Array(RES_AND, // Normal items: itemEnd must be after viewStart, itemStart must be before viewEnd
Array(
Array(RES_PROPERTY,
Array(RELOP => RELOP_GT,
ULPROPTAG => $properties["duedate"],
VALUE => $viewstart
)
),
Array(RES_PROPERTY,
Array(RELOP => RELOP_LT,
ULPROPTAG => $properties["startdate"],
VALUE => $viewend
)
)
)
),
// OR
Array(RES_PROPERTY,
Array(RELOP => RELOP_EQ,
ULPROPTAG => $properties["recurring"],
VALUE => true
)
)
) // EXISTS OR
); // global OR
// Get requested properties, plus whatever we need
$proplist = array(PR_ENTRYID, $properties["recurring"], $properties["recurring_data"], $properties["timezone_data"]);
$proplist = array_merge($proplist, $propsrequested);
$propslist = array_unique($proplist);
$rows = mapi_table_queryallrows($table, $proplist, $restriction);
// $rows now contains all the items that MAY be in the window; a recurring item needs expansion before including in the output.
foreach($rows as $row) {
$items = array();
if(isset($row[$properties["recurring"]]) && $row[$properties["recurring"]]) {
// Recurring item
$rec = new Recurrence($store, $row);
// GetItems guarantees that the item overlaps the interval <$viewstart, $viewend>
$occurrences = $rec->getItems($viewstart, $viewend);
foreach($occurrences as $occurrence) {
// The occurrence takes all properties from the main row, but overrides some properties (like start and end obviously)
$item = $occurrence + $row;
array_push($items, $item);
}
} else {
// Normal item, it matched the search criteria and therefore overlaps the interval <$viewstart, $viewend>
array_push($items, $row);
}
$result = array_merge($result,$items);
}
// All items are guaranteed to overlap the interval <$viewstart, $viewend>. Note that we may be returning a few extra
// properties that the caller did not request (recurring, etc). This shouldn't be a problem though.
return $result;
}

View file

@ -1,246 +0,0 @@
<?php
/*
* Copyright 2005 - 2013 Zarafa B.V.
*
* 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. 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 "Zarafa" 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 "Zarafa" or "Zarafa Server", you may only do so if you
* have a written permission by Zarafa B.V. (to acquire a permission
* please contact Zarafa at trademark@zarafa.com).
*
* The interactive user interface of the software displays an attribution
* notice containing the term "Zarafa" and/or the logo of Zarafa.
* Interactive user interfaces of unmodified and modified versions must
* display Appropriate Legal Notices according to sec. 5 of the GNU
* Affero General Public License, version 3, when you propagate
* unmodified or modified versions of the Program. In accordance with
* sec. 7 b) of the GNU Affero General Public License, version 3, these
* Appropriate Legal Notices must retain the logo of Zarafa or display
* the words "Initial Development by Zarafa" if the display of the logo
* is not reasonably feasible for technical reasons. The use of the logo
* of Zarafa in Legal Notices is allowed for unmodified and modified
* versions of the software.
*
* 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 <http://www.gnu.org/licenses/>.
*
*/
/**
* Status codes returned by MAPI functions
*
*
*/
/* From winerror.h */
//
// Success codes
//
define('S_OK', 0x00000000);
define('S_FALSE', 0x00000001);
define('SEVERITY_ERROR', 1);
/* from winerror.h */
/**
* Function to make a error
*/
function make_mapi_e($code)
{
return (int) mapi_make_scode(1, $code);
}
/**
* Function to make an warning
*/
function make_mapi_s($code)
{
return (int) mapi_make_scode(0, $code);
}
/* From mapicode.h */
/*
* On Windows NT 3.5 and Windows 95, scodes are 32-bit values
* laid out as follows:
*
* 3 3 2 2 2 2 2 2 2 2 2 2 1 1 1 1 1 1 1 1 1 1
* 1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0
* +-+-+-+-+-+---------------------+-------------------------------+
* |S|R|C|N|r| Facility | Code |
* +-+-+-+-+-+---------------------+-------------------------------+
*
* where
*
* S - Severity - indicates success/fail
*
* 0 - Success
* 1 - Fail (COERROR)
*
* R - reserved portion of the facility code, corresponds to NT's
* second severity bit.
*
* C - reserved portion of the facility code, corresponds to NT's
* C field.
*
* N - reserved portion of the facility code. Used to indicate a
* mapped NT status value.
*
* r - reserved portion of the facility code. Reserved for internal
* use. Used to indicate HRESULT values that are not status
* values, but are instead message ids for display strings.
*
* Facility - is the facility code
* FACILITY_NULL 0x0
* FACILITY_RPC 0x1
* FACILITY_DISPATCH 0x2
* FACILITY_STORAGE 0x3
* FACILITY_ITF 0x4
* FACILITY_WIN32 0x7
* FACILITY_WINDOWS 0x8
*
* Code - is the facility's status code
*
*/
define('NOERROR' ,0);
define('MAPI_E_CALL_FAILED' ,(int) 0x80004005);
define('MAPI_E_NOT_ENOUGH_MEMORY' ,(int) 0x8007000E);
define('MAPI_E_INVALID_PARAMETER' ,(int) 0x80070057);
define('MAPI_E_INTERFACE_NOT_SUPPORTED' ,(int) 0x80004002);
define('MAPI_E_NO_ACCESS' ,(int) 0x80070005);
define('MAPI_E_NO_SUPPORT' ,make_mapi_e(0x102));
define('MAPI_E_BAD_CHARWIDTH' ,make_mapi_e(0x103));
define('MAPI_E_STRING_TOO_LONG' ,make_mapi_e(0x105));
define('MAPI_E_UNKNOWN_FLAGS' ,make_mapi_e(0x106));
define('MAPI_E_INVALID_ENTRYID' ,make_mapi_e(0x107));
define('MAPI_E_INVALID_OBJECT' ,make_mapi_e(0x108));
define('MAPI_E_OBJECT_CHANGED' ,make_mapi_e(0x109));
define('MAPI_E_OBJECT_DELETED' ,make_mapi_e(0x10A));
define('MAPI_E_BUSY' ,make_mapi_e(0x10B));
define('MAPI_E_NOT_ENOUGH_DISK' ,make_mapi_e(0x10D));
define('MAPI_E_NOT_ENOUGH_RESOURCES' ,make_mapi_e(0x10E));
define('MAPI_E_NOT_FOUND' ,make_mapi_e(0x10F));
define('MAPI_E_VERSION' ,make_mapi_e(0x110));
define('MAPI_E_LOGON_FAILED' ,make_mapi_e(0x111));
define('MAPI_E_SESSION_LIMIT' ,make_mapi_e(0x112));
define('MAPI_E_USER_CANCEL' ,make_mapi_e(0x113));
define('MAPI_E_UNABLE_TO_ABORT' ,make_mapi_e(0x114));
define('MAPI_E_NETWORK_ERROR' ,make_mapi_e(0x115));
define('MAPI_E_DISK_ERROR' ,make_mapi_e(0x116));
define('MAPI_E_TOO_COMPLEX' ,make_mapi_e(0x117));
define('MAPI_E_BAD_COLUMN' ,make_mapi_e(0x118));
define('MAPI_E_EXTENDED_ERROR' ,make_mapi_e(0x119));
define('MAPI_E_COMPUTED' ,make_mapi_e(0x11A));
define('MAPI_E_CORRUPT_DATA' ,make_mapi_e(0x11B));
define('MAPI_E_UNCONFIGURED' ,make_mapi_e(0x11C));
define('MAPI_E_FAILONEPROVIDER' ,make_mapi_e(0x11D));
define('MAPI_E_UNKNOWN_CPID' ,make_mapi_e(0x11E));
define('MAPI_E_UNKNOWN_LCID' ,make_mapi_e(0x11F));
/* Flavors of E_ACCESSDENIED, used at logon */
define('MAPI_E_PASSWORD_CHANGE_REQUIRED' ,make_mapi_e(0x120));
define('MAPI_E_PASSWORD_EXPIRED' ,make_mapi_e(0x121));
define('MAPI_E_INVALID_WORKSTATION_ACCOUNT' ,make_mapi_e(0x122));
define('MAPI_E_INVALID_ACCESS_TIME' ,make_mapi_e(0x123));
define('MAPI_E_ACCOUNT_DISABLED' ,make_mapi_e(0x124));
/* MAPI base function and status object specific errors and warnings */
define('MAPI_E_END_OF_SESSION' ,make_mapi_e(0x200));
define('MAPI_E_UNKNOWN_ENTRYID' ,make_mapi_e(0x201));
define('MAPI_E_MISSING_REQUIRED_COLUMN' ,make_mapi_e(0x202));
define('MAPI_W_NO_SERVICE' ,make_mapi_s(0x203));
/* Property specific errors and warnings */
define('MAPI_E_BAD_VALUE' ,make_mapi_e(0x301));
define('MAPI_E_INVALID_TYPE' ,make_mapi_e(0x302));
define('MAPI_E_TYPE_NO_SUPPORT' ,make_mapi_e(0x303));
define('MAPI_E_UNEXPECTED_TYPE' ,make_mapi_e(0x304));
define('MAPI_E_TOO_BIG' ,make_mapi_e(0x305));
define('MAPI_E_DECLINE_COPY' ,make_mapi_e(0x306));
define('MAPI_E_UNEXPECTED_ID' ,make_mapi_e(0x307));
define('MAPI_W_ERRORS_RETURNED' ,make_mapi_s(0x380));
/* Table specific errors and warnings */
define('MAPI_E_UNABLE_TO_COMPLETE' ,make_mapi_e(0x400));
define('MAPI_E_TIMEOUT' ,make_mapi_e(0x401));
define('MAPI_E_TABLE_EMPTY' ,make_mapi_e(0x402));
define('MAPI_E_TABLE_TOO_BIG' ,make_mapi_e(0x403));
define('MAPI_E_INVALID_BOOKMARK' ,make_mapi_e(0x405));
define('MAPI_W_POSITION_CHANGED' ,make_mapi_s(0x481));
define('MAPI_W_APPROX_COUNT' ,make_mapi_s(0x482));
/* Transport specific errors and warnings */
define('MAPI_E_WAIT' ,make_mapi_e(0x500));
define('MAPI_E_CANCEL' ,make_mapi_e(0x501));
define('MAPI_E_NOT_ME' ,make_mapi_e(0x502));
define('MAPI_W_CANCEL_MESSAGE' ,make_mapi_s(0x580));
/* Message Store, Folder, and Message specific errors and warnings */
define('MAPI_E_CORRUPT_STORE' ,make_mapi_e(0x600));
define('MAPI_E_NOT_IN_QUEUE' ,make_mapi_e(0x601));
define('MAPI_E_NO_SUPPRESS' ,make_mapi_e(0x602));
define('MAPI_E_COLLISION' ,make_mapi_e(0x604));
define('MAPI_E_NOT_INITIALIZED' ,make_mapi_e(0x605));
define('MAPI_E_NON_STANDARD' ,make_mapi_e(0x606));
define('MAPI_E_NO_RECIPIENTS' ,make_mapi_e(0x607));
define('MAPI_E_SUBMITTED' ,make_mapi_e(0x608));
define('MAPI_E_HAS_FOLDERS' ,make_mapi_e(0x609));
define('MAPI_E_HAS_MESSAGES' ,make_mapi_e(0x60A));
define('MAPI_E_FOLDER_CYCLE' ,make_mapi_e(0x60B));
define('MAPI_E_STORE_FULL' ,make_mapi_e(0x60C));
define('MAPI_W_PARTIAL_COMPLETION' ,make_mapi_s(0x680));
/* Address Book specific errors and warnings */
define('MAPI_E_AMBIGUOUS_RECIP' ,make_mapi_e(0x700));
/* ICS errors and warnings */
define('SYNC_E_UNKNOWN_FLAGS', MAPI_E_UNKNOWN_FLAGS);
define('SYNC_E_INVALID_PARAMETER', MAPI_E_INVALID_PARAMETER);
define('SYNC_E_ERROR', MAPI_E_CALL_FAILED);
define('SYNC_E_OBJECT_DELETED', make_mapi_e(0x800));
define('SYNC_E_IGNORE', make_mapi_e(0x801));
define('SYNC_E_CONFLICT', make_mapi_e(0x802));
define('SYNC_E_NO_PARENT', make_mapi_e(0x803));
define('SYNC_E_INCEST', make_mapi_e(0x804));
define('SYNC_E_UNSYNCHRONIZED', make_mapi_e(0x805));
define('SYNC_W_PROGRESS', make_mapi_s(0x820));
define('SYNC_W_CLIENT_CHANGE_NEWER', make_mapi_s(0x821));

View file

@ -1,666 +0,0 @@
<?php
/*
* Copyright 2005 - 2013 Zarafa B.V.
*
* 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. 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 "Zarafa" 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 "Zarafa" or "Zarafa Server", you may only do so if you
* have a written permission by Zarafa B.V. (to acquire a permission
* please contact Zarafa at trademark@zarafa.com).
*
* The interactive user interface of the software displays an attribution
* notice containing the term "Zarafa" and/or the logo of Zarafa.
* Interactive user interfaces of unmodified and modified versions must
* display Appropriate Legal Notices according to sec. 5 of the GNU
* Affero General Public License, version 3, when you propagate
* unmodified or modified versions of the Program. In accordance with
* sec. 7 b) of the GNU Affero General Public License, version 3, these
* Appropriate Legal Notices must retain the logo of Zarafa or display
* the words "Initial Development by Zarafa" if the display of the logo
* is not reasonably feasible for technical reasons. The use of the logo
* of Zarafa in Legal Notices is allowed for unmodified and modified
* versions of the software.
*
* 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 <http://www.gnu.org/licenses/>.
*
*/
/* Resource types as defined in main.h of the mapi extension */
define('RESOURCE_SESSION' ,'MAPI Session');
define('RESOURCE_TABLE' ,'MAPI Table');
define('RESOURCE_ROWSET' ,'MAPI Rowset');
define('RESOURCE_MSGSTORE' ,'MAPI Message Store');
define('RESOURCE_FOLDER' ,'MAPI Folder');
define('RESOURCE_MESSAGE' ,'MAPI Message');
define('RESOURCE_ATTACHMENT' ,'MAPI Attachment');
/* Object type */
define('MAPI_STORE' ,0x00000001); /* Message Store */
define('MAPI_ADDRBOOK' ,0x00000002); /* Address Book */
define('MAPI_FOLDER' ,0x00000003); /* Folder */
define('MAPI_ABCONT' ,0x00000004); /* Address Book Container */
define('MAPI_MESSAGE' ,0x00000005); /* Message */
define('MAPI_MAILUSER' ,0x00000006); /* Individual Recipient */
define('MAPI_ATTACH' ,0x00000007); /* Attachment */
define('MAPI_DISTLIST' ,0x00000008); /* Distribution List Recipient */
define('MAPI_PROFSECT' ,0x00000009); /* Profile Section */
define('MAPI_STATUS' ,0x0000000A); /* Status Object */
define('MAPI_SESSION' ,0x0000000B); /* Session */
define('MAPI_FORMINFO' ,0x0000000C); /* Form Information */
define('MV_FLAG' ,0x1000);
define('MV_INSTANCE' ,0x2000);
define('MVI_FLAG' ,MV_FLAG | MV_INSTANCE);
define('PT_UNSPECIFIED' , 0); /* (Reserved for interface use) type doesn't matter to caller */
define('PT_NULL' , 1); /* NULL property value */
define('PT_I2' , 2); /* Signed 16-bit value */
define('PT_LONG' , 3); /* Signed 32-bit value */
define('PT_R4' , 4); /* 4-byte floating point */
define('PT_DOUBLE' , 5); /* Floating point double */
define('PT_CURRENCY' , 6); /* Signed 64-bit int (decimal w/ 4 digits right of decimal pt) */
define('PT_APPTIME' , 7); /* Application time */
define('PT_ERROR' , 10); /* 32-bit error value */
define('PT_BOOLEAN' , 11); /* 16-bit boolean (non-zero true) */
define('PT_OBJECT' , 13); /* Embedded object in a property */
define('PT_I8' , 20); /* 8-byte signed integer */
define('PT_STRING8' , 30); /* Null terminated 8-bit character string */
define('PT_UNICODE' , 31); /* Null terminated Unicode string */
define('PT_SYSTIME' , 64); /* FILETIME 64-bit int w/ number of 100ns periods since Jan 1,1601 */
define('PT_CLSID' , 72); /* OLE GUID */
define('PT_BINARY' ,258); /* Uninterpreted (counted byte array) */
/* Changes are likely to these numbers, and to their structures. */
/* Alternate property type names for ease of use */
define('PT_SHORT' ,PT_I2);
define('PT_I4' ,PT_LONG);
define('PT_FLOAT' ,PT_R4);
define('PT_R8' ,PT_DOUBLE);
define('PT_LONGLONG' ,PT_I8);
define('PT_TSTRING' ,PT_STRING8);
define('PT_MV_I2' ,(MV_FLAG | PT_I2));
define('PT_MV_LONG' ,(MV_FLAG | PT_LONG));
define('PT_MV_R4' ,(MV_FLAG | PT_R4));
define('PT_MV_DOUBLE' ,(MV_FLAG | PT_DOUBLE));
define('PT_MV_CURRENCY' ,(MV_FLAG | PT_CURRENCY));
define('PT_MV_APPTIME' ,(MV_FLAG | PT_APPTIME));
define('PT_MV_SYSTIME' ,(MV_FLAG | PT_SYSTIME));
define('PT_MV_STRING8' ,(MV_FLAG | PT_STRING8));
define('PT_MV_BINARY' ,(MV_FLAG | PT_BINARY));
define('PT_MV_UNICODE' ,(MV_FLAG | PT_UNICODE));
define('PT_MV_CLSID' ,(MV_FLAG | PT_CLSID));
define('PT_MV_I8' ,(MV_FLAG | PT_I8));
define('PT_MV_TSTRING' ,PT_MV_STRING8);
/* bit 0: set if descending, clear if ascending */
define('TABLE_SORT_ASCEND' ,(0x00000000));
define('TABLE_SORT_DESCEND' ,(0x00000001));
define('TABLE_SORT_COMBINE' ,(0x00000002));
define('MAPI_UNICODE' ,0x80000000);
/* IMAPIFolder Interface --------------------------------------------------- */
define('CONVENIENT_DEPTH' ,0x00000001);
define('SEARCH_RUNNING' ,0x00000001);
define('SEARCH_REBUILD' ,0x00000002);
define('SEARCH_RECURSIVE' ,0x00000004);
define('SEARCH_FOREGROUND' ,0x00000008);
define('STOP_SEARCH' ,0x00000001);
define('RESTART_SEARCH' ,0x00000002);
define('RECURSIVE_SEARCH' ,0x00000004);
define('SHALLOW_SEARCH' ,0x00000008);
define('FOREGROUND_SEARCH' ,0x00000010);
define('BACKGROUND_SEARCH' ,0x00000020);
/* IMAPIFolder folder type (enum) */
define('FOLDER_ROOT' ,0x00000000);
define('FOLDER_GENERIC' ,0x00000001);
define('FOLDER_SEARCH' ,0x00000002);
/* CreateMessage */
/****** MAPI_DEFERRED_ERRORS ((ULONG) 0x00000008) below */
/****** MAPI_ASSOCIATED ((ULONG) 0x00000040) below */
/* CopyMessages */
define('MESSAGE_MOVE' ,0x00000001);
define('MESSAGE_DIALOG' ,0x00000002);
/****** MAPI_DECLINE_OK ((ULONG) 0x00000004) above */
/* CreateFolder */
define('OPEN_IF_EXISTS' ,0x00000001);
/****** MAPI_DEFERRED_ERRORS ((ULONG) 0x00000008) below */
/****** MAPI_UNICODE ((ULONG) 0x80000000) above */
/* DeleteFolder */
define('DEL_MESSAGES' ,0x00000001);
define('FOLDER_DIALOG' ,0x00000002);
define('DEL_FOLDERS' ,0x00000004);
/* EmptyFolder */
define('DEL_ASSOCIATED' ,0x00000008);
/* CopyFolder */
define('FOLDER_MOVE' ,0x00000001);
/****** FOLDER_DIALOG ((ULONG) 0x00000002) above */
/****** MAPI_DECLINE_OK ((ULONG) 0x00000004) above */
define('COPY_SUBFOLDERS' ,0x00000010);
/****** MAPI_UNICODE ((ULONG) 0x80000000) above */
/* SetReadFlags */
define('SUPPRESS_RECEIPT' ,0x00000001);
/****** FOLDER_DIALOG ((ULONG) 0x00000002) above */
define('CLEAR_READ_FLAG' ,0x00000004);
/****** MAPI_DEFERRED_ERRORS ((ULONG) 0x00000008) below */
define('GENERATE_RECEIPT_ONLY' ,0x00000010);
define('CLEAR_RN_PENDING' ,0x00000020);
define('CLEAR_NRN_PENDING' ,0x00000040);
/* Flags defined in PR_MESSAGE_FLAGS */
define('MSGFLAG_READ' ,0x00000001);
define('MSGFLAG_UNMODIFIED' ,0x00000002);
define('MSGFLAG_SUBMIT' ,0x00000004);
define('MSGFLAG_UNSENT' ,0x00000008);
define('MSGFLAG_HASATTACH' ,0x00000010);
define('MSGFLAG_FROMME' ,0x00000020);
define('MSGFLAG_ASSOCIATED' ,0x00000040);
define('MSGFLAG_RESEND' ,0x00000080);
define('MSGFLAG_RN_PENDING' ,0x00000100);
define('MSGFLAG_NRN_PENDING' ,0x00000200);
/* GetMessageStatus */
define('MSGSTATUS_HIGHLIGHTED' ,0x00000001);
define('MSGSTATUS_TAGGED' ,0x00000002);
define('MSGSTATUS_HIDDEN' ,0x00000004);
define('MSGSTATUS_DELMARKED' ,0x00000008);
/* Bits for remote message status */
define('MSGSTATUS_REMOTE_DOWNLOAD' ,0x00001000);
define('MSGSTATUS_REMOTE_DELETE' ,0x00002000);
/* SaveContentsSort */
define('RECURSIVE_SORT' ,0x00000002);
/* PR_STATUS property */
define('FLDSTATUS_HIGHLIGHTED' ,0x00000001);
define('FLDSTATUS_TAGGED' ,0x00000002);
define('FLDSTATUS_HIDDEN' ,0x00000004);
define('FLDSTATUS_DELMARKED' ,0x00000008);
/* IMAPIStatus Interface --------------------------------------------------- */
/* Values for PR_RESOURCE_TYPE, _METHODS, _FLAGS */
define('MAPI_STORE_PROVIDER' , 33); /* Message Store */
define('MAPI_AB' , 34); /* Address Book */
define('MAPI_AB_PROVIDER' , 35); /* Address Book Provider */
define('MAPI_TRANSPORT_PROVIDER' , 36); /* Transport Provider */
define('MAPI_SPOOLER' , 37); /* Message Spooler */
define('MAPI_PROFILE_PROVIDER' , 38); /* Profile Provider */
define('MAPI_SUBSYSTEM' , 39); /* Overall Subsystem Status */
define('MAPI_HOOK_PROVIDER' , 40); /* Spooler Hook */
define('STATUS_VALIDATE_STATE' ,0x00000001);
define('STATUS_SETTINGS_DIALOG' ,0x00000002);
define('STATUS_CHANGE_PASSWORD' ,0x00000004);
define('STATUS_FLUSH_QUEUES' ,0x00000008);
define('STATUS_DEFAULT_OUTBOUND' ,0x00000001);
define('STATUS_DEFAULT_STORE' ,0x00000002);
define('STATUS_PRIMARY_IDENTITY' ,0x00000004);
define('STATUS_SIMPLE_STORE' ,0x00000008);
define('STATUS_XP_PREFER_LAST' ,0x00000010);
define('STATUS_NO_PRIMARY_IDENTITY' ,0x00000020);
define('STATUS_NO_DEFAULT_STORE' ,0x00000040);
define('STATUS_TEMP_SECTION' ,0x00000080);
define('STATUS_OWN_STORE' ,0x00000100);
define('STATUS_NEED_IPM_TREE' ,0x00000800);
define('STATUS_PRIMARY_STORE' ,0x00001000);
define('STATUS_SECONDARY_STORE' ,0x00002000);
/* ------------ */
/* Random flags */
/* Flag for deferred error */
define('MAPI_DEFERRED_ERRORS' ,0x00000008);
/* Flag for creating and using Folder Associated Information Messages */
define('MAPI_ASSOCIATED' ,0x00000040);
/* Flags for OpenMessageStore() */
define('MDB_NO_DIALOG' ,0x00000001);
define('MDB_WRITE' ,0x00000004);
/****** MAPI_DEFERRED_ERRORS ((ULONG) 0x00000008) above */
/****** MAPI_BEST_ACCESS ((ULONG) 0x00000010) above */
define('MDB_TEMPORARY' ,0x00000020);
define('MDB_NO_MAIL' ,0x00000080);
/* Flags for OpenAddressBook */
define('AB_NO_DIALOG' ,0x00000001);
/* ((ULONG) 0x00000001 is not a valid flag on ModifyRecipients. */
define('MODRECIP_ADD' ,0x00000002);
define('MODRECIP_MODIFY' ,0x00000004);
define('MODRECIP_REMOVE' ,0x00000008);
define('MAPI_ORIG' ,0); /* Recipient is message originator */
define('MAPI_TO' ,1); /* Recipient is a primary recipient */
define('MAPI_CC' ,2); /* Recipient is a copy recipient */
define('MAPI_BCC' ,3); /* Recipient is blind copy recipient */
/* IAttach Interface ------------------------------------------------------- */
/* IAttach attachment methods: PR_ATTACH_METHOD values */
define('NO_ATTACHMENT' ,0x00000000);
define('ATTACH_BY_VALUE' ,0x00000001);
define('ATTACH_BY_REFERENCE' ,0x00000002);
define('ATTACH_BY_REF_RESOLVE' ,0x00000003);
define('ATTACH_BY_REF_ONLY' ,0x00000004);
define('ATTACH_EMBEDDED_MSG' ,0x00000005);
define('ATTACH_OLE' ,0x00000006);
/* OpenProperty - ulFlags */
define('MAPI_MODIFY' ,0x00000001);
define('MAPI_CREATE' ,0x00000002);
define('STREAM_APPEND' ,0x00000004);
/****** MAPI_DEFERRED_ERRORS ((ULONG) 0x00000008) below */
/* PR_PRIORITY values */
define('PRIO_URGENT' , 1);
define('PRIO_NORMAL' , 0);
define('PRIO_NONURGENT' ,-1);
/* PR_SENSITIVITY values */
define('SENSITIVITY_NONE' ,0x00000000);
define('SENSITIVITY_PERSONAL' ,0x00000001);
define('SENSITIVITY_PRIVATE' ,0x00000002);
define('SENSITIVITY_COMPANY_CONFIDENTIAL' ,0x00000003);
/* PR_IMPORTANCE values */
define('IMPORTANCE_LOW' ,0);
define('IMPORTANCE_NORMAL' ,1);
define('IMPORTANCE_HIGH' ,2);
/* Stream interace values */
define('STREAM_SEEK_SET' ,0);
define('STREAM_SEEK_CUR' ,1);
define('STREAM_SEEK_END' ,2);
define('SHOW_SOFT_DELETES' ,0x00000002);
define('DELETE_HARD_DELETE' ,0x00000010);
/*
* The following flags are used to indicate to the client what access
* level is permissible in the object. They appear in PR_ACCESS in
* message and folder objects as well as in contents and associated
* contents tables
*/
define('MAPI_ACCESS_MODIFY' ,0x00000001);
define('MAPI_ACCESS_READ' ,0x00000002);
define('MAPI_ACCESS_DELETE' ,0x00000004);
define('MAPI_ACCESS_CREATE_HIERARCHY' ,0x00000008);
define('MAPI_ACCESS_CREATE_CONTENTS' ,0x00000010);
define('MAPI_ACCESS_CREATE_ASSOCIATED' ,0x00000020);
define('MAPI_SEND_NO_RICH_INFO' ,0x00010000);
/* flags for PR_STORE_SUPPORT_MASK */
define('STORE_ANSI_OK' ,0x00020000); // The message store supports properties containing ANSI (8-bit) characters.
define('STORE_ATTACH_OK' ,0x00000020); // The message store supports attachments (OLE or non-OLE) to messages.
define('STORE_CATEGORIZE_OK' ,0x00000400); // The message store supports categorized views of tables.
define('STORE_CREATE_OK' ,0x00000010); // The message store supports creation of new messages.
define('STORE_ENTRYID_UNIQUE' ,0x00000001); // Entry identifiers for the objects in the message store are unique, that is, never reused during the life of the store.
define('STORE_HTML_OK' ,0x00010000); // The message store supports Hypertext Markup Language (HTML) messages, stored in the PR_BODY_HTML property. Note that STORE_HTML_OK is not defined in versions of MAPIDEFS.H included with Microsoft<66> Exchange 2000 Server and earlier. If your development environment uses a MAPIDEFS.H file that does not include STORE_HTML_OK, use the value 0x00010000 instead.
define('STORE_LOCALSTORE' ,0x00080000); // This flag is reserved and should not be used.
define('STORE_MODIFY_OK' ,0x00000008); // The message store supports modification of its existing messages.
define('STORE_MV_PROPS_OK' ,0x00000200); // The message store supports multivalued properties, guarantees the stability of value order in a multivalued property throughout a save operation, and supports instantiation of multivalued properties in tables.
define('STORE_NOTIFY_OK' ,0x00000100); // The message store supports notifications.
define('STORE_OLE_OK' ,0x00000040); // The message store supports OLE attachments. The OLE data is accessible through an IStorage interface, such as that available through the PR_ATTACH_DATA_OBJ property.
define('STORE_PUBLIC_FOLDERS' ,0x00004000); // The folders in this store are public (multi-user), not private (possibly multi-instance but not multi-user).
define('STORE_READONLY' ,0x00000002); // All interfaces for the message store have a read-only access level.
define('STORE_RESTRICTION_OK' ,0x00001000); // The message store supports restrictions.
define('STORE_RTF_OK' ,0x00000800); // The message store supports Rich Text Format (RTF) messages, usually stored compressed, and the store itself keeps PR_BODY and PR_RTF_COMPRESSED synchronized.
define('STORE_SEARCH_OK' ,0x00000004); // The message store supports search-results folders.
define('STORE_SORT_OK' ,0x00002000); // The message store supports sorting views of tables.
define('STORE_SUBMIT_OK' ,0x00000080); // The message store supports marking a message for submission.
define('STORE_UNCOMPRESSED_RTF' ,0x00008000); // The message store supports storage of Rich Text Format (RTF) messages in uncompressed form. An uncompressed RTF stream is identified by the value dwMagicUncompressedRTF in the stream header. The dwMagicUncompressedRTF value is defined in the RTFLIB.H file.
define('STORE_UNICODE_OK' ,0x00040000); // The message store supports properties containing Unicode characters.
/* PR_DISPLAY_TYPEs */
/* For address book contents tables */
define('DT_MAILUSER' ,0x00000000);
define('DT_DISTLIST' ,0x00000001);
define('DT_FORUM' ,0x00000002);
define('DT_AGENT' ,0x00000003);
define('DT_ORGANIZATION' ,0x00000004);
define('DT_PRIVATE_DISTLIST' ,0x00000005);
define('DT_REMOTE_MAILUSER' ,0x00000006);
/* For address book hierarchy tables */
define('DT_MODIFIABLE' ,0x00010000);
define('DT_GLOBAL' ,0x00020000);
define('DT_LOCAL' ,0x00030000);
define('DT_WAN' ,0x00040000);
define('DT_NOT_SPECIFIC' ,0x00050000);
/* For folder hierarchy tables */
define('DT_FOLDER' ,0x01000000);
define('DT_FOLDER_LINK' ,0x02000000);
define('DT_FOLDER_SPECIAL' ,0x04000000);
/* PR_DISPLAY_TYPE_EX values */
define('DT_ROOM' ,0x00000007);
define('DT_EQUIPMENT' ,0x00000008);
define('DT_SEC_DISTLIST' ,0x00000009);
/* PR_DISPLAY_TYPE_EX flags */
define('DTE_FLAG_REMOTE_VALID' ,0x80000000);
define('DTE_FLAG_ACL_CAPABLE' ,0x40000000); /* on for DT_MAILUSER and DT_SEC_DISTLIST */
define('DTE_MASK_REMOTE' ,0x0000FF00);
define('DTE_MASK_LOCAL' ,0x000000FF);
/* OlResponseStatus */
define('olResponseNone' ,0);
define('olResponseOrganized' ,1);
define('olResponseTentative' ,2);
define('olResponseAccepted' ,3);
define('olResponseDeclined' ,4);
define('olResponseNotResponded' ,5);
/* OlRecipientTrackStatus to set PR_RECIPIENT_TRACKSTATUS in recipient table
* Value of the recipient trackstatus are same as OlResponseStatus but
* recipient trackstatus doesn't have olResponseOrganized and olResponseNotResponded
* and olResponseNone has different interpretation with PR_RECIPIENT_TRACKSTATUS
* so to avoid confusions we have defined new constants.
*/
define('olRecipientTrackStatusNone' ,0);
define('olRecipientTrackStatusTentative' ,2);
define('olRecipientTrackStatusAccepted' ,3);
define('olRecipientTrackStatusDeclined' ,4);
/* OlMeetingStatus */
define('olNonMeeting' ,0);
define('olMeeting' ,1);
define('olMeetingReceived' ,3);
define('olMeetingCanceled' ,5);
define('olMeetingReceivedAndCanceled' ,7);
/* OlMeetingResponse */
define('olMeetingTentative' ,2);
define('olMeetingAccepted' ,3);
define('olMeetingDeclined' ,4);
/* OL Attendee type */
define('olAttendeeRequired' ,1);
define('olAttendeeOptional' ,2);
define('olAttendeeResource' ,3);
/* task status */
define('olTaskNotStarted' ,0);
define('olTaskInProgress' ,1);
define('olTaskComplete' ,2);
define('olTaskWaiting' ,3);
define('olTaskDeferred' ,4);
/* restrictions */
define('RES_AND' ,0);
define('RES_OR' ,1);
define('RES_NOT' ,2);
define('RES_CONTENT' ,3);
define('RES_PROPERTY' ,4);
define('RES_COMPAREPROPS' ,5);
define('RES_BITMASK' ,6);
define('RES_SIZE' ,7);
define('RES_EXIST' ,8);
define('RES_SUBRESTRICTION' ,9);
define('RES_COMMENT' ,10);
/* restriction compares */
define('RELOP_LT' ,0);
define('RELOP_LE' ,1);
define('RELOP_GT' ,2);
define('RELOP_GE' ,3);
define('RELOP_EQ' ,4);
define('RELOP_NE' ,5);
define('RELOP_RE' ,6);
/* string 'fuzzylevel' */
define('FL_FULLSTRING' ,0x00000000);
define('FL_SUBSTRING' ,0x00000001);
define('FL_PREFIX' ,0x00000002);
define('FL_IGNORECASE' ,0x00010000);
define('FL_IGNORENONSPACE' ,0x00020000);
define('FL_LOOSE' ,0x00040000);
/* bitmask restriction types */
define('BMR_EQZ' ,0x00000000);
define('BMR_NEZ' ,0x00000001);
/* array index values of restrictions -- same values are used in php-ext/main.cpp::PHPArraytoSRestriction() */
define('VALUE' ,0); // propval
define('RELOP' ,1); // compare method
define('FUZZYLEVEL' ,2); // string search flags
define('CB' ,3); // size restriction
define('ULTYPE' ,4); // bit mask restriction type BMR_xxx
define('ULMASK' ,5); // bitmask
define('ULPROPTAG' ,6); // property
define('ULPROPTAG1' ,7); // RES_COMPAREPROPS 1st property
define('ULPROPTAG2' ,8); // RES_COMPAREPROPS 2nd property
define('PROPS' ,9); // RES_COMMENT properties
define('RESTRICTION' ,10); // RES_COMMENT and RES_SUBRESTRICTION restriction
/* GUID's for PR_MDB_PROVIDER */
define("ZARAFA_SERVICE_GUID" ,makeGuid("{3C253DCA-D227-443C-94FE-425FAB958C19}")); // default store
define("ZARAFA_STORE_PUBLIC_GUID" ,makeGuid("{D47F4A09-D3BD-493C-B2FC-3C90BBCB48D4}")); // public store
define("ZARAFA_STORE_DELEGATE_GUID" ,makeGuid("{7C7C1085-BC6D-4E53-9DAB-8A53F8DEF808}")); // other store
define('ZARAFA_STORE_ARCHIVER_GUID' ,makeGuid("{BC8953AD-2E3F-4172-9404-896FF459870F}")); // archive store
/* global profile section guid */
define('pbGlobalProfileSectionGuid' ,makeGuid("{C8B0DB13-05AA-1A10-9BB0-00AA002FC45A}"));
/* Zarafa Contacts provider GUID */
define('ZARAFA_CONTACTS_GUID' ,makeGuid("{30047F72-92E3-DA4F-B86A-E52A7FE46571}"));
/* Permissions */
// Get permission type
define('ACCESS_TYPE_DENIED' ,1);
define('ACCESS_TYPE_GRANT' ,2);
define('ACCESS_TYPE_BOTH' ,3);
define('ecRightsNone' ,0x00000000);
define('ecRightsReadAny' ,0x00000001);
define('ecRightsCreate' ,0x00000002);
define('ecRightsEditOwned' ,0x00000008);
define('ecRightsDeleteOwned' ,0x00000010);
define('ecRightsEditAny' ,0x00000020);
define('ecRightsDeleteAny' ,0x00000040);
define('ecRightsCreateSubfolder' ,0x00000080);
define('ecRightsFolderAccess' ,0x00000100);
//define('ecrightsContact' ,0x00000200);
define('ecRightsFolderVisible' ,0x00000400);
define('ecRightsAll' ,ecRightsReadAny | ecRightsCreate | ecRightsEditOwned | ecRightsDeleteOwned | ecRightsEditAny | ecRightsDeleteAny | ecRightsCreateSubfolder | ecRightsFolderAccess | ecRightsFolderVisible);
define('ecRightsFullControl' ,ecRightsReadAny | ecRightsCreate | ecRightsEditOwned | ecRightsDeleteOwned | ecRightsEditAny | ecRightsDeleteAny | ecRightsCreateSubfolder | ecRightsFolderVisible);
define('ecRightsDefault' ,ecRightsNone | ecRightsFolderVisible);
define('ecRightsDefaultPublic' ,ecRightsReadAny | ecRightsFolderVisible);
define('ecRightsAdmin' ,0x00001000);
define('ecRightsAllMask' ,0x000015FB);
// Right change indication
define('RIGHT_NORMAL' ,0x00);
define('RIGHT_NEW' ,0x01);
define('RIGHT_MODIFY' ,0x02);
define('RIGHT_DELETED' ,0x04);
define('RIGHT_AUTOUPDATE_DENIED' ,0x08);
// IExchangeModifyTable: defines for rules
define('ROWLIST_REPLACE' ,0x0001);
define('ROW_ADD' ,0x0001);
define('ROW_MODIFY' ,0x0002);
define('ROW_REMOVE' ,0x0004);
define('ROW_EMPTY' ,(ROW_ADD|ROW_REMOVE));
// new property types
define('PT_SRESTRICTION' ,0x00FD);
define('PT_ACTIONS' ,0x00FE);
// unused, I believe
define('PT_FILE_HANDLE' ,0x0103);
define('PT_FILE_EA' ,0x0104);
define('PT_VIRTUAL' ,0x0105);
// rules state
define('ST_DISABLED' ,0x0000);
define('ST_ENABLED' ,0x0001);
define('ST_ERROR' ,0x0002);
define('ST_ONLY_WHEN_OOF' ,0x0004);
define('ST_KEEP_OOF_HIST' ,0x0008);
define('ST_EXIT_LEVEL' ,0x0010);
define('ST_SKIP_IF_SCL_IS_SAFE' ,0x0020);
define('ST_RULE_PARSE_ERROR' ,0x0040);
define('ST_CLEAR_OOF_HIST' ,0x80000000);
// action types
define('OP_MOVE' ,1);
define('OP_COPY' ,2);
define('OP_REPLY' ,3);
define('OP_OOF_REPLY' ,4);
define('OP_DEFER_ACTION' ,5);
define('OP_BOUNCE' ,6);
define('OP_FORWARD' ,7);
define('OP_DELEGATE' ,8);
define('OP_TAG' ,9);
define('OP_DELETE' ,10);
define('OP_MARK_AS_READ' ,11);
// for OP_REPLY
define('DO_NOT_SEND_TO_ORIGINATOR' ,1);
define('STOCK_REPLY_TEMPLATE' ,2);
// for OP_FORWARD
define('FWD_PRESERVE_SENDER' ,1);
define('FWD_DO_NOT_MUNGE_MSG' ,2);
define('FWD_AS_ATTACHMENT' ,4);
// scBounceCodevalues
define('BOUNCE_MESSAGE_SIZE_TOO_LARGE' ,13);
define('BOUNCE_FORMS_MISMATCH' ,31);
define('BOUNCE_ACCESS_DENIED' ,38);
// Free/busystatus
define('fbFree' ,0);
define('fbTentative' ,1);
define('fbBusy' ,2);
define('fbOutOfOffice' ,3);
define('fbWorkingElsewhere' ,4);
/* ICS flags */
// For Synchronize()
define('SYNC_UNICODE' ,0x01);
define('SYNC_NO_DELETIONS' ,0x02);
define('SYNC_NO_SOFT_DELETIONS' ,0x04);
define('SYNC_READ_STATE' ,0x08);
define('SYNC_ASSOCIATED' ,0x10);
define('SYNC_NORMAL' ,0x20);
define('SYNC_NO_CONFLICTS' ,0x40);
define('SYNC_ONLY_SPECIFIED_PROPS' ,0x80);
define('SYNC_NO_FOREIGN_KEYS' ,0x100);
define('SYNC_LIMITED_IMESSAGE' ,0x200);
define('SYNC_CATCHUP' ,0x400);
define('SYNC_NEW_MESSAGE' ,0x800); // only applicable to ImportMessageChange()
define('SYNC_MSG_SELECTIVE' ,0x1000); // Used internally. Will reject if used by clients.
define('SYNC_BEST_BODY' ,0x2000);
define('SYNC_IGNORE_SPECIFIED_ON_ASSOCIATED' ,0x4000);
define('SYNC_PROGRESS_MODE' ,0x8000); // AirMapi progress mode
define('SYNC_FXRECOVERMODE' ,0x10000);
define('SYNC_DEFER_CONFIG' ,0x20000);
define('SYNC_FORCE_UNICODE' ,0x40000); // Forces server to return Unicode properties
define('EMS_AB_ADDRESS_LOOKUP' ,0x00000001); // Flag for resolvename to resolve only exact matches
define('TBL_BATCH' ,0x00000002); // Batch multiple table commands
/* Flags for recipients in exceptions */
define('recipSendable' ,0x00000001); // sendable attendee.
define('recipOrganizer' ,0x00000002); // meeting organizer
define('recipExceptionalResponse' ,0x00000010); // attendee gave a response for the exception
define('recipExceptionalDeleted' ,0x00000020); // recipientRow exists, but it is treated as if the corresponding recipient is deleted from meeting
define('recipOriginal' ,0x00000100); // recipient is an original Attendee
define('recipReserved' ,0x00000200);
/* Flags which indicates type of Meeting Object */
define('mtgEmpty' ,0x00000000); // Unspecified.
define('mtgRequest' ,0x00000001); // Initial meeting request.
define('mtgFull' ,0x00010000); // Full update.
define('mtgInfo' ,0x00020000); // Informational update.
define('mtgOutOfDate' ,0x00080000); // A newer Meeting Request object or Meeting Update object was received after this one.
define('mtgDelegatorCopy' ,0x00100000); // This is set on the delegator's copy when a delegate will handle meeting-related objects.
define('MAPI_ONE_OFF_UNICODE' ,0x8000); // the flag that defines whether the embedded strings are Unicode in one off entryids.
define('MAPI_ONE_OFF_NO_RICH_INFO' ,0x0001); // the flag that specifies whether the recipient gets TNEF or not.
/* Mask flags for mapi_msgstore_advise */
define('fnevCriticalError' ,0x00000001);
define('fnevNewMail' ,0x00000002);
define('fnevObjectCreated' ,0x00000004);
define('fnevObjectDeleted' ,0x00000008);
define('fnevObjectModified' ,0x00000010);
define('fnevObjectMoved' ,0x00000020);
define('fnevObjectCopied' ,0x00000040);
define('fnevSearchComplete' ,0x00000080);
define('fnevTableModified' ,0x00000100);
define('fnevStatusObjectModified' ,0x00000200);
define('fnevReservedForMapi' ,0x40000000);
define('fnevExtended' ,0x80000000);

View file

@ -1,72 +0,0 @@
<?php
/*
* Copyright 2005 - 2013 Zarafa B.V.
*
* 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. 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 "Zarafa" 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 "Zarafa" or "Zarafa Server", you may only do so if you
* have a written permission by Zarafa B.V. (to acquire a permission
* please contact Zarafa at trademark@zarafa.com).
*
* The interactive user interface of the software displays an attribution
* notice containing the term "Zarafa" and/or the logo of Zarafa.
* Interactive user interfaces of unmodified and modified versions must
* display Appropriate Legal Notices according to sec. 5 of the GNU
* Affero General Public License, version 3, when you propagate
* unmodified or modified versions of the Program. In accordance with
* sec. 7 b) of the GNU Affero General Public License, version 3, these
* Appropriate Legal Notices must retain the logo of Zarafa or display
* the words "Initial Development by Zarafa" if the display of the logo
* is not reasonably feasible for technical reasons. The use of the logo
* of Zarafa in Legal Notices is allowed for unmodified and modified
* versions of the software.
*
* 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 <http://www.gnu.org/licenses/>.
*
*/
define('IID_IStream', makeguid("{0000000c-0000-0000-c000-000000000046}"));
define('IID_IMAPITable', makeguid("{00020301-0000-0000-c000-000000000046}"));
define('IID_IMessage', makeguid("{00020307-0000-0000-c000-000000000046}"));
define('IID_IExchangeExportChanges', makeguid("{a3ea9cc0-d1b2-11cd-80fc-00aa004bba0b}"));
define('IID_IExchangeImportContentsChanges', makeguid("{f75abfa0-d0e0-11cd-80fc-00aa004bba0b}"));
define('IID_IExchangeImportHierarchyChanges', makeguid("{85a66cf0-d0e0-11cd-80fc-00aa004bba0b}"));
define('PSETID_Appointment', makeguid("{00062002-0000-0000-C000-000000000046}"));
define('PSETID_Task', makeguid("{00062003-0000-0000-C000-000000000046}"));
define('PSETID_Address', makeguid("{00062004-0000-0000-C000-000000000046}"));
define('PSETID_Common', makeguid("{00062008-0000-0000-C000-000000000046}"));
define('PSETID_Log', makeguid("{0006200A-0000-0000-C000-000000000046}"));
define('PSETID_Note', makeguid("{0006200E-0000-0000-C000-000000000046}"));
define('PSETID_Meeting', makeguid("{6ED8DA90-450B-101B-98DA-00AA003F1305}"));
define('PSETID_Archive', makeguid("{72E98EBC-57D2-4AB5-B0AA-D50A7B531CB9}"));
define('PS_MAPI', makeguid("{00020328-0000-0000-C000-000000000046}"));
define('PS_PUBLIC_STRINGS', makeguid("{00020329-0000-0000-C000-000000000046}"));
define('PS_INTERNET_HEADERS', makeguid("{00020386-0000-0000-c000-000000000046}"));
// sk added for Z-Push
define ('PSETID_AirSync', makeguid("{71035549-0739-4DCB-9163-00F0580DBBDF}"));

File diff suppressed because it is too large Load diff

View file

@ -1,526 +0,0 @@
<?php
/***********************************************
* File : mapimapping.php
* Project : Z-Push
* Descr :
*
* Created : 29.04.2011
*
* 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 <http://www.gnu.org/licenses/>.
*
* Consult LICENSE file for details
************************************************/
/**
*
* MAPI to AS mapping class
*
*
*/
class MAPIMapping {
/**
* Returns the MAPI to AS mapping for contacts
*
* @return array
*/
public static function GetContactMapping() {
return array (
"anniversary" => PR_WEDDING_ANNIVERSARY,
"assistantname" => PR_ASSISTANT,
"assistnamephonenumber" => PR_ASSISTANT_TELEPHONE_NUMBER,
"birthday" => PR_BIRTHDAY,
"body" => PR_BODY,
"business2phonenumber" => PR_BUSINESS2_TELEPHONE_NUMBER,
"businesscity" => "PT_STRING8:PSETID_Address:0x8046",
"businesscountry" => "PT_STRING8:PSETID_Address:0x8049",
"businesspostalcode" => "PT_STRING8:PSETID_Address:0x8048",
"businessstate" => "PT_STRING8:PSETID_Address:0x8047",
"businessstreet" => "PT_STRING8:PSETID_Address:0x8045",
"businessfaxnumber" => PR_BUSINESS_FAX_NUMBER,
"businessphonenumber" => PR_OFFICE_TELEPHONE_NUMBER,
"carphonenumber" => PR_CAR_TELEPHONE_NUMBER,
"categories" => "PT_MV_STRING8:PS_PUBLIC_STRINGS:Keywords",
"children" => PR_CHILDRENS_NAMES,
"companyname" => PR_COMPANY_NAME,
"department" => PR_DEPARTMENT_NAME,
"email1address" => "PT_STRING8:PSETID_Address:0x8083",
"email2address" => "PT_STRING8:PSETID_Address:0x8093",
"email3address" => "PT_STRING8:PSETID_Address:0x80A3",
"fileas" => "PT_STRING8:PSETID_Address:0x8005",
"firstname" => PR_GIVEN_NAME,
"home2phonenumber" => PR_HOME2_TELEPHONE_NUMBER,
"homecity" => PR_HOME_ADDRESS_CITY,
"homecountry" => PR_HOME_ADDRESS_COUNTRY,
"homepostalcode" => PR_HOME_ADDRESS_POSTAL_CODE,
"homestate" => PR_HOME_ADDRESS_STATE_OR_PROVINCE,
"homestreet" => PR_HOME_ADDRESS_STREET,
"homefaxnumber" => PR_HOME_FAX_NUMBER,
"homephonenumber" => PR_HOME_TELEPHONE_NUMBER,
"jobtitle" => PR_TITLE,
"lastname" => PR_SURNAME,
"middlename" => PR_MIDDLE_NAME,
"mobilephonenumber" => PR_CELLULAR_TELEPHONE_NUMBER,
"officelocation" => PR_OFFICE_LOCATION,
"othercity" => PR_OTHER_ADDRESS_CITY,
"othercountry" => PR_OTHER_ADDRESS_COUNTRY,
"otherpostalcode" => PR_OTHER_ADDRESS_POSTAL_CODE,
"otherstate" => PR_OTHER_ADDRESS_STATE_OR_PROVINCE,
"otherstreet" => PR_OTHER_ADDRESS_STREET,
"pagernumber" => PR_PAGER_TELEPHONE_NUMBER,
"radiophonenumber" => PR_RADIO_TELEPHONE_NUMBER,
"spouse" => PR_SPOUSE_NAME,
"suffix" => PR_GENERATION,
"title" => PR_DISPLAY_NAME_PREFIX,
"webpage" => "PT_STRING8:PSETID_Address:0x802b",
"yomicompanyname" => "PT_STRING8:PSETID_Address:0x802e",
"yomifirstname" => "PT_STRING8:PSETID_Address:0x802c",
"yomilastname" => "PT_STRING8:PSETID_Address:0x802d",
"rtf" => PR_RTF_COMPRESSED,
// picture
"customerid" => PR_CUSTOMER_ID,
"governmentid" => PR_GOVERNMENT_ID_NUMBER,
"imaddress" => "PT_STRING8:PSETID_Address:0x8062",
"imaddress2" => "PT_STRING8:PSETID_AirSync:IMAddress2",
"imaddress3" => "PT_STRING8:PSETID_AirSync:IMAddress3",
"managername" => PR_MANAGER_NAME,
"companymainphone" => PR_COMPANY_MAIN_PHONE_NUMBER,
"accountname" => PR_ACCOUNT,
"nickname" => PR_NICKNAME,
// mms
);
}
/**
*
* Returns contact specific MAPI properties
*
* @access public
*
* @return array
*/
public static function GetContactProperties() {
return array (
"haspic" => "PT_BOOLEAN:PSETID_Address:0x8015",
"emailaddress1" => "PT_STRING8:PSETID_Address:0x8083",
"emailaddressdname1" => "PT_STRING8:PSETID_Address:0x8080",
"emailaddressdemail1" => "PT_STRING8:PSETID_Address:0x8084",
"emailaddresstype1" => "PT_STRING8:PSETID_Address:0x8082",
"emailaddressentryid1" => "PT_BINARY:PSETID_Address:0x8085",
"emailaddress2" => "PT_STRING8:PSETID_Address:0x8093",
"emailaddressdname2" => "PT_STRING8:PSETID_Address:0x8090",
"emailaddressdemail2" => "PT_STRING8:PSETID_Address:0x8094",
"emailaddresstype2" => "PT_STRING8:PSETID_Address:0x8092",
"emailaddressentryid2" => "PT_BINARY:PSETID_Address:0x8095",
"emailaddress3" => "PT_STRING8:PSETID_Address:0x80a3",
"emailaddressdname3" => "PT_STRING8:PSETID_Address:0x80a0",
"emailaddressdemail3" => "PT_STRING8:PSETID_Address:0x80a4",
"emailaddresstype3" => "PT_STRING8:PSETID_Address:0x80a2",
"emailaddressentryid3" => "PT_BINARY:PSETID_Address:0x80a5",
"addressbookmv" => "PT_MV_LONG:PSETID_Address:0x8028",
"addressbooklong" => "PT_LONG:PSETID_Address:0x8029",
"displayname" => PR_DISPLAY_NAME,
"subject" => PR_SUBJECT,
"country" => PR_COUNTRY,
"city" => PR_LOCALITY,
"postaladdress" => PR_POSTAL_ADDRESS,
"postalcode" => PR_POSTAL_CODE,
"state" => PR_STATE_OR_PROVINCE,
"street" => PR_STREET_ADDRESS,
"homeaddress" => "PT_STRING8:PSETID_Address:0x801a",
"businessaddress" => "PT_STRING8:PSETID_Address:0x801b",
"otheraddress" => "PT_STRING8:PSETID_Address:0x801c",
"mailingaddress" => "PT_LONG:PSETID_Address:0x8022",
);
}
/**
* Returns the MAPI to AS mapping for emails
*
* @return array
*/
public static function GetEmailMapping() {
return array (
// from
"datereceived" => PR_MESSAGE_DELIVERY_TIME,
"displayname" => PR_SUBJECT,
"displayto" => PR_DISPLAY_TO,
"importance" => PR_IMPORTANCE,
"messageclass" => PR_MESSAGE_CLASS,
"subject" => PR_SUBJECT,
"read" => PR_MESSAGE_FLAGS,
// "to" // need to be generated with SMTP addresses
// "cc"
// "threadtopic" => PR_CONVERSATION_TOPIC,
"internetcpid" => PR_INTERNET_CPID,
"nativebodytype" => PR_NATIVE_BODY_INFO,
"lastverbexecuted" => PR_LAST_VERB_EXECUTED,
"lastverbexectime" => PR_LAST_VERB_EXECUTION_TIME,
);
}
/**
*
* Returns email specific MAPI properties
*
* @access public
*
* @return array
*/
public static function GetEmailProperties() {
return array (
// Override 'From' to show "Full Name <user@domain.com>"
"representingname" => PR_SENT_REPRESENTING_NAME,
"representingentryid" => PR_SENT_REPRESENTING_ENTRYID,
"sourcekey" => PR_SOURCE_KEY,
"entryid" => PR_ENTRYID,
"body" => PR_BODY,
"rtfcompressed" => PR_RTF_COMPRESSED,
"html" => PR_HTML,
"rtfinsync" => PR_RTF_IN_SYNC,
"processed" => PR_PROCESSED,
);
}
/**
* Returns the MAPI to AS mapping for meeting requests
*
* @return array
*/
public static function GetMeetingRequestMapping() {
return array (
"responserequested" => PR_RESPONSE_REQUESTED,
// timezone
"alldayevent" => "PT_BOOLEAN:PSETID_Appointment:0x8215",
"busystatus" => "PT_LONG:PSETID_Appointment:0x8224",
"rtf" => PR_RTF_COMPRESSED,
"dtstamp" => PR_LAST_MODIFICATION_TIME,
"endtime" => "PT_SYSTIME:PSETID_Appointment:0x820e",
"location" => "PT_STRING8:PSETID_Appointment:0x8208",
// recurrences
"reminder" => "PT_LONG:PSETID_Common:0x8501",
"starttime" => "PT_SYSTIME:PSETID_Appointment:0x820d",
"sensitivity" => PR_SENSITIVITY,
);
}
public static function GetMeetingRequestProperties() {
return array (
"goidtag" => "PT_BINARY:PSETID_Meeting:0x3",
"timezonetag" => "PT_BINARY:PSETID_Appointment:0x8233",
"recReplTime" => "PT_SYSTIME:PSETID_Appointment:0x8228",
"isrecurringtag" => "PT_BOOLEAN:PSETID_Appointment:0x8223",
"recurringstate" => "PT_BINARY:PSETID_Appointment:0x8216",
"appSeqNr" => "PT_LONG:PSETID_Appointment:0x8201",
"lidIsException" => "PT_BOOLEAN:PSETID_Appointment:0xA",
"recurStartTime" => "PT_LONG:PSETID_Meeting:0xE",
"reminderset" => "PT_BOOLEAN:PSETID_Common:0x8503",
"remindertime" => "PT_LONG:PSETID_Common:0x8501",
"recurrenceend" => "PT_SYSTIME:PSETID_Appointment:0x8236",
);
}
public static function GetTnefAndIcalProperties() {
return array(
"starttime" => "PT_SYSTIME:PSETID_Appointment:0x820d",
"endtime" => "PT_SYSTIME:PSETID_Appointment:0x820e",
"commonstart" => "PT_SYSTIME:PSETID_Common:0x8516",
"commonend" => "PT_SYSTIME:PSETID_Common:0x8517",
"clipstart" => "PT_SYSTIME:PSETID_Appointment:0x8235", //ical only
"recurrenceend" => "PT_SYSTIME:PSETID_Appointment:0x8236", //ical only
"isrecurringtag" => "PT_BOOLEAN:PSETID_Appointment:0x8223",
"goidtag" => "PT_BINARY:PSETID_Meeting:0x3",
"goid2tag" => "PT_BINARY:PSETID_Meeting:0x23",
"usetnef" => "PT_LONG:PSETID_Meeting:0x8582",
"tneflocation" => "PT_STRING8:PSETID_Meeting:0x2", //ical only
"location" => "PT_STRING8:PSETID_Appointment:0x8208",
"tnefrecurr" => "PT_BOOLEAN:PSETID_Meeting:0x5",
"sideeffects" => "PT_LONG:PSETID_Common:0x8510",
"type" => "PT_STRING8:PSETID_Meeting:0x24",
"busystatus" => "PT_LONG:PSETID_Appointment:0x8205",
"meetingstatus" => "PT_LONG:PSETID_Appointment:0x8217",
"responsestatus" => "PT_LONG:PSETID_Meeting:0x8218",
//the properties below are currently not used
"dayinterval" => "PT_I2:PSETID_Meeting:0x11",
"weekinterval" => "PT_I2:PSETID_Meeting:0x12",
"monthinterval" => "PT_I2:PSETID_Meeting:0x13",
"yearinterval" => "PT_I2:PSETID_Meeting:0x14",
);
}
/**
* Returns the MAPI to AS mapping for appointments
*
* @return array
*/
public static function GetAppointmentMapping() {
return array (
"alldayevent" => "PT_BOOLEAN:PSETID_Appointment:0x8215",
"body" => PR_BODY,
"busystatus" => "PT_LONG:PSETID_Appointment:0x8205",
"categories" => "PT_MV_STRING8:PS_PUBLIC_STRINGS:Keywords",
"rtf" => PR_RTF_COMPRESSED,
"dtstamp" => PR_LAST_MODIFICATION_TIME,
"endtime" => "PT_SYSTIME:PSETID_Appointment:0x820e",
"location" => "PT_STRING8:PSETID_Appointment:0x8208",
"meetingstatus" => "PT_LONG:PSETID_Appointment:0x8217",
"sensitivity" => PR_SENSITIVITY,
"subject" => PR_SUBJECT,
"starttime" => "PT_SYSTIME:PSETID_Appointment:0x820d",
"uid" => "PT_BINARY:PSETID_Meeting:0x3",
"nativebodytype" => PR_NATIVE_BODY_INFO,
);
}
/**
*
* Returns appointment specific MAPI properties
*
* @access public
*
* @return array
*/
public static function GetAppointmentProperties() {
return array(
"sourcekey" => PR_SOURCE_KEY,
"representingentryid" => PR_SENT_REPRESENTING_ENTRYID,
"representingname" => PR_SENT_REPRESENTING_NAME,
"sentrepresentingemail" => PR_SENT_REPRESENTING_EMAIL_ADDRESS,
"sentrepresentingaddt" => PR_SENT_REPRESENTING_ADDRTYPE,
"sentrepresentinsrchk" => PR_SENT_REPRESENTING_SEARCH_KEY,
"reminderset" => "PT_BOOLEAN:PSETID_Common:0x8503",
"remindertime" => "PT_LONG:PSETID_Common:0x8501",
"meetingstatus" => "PT_LONG:PSETID_Appointment:0x8217",
"isrecurring" => "PT_BOOLEAN:PSETID_Appointment:0x8223",
"recurringstate" => "PT_BINARY:PSETID_Appointment:0x8216",
"timezonetag" => "PT_BINARY:PSETID_Appointment:0x8233",
"recurrenceend" => "PT_SYSTIME:PSETID_Appointment:0x8236",
"responsestatus" => "PT_LONG:PSETID_Appointment:0x8218",
"commonstart" => "PT_SYSTIME:PSETID_Common:0x8516",
"commonend" => "PT_SYSTIME:PSETID_Common:0x8517",
"reminderstart" => "PT_SYSTIME:PSETID_Common:0x8502",
"duration" => "PT_LONG:PSETID_Appointment:0x8213",
"private" => "PT_BOOLEAN:PSETID_Common:0x8506",
"uid" => "PT_BINARY:PSETID_Meeting:0x23",
"sideeffects" => "PT_LONG:PSETID_Common:0x8510",
"flagdueby" => "PT_SYSTIME:PSETID_Common:0x8560",
"icon" => PR_ICON_INDEX,
"mrwassent" => "PT_BOOLEAN:PSETID_Appointment:0x8229",
"endtime" => "PT_SYSTIME:PSETID_Appointment:0x820e",//this is here for calendar restriction, tnef and ical
"starttime" => "PT_SYSTIME:PSETID_Appointment:0x820d",//this is here for calendar restriction, tnef and ical
"clipstart" => "PT_SYSTIME:PSETID_Appointment:0x8235", //ical only
"recurrencetype" => "PT_LONG:PSETID_Appointment:0x8231",
"body" => PR_BODY,
"rtfcompressed" => PR_RTF_COMPRESSED,
"html" => PR_HTML,
"rtfinsync" => PR_RTF_IN_SYNC,
);
}
/**
* Returns the MAPI to AS mapping for tasks
*
* @return array
*/
public static function GetTaskMapping() {
return array (
"body" => PR_BODY,
"categories" => "PT_MV_STRING8:PS_PUBLIC_STRINGS:Keywords",
"complete" => "PT_BOOLEAN:PSETID_Task:0x811C",
"datecompleted" => "PT_SYSTIME:PSETID_Task:0x810F",
"duedate" => "PT_SYSTIME:PSETID_Task:0x8105",
"utcduedate" => "PT_SYSTIME:PSETID_Common:0x8517",
"utcstartdate" => "PT_SYSTIME:PSETID_Common:0x8516",
"importance" => PR_IMPORTANCE,
// recurrence
// regenerate
// deadoccur
"reminderset" => "PT_BOOLEAN:PSETID_Common:0x8503",
"remindertime" => "PT_SYSTIME:PSETID_Common:0x8502",
"sensitivity" => PR_SENSITIVITY,
"startdate" => "PT_SYSTIME:PSETID_Task:0x8104",
"subject" => PR_SUBJECT,
"rtf" => PR_RTF_COMPRESSED,
"html" => PR_HTML,
);
}
/**
* Returns task specific MAPI properties
*
* @access public
*
* @return array
*/
public static function GetTaskProperties() {
return array (
"isrecurringtag" => "PT_BOOLEAN:PSETID_Task:0x8126",
"recurringstate" => "PT_BINARY:PSETID_Task:0x8116",
"deadoccur" => "PT_BOOLEAN:PSETID_Task:0x8109",
"completion" => "PT_DOUBLE:PSETID_Task:0x8102",
"status" => "PT_LONG:PSETID_Task:0x8101",
"icon" => PR_ICON_INDEX,
"owner" => "PT_STRING8:PSETID_Task:0x811F",
);
}
/**
* Returns the MAPI to AS mapping for email todo flags
*
* @return array
*/
public static function GetMailFlagsMapping() {
return array (
"flagstatus" => PR_FLAG_STATUS,
"flagtype" => "PT_STRING8:PSETID_Common:0x8530",
"datecompleted" => "PT_SYSTIME:PSETID_Common:0x810F",
"completetime" => PR_FLAG_COMPLETE_TIME,
"startdate" => "PT_SYSTIME:PSETID_Task:0x8104",
"duedate" => "PT_SYSTIME:PSETID_Task:0x8105",
"utcstartdate" => "PT_SYSTIME:PSETID_Common:0x8516",
"utcduedate" => "PT_SYSTIME:PSETID_Common:0x8517",
"reminderset" => "PT_BOOLEAN:PSETID_Common:0x8503",
"remindertime" => "PT_SYSTIME:PSETID_Common:0x8502",
"ordinaldate" => "PT_SYSTIME:PSETID_Common:0x85A0",
"subordinaldate" => "PT_STRING8:PSETID_Common:0x85A1",
);
}
/**
* Returns email todo flags' specific MAPI properties
*
* @access public
*
* @return array
*/
public static function GetMailFlagsProperties() {
return array(
"todoitemsflags" => PR_TODO_ITEM_FLAGS,
"todotitle" => "PT_STRING8:PSETID_Common:0x85A4",
"flagicon" => PR_FLAG_ICON,
"replyrequested" => PR_REPLY_REQUESTED,
"responserequested" => PR_RESPONSE_REQUESTED,
"status" => "PT_LONG:PSETID_Task:0x8101",
"completion" => "PT_DOUBLE:PSETID_Task:0x8102",
"complete" => "PT_BOOLEAN:PSETID_Task:0x811C",
);
}
/**
* Returns the MAPI to AS mapping for notes
*
* @access public
*
* @return array
*/
public static function GetNoteMapping() {
return array(
"categories" => "PT_MV_STRING8:PS_PUBLIC_STRINGS:Keywords",
"lastmodificationtime" => PR_LAST_MODIFICATION_TIME,
"messageclass" => PR_MESSAGE_CLASS,
"subject" => PR_SUBJECT,
);
}
/**
* Returns note specific MAPI properties
*
* @access public
*
* @return array
*/
public static function GetNoteProperties() {
return array(
"body" => PR_BODY,
"messageclass" => PR_MESSAGE_CLASS,
"html" => PR_HTML,
"internetcpid" => PR_INTERNET_CPID,
);
}
/**
* Returns properties for sending an email
*
* @access public
*
* @return array
*/
public static function GetSendMailProperties() {
return array(
"outboxentryid" => PR_IPM_OUTBOX_ENTRYID,
"ipmsentmailentryid" => PR_IPM_SENTMAIL_ENTRYID,
"sentmailentryid" => PR_SENTMAIL_ENTRYID,
"subject" => PR_SUBJECT,
"messageclass" => PR_MESSAGE_CLASS,
"deliverytime" => PR_MESSAGE_DELIVERY_TIME,
"importance" => PR_IMPORTANCE,
"priority" => PR_PRIORITY,
"addrtype" => PR_ADDRTYPE,
"emailaddress" => PR_EMAIL_ADDRESS,
"displayname" => PR_DISPLAY_NAME,
"recipienttype" => PR_RECIPIENT_TYPE,
"entryid" => PR_ENTRYID,
"iconindex" => PR_ICON_INDEX,
"body" => PR_BODY,
"html" => PR_HTML,
"sentrepresentingname" => PR_SENT_REPRESENTING_NAME,
"sentrepresentingemail" => PR_SENT_REPRESENTING_EMAIL_ADDRESS,
"representingentryid" => PR_SENT_REPRESENTING_ENTRYID,
"sentrepresentingaddt" => PR_SENT_REPRESENTING_ADDRTYPE,
"sentrepresentinsrchk" => PR_SENT_REPRESENTING_SEARCH_KEY,
"displayto" => PR_DISPLAY_TO,
"displaycc" => PR_DISPLAY_CC,
"clientsubmittime" => PR_CLIENT_SUBMIT_TIME,
"attachnum" => PR_ATTACH_NUM,
"attachdatabin" => PR_ATTACH_DATA_BIN,
"internetcpid" => PR_INTERNET_CPID,
"rtf" => PR_RTF_COMPRESSED,
"rtfinsync" => PR_RTF_IN_SYNC,
);
}
}

View file

@ -1,234 +0,0 @@
<?php
/***********************************************
* File : mapiphpwrapper.php
* Project : Z-Push
* Descr : The ICS importer is very MAPI specific
* and needs to be wrapped, because we
* want all MAPI code to be separate from
* the rest of z-push. To do so all
* MAPI dependency are removed in this class.
* All the other importers are based on
* IChanges, not MAPI.
*
* Created : 14.02.2011
*
* Copyright 2007 - 2015 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 <http://www.gnu.org/licenses/>.
*
* Consult LICENSE file for details
************************************************/
/**
* This is the PHP wrapper which strips MAPI information from
* the import interface of ICS. We get all the information about messages
* from MAPI here which are sent to the next importer, which will
* convert the data into WBXML which is streamed to the PDA
*/
class PHPWrapper {
private $importer;
private $mapiprovider;
private $store;
private $contentparameters;
private $folderid;
/**
* Constructor of the PHPWrapper
*
* @param ressource $session
* @param ressource $store
* @param IImportChanges $importer incoming changes from ICS are forwarded here.
* @param string $folderid the folder this wrapper was configured for.
*
* @access public
* @return
*/
public function PHPWrapper($session, $store, $importer, $folderid) {
$this->importer = &$importer;
$this->store = $store;
$this->mapiprovider = new MAPIProvider($session, $this->store);
$this->folderid = $folderid;
}
/**
* Configures additional parameters used for content synchronization
*
* @param ContentParameters $contentparameters
*
* @access public
* @return boolean
* @throws StatusException
*/
public function ConfigContentParameters($contentparameters) {
$this->contentparameters = $contentparameters;
}
/**
* Implement MAPI interface
*/
public function Config($stream, $flags = 0) {}
public function GetLastError($hresult, $ulflags, &$lpmapierror) {}
public function UpdateState($stream) { }
/**
* Imports a single message
*
* @param array $props
* @param long $flags
* @param object $retmapimessage
*
* @access public
* @return long
*/
public function ImportMessageChange($props, $flags, &$retmapimessage) {
$sourcekey = $props[PR_SOURCE_KEY];
$parentsourcekey = $props[PR_PARENT_SOURCE_KEY];
$entryid = mapi_msgstore_entryidfromsourcekey($this->store, $parentsourcekey, $sourcekey);
if(!$entryid)
return SYNC_E_IGNORE;
$mapimessage = mapi_msgstore_openentry($this->store, $entryid);
try {
ZLog::Write(LOGLEVEL_DEBUG, sprintf("PHPWrapper->ImportMessageChange(): Getting message from MAPIProvider, sourcekey: '%s', parentsourcekey: '%s', entryid: '%s'", bin2hex($sourcekey), bin2hex($parentsourcekey), bin2hex($entryid)));
$message = $this->mapiprovider->GetMessage($mapimessage, $this->contentparameters);
}
catch (SyncObjectBrokenException $mbe) {
$brokenSO = $mbe->GetSyncObject();
if (!$brokenSO) {
ZLog::Write(LOGLEVEL_ERROR, sprintf("PHPWrapper->ImportMessageChange(): Catched SyncObjectBrokenException but broken SyncObject available"));
}
else {
if (!isset($brokenSO->id)) {
$brokenSO->id = "Unknown ID";
ZLog::Write(LOGLEVEL_ERROR, sprintf("PHPWrapper->ImportMessageChange(): Catched SyncObjectBrokenException but no ID of object set"));
}
ZPush::GetDeviceManager()->AnnounceIgnoredMessage(false, $brokenSO->id, $brokenSO);
}
// tell MAPI to ignore the message
return SYNC_E_IGNORE;
}
// substitute the MAPI SYNC_NEW_MESSAGE flag by a z-push proprietary flag
if ($flags == SYNC_NEW_MESSAGE) $message->flags = SYNC_NEWMESSAGE;
else $message->flags = $flags;
$this->importer->ImportMessageChange(bin2hex($sourcekey), $message);
// Tell MAPI it doesn't need to do anything itself, as we've done all the work already.
return SYNC_E_IGNORE;
}
/**
* Imports a list of messages to be deleted
*
* @param long $flags
* @param array $sourcekeys array with sourcekeys
*
* @access public
* @return
*/
public function ImportMessageDeletion($flags, $sourcekeys) {
$amount = count($sourcekeys);
if ($amount > 1000) {
throw new StatusException(sprintf("PHPWrapper->ImportMessageDeletion(): Received %d remove requests from ICS for folder '%s' (max. 1000 allowed). Triggering folder re-sync.", $amount, bin2hex($this->folderid)), SYNC_STATUS_INVALIDSYNCKEY, null, LOGLEVEL_ERROR);
}
else {
ZLog::Write(LOGLEVEL_DEBUG, sprintf("PHPWrapper->ImportMessageDeletion(): Received %d remove requests from ICS", $amount));
}
foreach($sourcekeys as $sourcekey) {
$this->importer->ImportMessageDeletion(bin2hex($sourcekey));
}
}
/**
* Imports a list of messages to be deleted
*
* @param mixed $readstates sourcekeys and message flags
*
* @access public
* @return
*/
public function ImportPerUserReadStateChange($readstates) {
foreach($readstates as $readstate) {
$this->importer->ImportMessageReadFlag(bin2hex($readstate["sourcekey"]), $readstate["flags"] & MSGFLAG_READ);
}
}
/**
* Imports a message move
* this is never called by ICS
*
* @access public
* @return
*/
public function ImportMessageMove($sourcekeysrcfolder, $sourcekeysrcmessage, $message, $sourcekeydestmessage, $changenumdestmessage) {
// Never called
}
/**
* Imports a single folder change
*
* @param array $props properties of the changed folder
*
* @access public
* @return
*/
function ImportFolderChange($props) {
$folder = $this->mapiprovider->GetFolder($props);
// do not import folder if there is something "wrong" with it
if ($folder === false)
return 0;
$this->importer->ImportFolderChange($folder);
return 0;
}
/**
* Imports a list of folders which are to be deleted
*
* @param long $flags
* @param mixed $sourcekeys array with sourcekeys
*
* @access public
* @return
*/
function ImportFolderDeletion($flags, $sourcekeys) {
foreach ($sourcekeys as $sourcekey) {
$this->importer->ImportFolderDeletion(bin2hex($sourcekey));
}
return 0;
}
}

File diff suppressed because it is too large Load diff

View file

@ -1,146 +0,0 @@
<?php
/***********************************************
* File : mapistreamwrapper.php
* Project : Z-Push
* Descr : Wraps a mapi stream as a standard php stream
* The used method names are predefined and can not be altered.
*
* Created : 24.11.2011
*
* 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 <http://www.gnu.org/licenses/>.
*
* Consult LICENSE file for details
************************************************/
class MAPIStreamWrapper {
const PROTOCOL = "mapistream";
private $mapistream;
private $position;
private $streamlength;
/**
* Opens the stream
* The mapistream reference is passed over the context
*
* @param string $path Specifies the URL that was passed to the original function
* @param string $mode The mode used to open the file, as detailed for fopen()
* @param int $options Holds additional flags set by the streams API
* @param string $opened_path If the path is opened successfully, and STREAM_USE_PATH is set in options,
* opened_path should be set to the full path of the file/resource that was actually opened.
*
* @access public
* @return boolean
*/
public function stream_open($path, $mode, $options, &$opened_path) {
$contextOptions = stream_context_get_options($this->context);
if (!isset($contextOptions[self::PROTOCOL]['stream']))
return false;
$this->position = 0;
// this is our stream!
$this->mapistream = $contextOptions[self::PROTOCOL]['stream'];
// get the data length from mapi
$stat = mapi_stream_stat($this->mapistream);
$this->streamlength = $stat["cb"];
ZLog::Write(LOGLEVEL_DEBUG, sprintf("MAPIStreamWrapper::stream_open(): initialized mapistream: %s streamlength: %d", $this->mapistream, $this->streamlength));
return true;
}
/**
* Reads from stream
*
* @param int $len amount of bytes to be read
*
* @access public
* @return string
*/
public function stream_read($len) {
$len = ($this->position + $len > $this->streamlength) ? ($this->streamlength - $this->position) : $len;
$data = mapi_stream_read($this->mapistream, $len);
$this->position += strlen($data);
return $data;
}
/**
* Returns the current position on stream
*
* @access public
* @return int
*/
public function stream_tell() {
return $this->position;
}
/**
* Indicates if 'end of file' is reached
*
* @access public
* @return boolean
*/
public function stream_eof() {
return ($this->position >= $this->streamlength);
}
/**
* Retrieves information about a stream
*
* @access public
* @return array
*/
public function stream_stat() {
return array(
7 => $this->streamlength,
'size' => $this->streamlength,
);
}
/**
* Instantiates a MAPIStreamWrapper
*
* @param mapistream $mapistream The stream to be wrapped
*
* @access public
* @return MAPIStreamWrapper
*/
static public function Open($mapistream) {
$context = stream_context_create(array(self::PROTOCOL => array('stream' => &$mapistream)));
return fopen(self::PROTOCOL . "://",'r', false, $context);
}
}
stream_wrapper_register(MAPIStreamWrapper::PROTOCOL, "MAPIStreamWrapper");

View file

@ -1,585 +0,0 @@
<?php
/***********************************************
* File : mapiutils.php
* Project : Z-Push
* Descr :
*
* Created : 14.02.2011
*
* 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 <http://www.gnu.org/licenses/>.
*
* Consult LICENSE file for details
************************************************/
/**
*
* MAPI to AS mapping class
*
*
*/
class MAPIUtils {
/**
* Create a MAPI restriction to use within an email folder which will
* return all messages since since $timestamp
*
* @param long $timestamp Timestamp since when to include messages
*
* @access public
* @return array
*/
public static function GetEmailRestriction($timestamp) {
// ATTENTION: ON CHANGING THIS RESTRICTION, MAPIUtils::IsInEmailSyncInterval() also needs to be changed
$restriction = array ( RES_PROPERTY,
array ( RELOP => RELOP_GE,
ULPROPTAG => PR_MESSAGE_DELIVERY_TIME,
VALUE => $timestamp
)
);
return $restriction;
}
/**
* Create a MAPI restriction to use in the calendar which will
* return all future calendar items, plus those since $timestamp
*
* @param MAPIStore $store the MAPI store
* @param long $timestamp Timestamp since when to include messages
*
* @access public
* @return array
*/
//TODO getting named properties
public static function GetCalendarRestriction($store, $timestamp) {
// This is our viewing window
$start = $timestamp;
$end = 0x7fffffff; // infinite end
$props = MAPIMapping::GetAppointmentProperties();
$props = getPropIdsFromStrings($store, $props);
// ATTENTION: ON CHANGING THIS RESTRICTION, MAPIUtils::IsInCalendarSyncInterval() also needs to be changed
$restriction = Array(RES_OR,
Array(
// OR
// item.end > window.start && item.start < window.end
Array(RES_AND,
Array(
Array(RES_PROPERTY,
Array(RELOP => RELOP_LE,
ULPROPTAG => $props["starttime"],
VALUE => $end
)
),
Array(RES_PROPERTY,
Array(RELOP => RELOP_GE,
ULPROPTAG => $props["endtime"],
VALUE => $start
)
)
)
),
// OR
Array(RES_OR,
Array(
// OR
// (EXIST(recurrence_enddate_property) && item[isRecurring] == true && recurrence_enddate_property >= start)
Array(RES_AND,
Array(
Array(RES_EXIST,
Array(ULPROPTAG => $props["recurrenceend"],
)
),
Array(RES_PROPERTY,
Array(RELOP => RELOP_EQ,
ULPROPTAG => $props["isrecurring"],
VALUE => true
)
),
Array(RES_PROPERTY,
Array(RELOP => RELOP_GE,
ULPROPTAG => $props["recurrenceend"],
VALUE => $start
)
)
)
),
// OR
// (!EXIST(recurrence_enddate_property) && item[isRecurring] == true && item[start] <= end)
Array(RES_AND,
Array(
Array(RES_NOT,
Array(
Array(RES_EXIST,
Array(ULPROPTAG => $props["recurrenceend"]
)
)
)
),
Array(RES_PROPERTY,
Array(RELOP => RELOP_LE,
ULPROPTAG => $props["starttime"],
VALUE => $end
)
),
Array(RES_PROPERTY,
Array(RELOP => RELOP_EQ,
ULPROPTAG => $props["isrecurring"],
VALUE => true
)
)
)
)
)
) // EXISTS OR
)
); // global OR
return $restriction;
}
/**
* Create a MAPI restriction in order to check if a contact has a picture
*
* @access public
* @return array
*/
public static function GetContactPicRestriction() {
return array ( RES_PROPERTY,
array (
RELOP => RELOP_EQ,
ULPROPTAG => mapi_prop_tag(PT_BOOLEAN, 0x7FFF),
VALUE => true
)
);
}
/**
* Create a MAPI restriction for search
*
* @access public
*
* @param string $query
* @return array
*/
public static function GetSearchRestriction($query) {
return array(RES_AND,
array(
array(RES_OR,
array(
array(RES_CONTENT, array(FUZZYLEVEL => FL_SUBSTRING | FL_IGNORECASE, ULPROPTAG => PR_DISPLAY_NAME, VALUE => $query)),
array(RES_CONTENT, array(FUZZYLEVEL => FL_SUBSTRING | FL_IGNORECASE, ULPROPTAG => PR_ACCOUNT, VALUE => $query)),
array(RES_CONTENT, array(FUZZYLEVEL => FL_SUBSTRING | FL_IGNORECASE, ULPROPTAG => PR_SMTP_ADDRESS, VALUE => $query)),
), // RES_OR
),
array(RES_OR,
array (
array(
RES_PROPERTY,
array(RELOP => RELOP_EQ, ULPROPTAG => PR_OBJECT_TYPE, VALUE => MAPI_MAILUSER)
),
array(
RES_PROPERTY,
array(RELOP => RELOP_EQ, ULPROPTAG => PR_OBJECT_TYPE, VALUE => MAPI_DISTLIST)
)
)
) // RES_OR
) // RES_AND
);
}
/**
* Create a MAPI restriction for a certain email address
*
* @access public
*
* @param MAPIStore $store the MAPI store
* @param string $query email address
*
* @return array
*/
public static function GetEmailAddressRestriction($store, $email) {
$props = MAPIMapping::GetContactProperties();
$props = getPropIdsFromStrings($store, $props);
return array(RES_OR,
array(
array( RES_PROPERTY,
array( RELOP => RELOP_EQ,
ULPROPTAG => $props['emailaddress1'],
VALUE => array($props['emailaddress1'] => $email),
),
),
array( RES_PROPERTY,
array( RELOP => RELOP_EQ,
ULPROPTAG => $props['emailaddress2'],
VALUE => array($props['emailaddress2'] => $email),
),
),
array( RES_PROPERTY,
array( RELOP => RELOP_EQ,
ULPROPTAG => $props['emailaddress3'],
VALUE => array($props['emailaddress3'] => $email),
),
),
),
);
}
/**
* Create a MAPI restriction for a certain folder type
*
* @access public
*
* @param string $foldertype folder type for restriction
* @return array
*/
public static function GetFolderTypeRestriction($foldertype) {
return array( RES_PROPERTY,
array( RELOP => RELOP_EQ,
ULPROPTAG => PR_CONTAINER_CLASS,
VALUE => array(PR_CONTAINER_CLASS => $foldertype)
),
);
}
/**
* Returns subfolders of given type for a folder or false if there are none.
*
* @access public
*
* @param MAPIFolder $folder
* @param string $type
*
* @return MAPITable|boolean
*/
public static function GetSubfoldersForType($folder, $type) {
$subfolders = mapi_folder_gethierarchytable($folder, CONVENIENT_DEPTH);
mapi_table_restrict($subfolders, MAPIUtils::GetFolderTypeRestriction($type));
if (mapi_table_getrowcount($subfolders) > 0) {
return mapi_table_queryallrows($subfolders, array(PR_ENTRYID));
}
return false;
}
/**
* Checks if mapimessage is inside the synchronization interval
* also defined by MAPIUtils::GetEmailRestriction()
*
* @param MAPIStore $store mapi store
* @param MAPIMessage $mapimessage the mapi message to be checked
* @param long $timestamp the lower time limit
*
* @access public
* @return boolean
*/
public static function IsInEmailSyncInterval($store, $mapimessage, $timestamp) {
$p = mapi_getprops($mapimessage, array(PR_MESSAGE_DELIVERY_TIME));
if (isset($p[PR_MESSAGE_DELIVERY_TIME]) && $p[PR_MESSAGE_DELIVERY_TIME] >= $timestamp) {
ZLog::Write(LOGLEVEL_DEBUG, "MAPIUtils->IsInEmailSyncInterval: Message is in the synchronization interval");
return true;
}
ZLog::Write(LOGLEVEL_WARN, "MAPIUtils->IsInEmailSyncInterval: Message is OUTSIDE the synchronization interval");
return false;
}
/**
* Checks if mapimessage is inside the synchronization interval
* also defined by MAPIUtils::GetCalendarRestriction()
*
* @param MAPIStore $store mapi store
* @param MAPIMessage $mapimessage the mapi message to be checked
* @param long $timestamp the lower time limit
*
* @access public
* @return boolean
*/
public static function IsInCalendarSyncInterval($store, $mapimessage, $timestamp) {
// This is our viewing window
$start = $timestamp;
$end = 0x7fffffff; // infinite end
$props = MAPIMapping::GetAppointmentProperties();
$props = getPropIdsFromStrings($store, $props);
$p = mapi_getprops($mapimessage, array($props["starttime"], $props["endtime"], $props["recurrenceend"], $props["isrecurring"], $props["recurrenceend"]));
if (
(
isset($p[$props["endtime"]]) && isset($p[$props["starttime"]]) &&
//item.end > window.start && item.start < window.end
$p[$props["endtime"]] > $start && $p[$props["starttime"]] < $end
)
||
(
isset($p[$props["isrecurring"]]) &&
//(EXIST(recurrence_enddate_property) && item[isRecurring] == true && recurrence_enddate_property >= start)
isset($p[$props["recurrenceend"]]) && $p[$props["isrecurring"]] == true && $p[$props["recurrenceend"]] >= $start
)
||
(
isset($p[$props["isrecurring"]]) && isset($p[$props["starttime"]]) &&
//(!EXIST(recurrence_enddate_property) && item[isRecurring] == true && item[start] <= end)
!isset($p[$props["recurrenceend"]]) && $p[$props["isrecurring"]] == true && $p[$props["starttime"]] <= $end
)
) {
ZLog::Write(LOGLEVEL_DEBUG, "MAPIUtils->IsInCalendarSyncInterval: Message is in the synchronization interval");
return true;
}
ZLog::Write(LOGLEVEL_WARN, "MAPIUtils->IsInCalendarSyncInterval: Message is OUTSIDE the synchronization interval");
return false;
}
/**
* Reads data of large properties from a stream
*
* @param MAPIMessage $message
* @param long $prop
*
* @access public
* @return string
*/
public static function readPropStream($message, $prop) {
$stream = mapi_openproperty($message, $prop, IID_IStream, 0, 0);
$ret = mapi_last_hresult();
if ($ret == MAPI_E_NOT_FOUND) {
ZLog::Write(LOGLEVEL_DEBUG, sprintf("MAPIUtils->readPropStream: property 0x%s not found. It is either empty or not set. It will be ignored.", str_pad(dechex($prop), 8, 0, STR_PAD_LEFT)));
return "";
}
elseif ($ret) {
ZLog::Write(LOGLEVEL_ERROR, "MAPIUtils->readPropStream error opening stream: 0X%X", $ret);
return "";
}
$data = "";
$string = "";
while(1) {
$data = mapi_stream_read($stream, 1024);
if(strlen($data) == 0)
break;
$string .= $data;
}
return $string;
}
/**
* Checks if a store supports properties containing unicode characters
*
* @param MAPIStore $store
*
* @access public
* @return
*/
public static function IsUnicodeStore($store) {
$supportmask = mapi_getprops($store, array(PR_STORE_SUPPORT_MASK));
if (isset($supportmask[PR_STORE_SUPPORT_MASK]) && ($supportmask[PR_STORE_SUPPORT_MASK] & STORE_UNICODE_OK)) {
ZLog::Write(LOGLEVEL_DEBUG, "Store supports properties containing Unicode characters.");
define('STORE_SUPPORTS_UNICODE', true);
//setlocale to UTF-8 in order to support properties containing Unicode characters
setlocale(LC_CTYPE, "en_US.UTF-8");
define('STORE_INTERNET_CPID', INTERNET_CPID_UTF8);
}
}
/**
* Returns the MAPI PR_CONTAINER_CLASS string for an ActiveSync Foldertype
*
* @param int $foldertype
*
* @access public
* @return string
*/
public static function GetContainerClassFromFolderType($foldertype) {
switch ($foldertype) {
case SYNC_FOLDER_TYPE_TASK:
case SYNC_FOLDER_TYPE_USER_TASK:
return "IPF.Task";
break;
case SYNC_FOLDER_TYPE_APPOINTMENT:
case SYNC_FOLDER_TYPE_USER_APPOINTMENT:
return "IPF.Appointment";
break;
case SYNC_FOLDER_TYPE_CONTACT:
case SYNC_FOLDER_TYPE_USER_CONTACT:
return "IPF.Contact";
break;
case SYNC_FOLDER_TYPE_NOTE:
case SYNC_FOLDER_TYPE_USER_NOTE:
return "IPF.StickyNote";
break;
case SYNC_FOLDER_TYPE_JOURNAL:
case SYNC_FOLDER_TYPE_USER_JOURNAL:
return "IPF.Journal";
break;
case SYNC_FOLDER_TYPE_INBOX:
case SYNC_FOLDER_TYPE_DRAFTS:
case SYNC_FOLDER_TYPE_WASTEBASKET:
case SYNC_FOLDER_TYPE_SENTMAIL:
case SYNC_FOLDER_TYPE_OUTBOX:
case SYNC_FOLDER_TYPE_USER_MAIL:
case SYNC_FOLDER_TYPE_OTHER:
case SYNC_FOLDER_TYPE_UNKNOWN:
default:
return "IPF.Note";
break;
}
}
public static function GetSignedAttachmentRestriction() {
return array( RES_PROPERTY,
array( RELOP => RELOP_EQ,
ULPROPTAG => PR_ATTACH_MIME_TAG,
VALUE => array(PR_ATTACH_MIME_TAG => 'multipart/signed')
),
);
}
/**
* Calculates the native body type of a message using available properties. Refer to oxbbody.
*
* @param array $messageprops
*
* @access public
* @return int
*/
public static function GetNativeBodyType($messageprops) {
//check if the properties are set and get the error code if needed
if (!isset($messageprops[PR_BODY])) $messageprops[PR_BODY] = self::getError(PR_BODY, $messageprops);
if (!isset($messageprops[PR_RTF_COMPRESSED])) $messageprops[PR_RTF_COMPRESSED] = self::getError(PR_RTF_COMPRESSED, $messageprops);
if (!isset($messageprops[PR_HTML])) $messageprops[PR_HTML] = self::getError(PR_HTML, $messageprops);
if (!isset($messageprops[PR_RTF_IN_SYNC])) $messageprops[PR_RTF_IN_SYNC] = self::getError(PR_RTF_IN_SYNC, $messageprops);
if ( // 1
($messageprops[PR_BODY] == MAPI_E_NOT_FOUND) &&
($messageprops[PR_RTF_COMPRESSED] == MAPI_E_NOT_FOUND) &&
($messageprops[PR_HTML] == MAPI_E_NOT_FOUND))
return SYNC_BODYPREFERENCE_PLAIN;
elseif ( // 2
($messageprops[PR_BODY] == MAPI_E_NOT_ENOUGH_MEMORY) &&
($messageprops[PR_RTF_COMPRESSED] == MAPI_E_NOT_FOUND) &&
($messageprops[PR_HTML] == MAPI_E_NOT_FOUND))
return SYNC_BODYPREFERENCE_PLAIN;
elseif ( // 3
($messageprops[PR_BODY] == MAPI_E_NOT_ENOUGH_MEMORY) &&
($messageprops[PR_RTF_COMPRESSED] == MAPI_E_NOT_ENOUGH_MEMORY) &&
($messageprops[PR_HTML] == MAPI_E_NOT_FOUND))
return SYNC_BODYPREFERENCE_RTF;
elseif ( // 4
($messageprops[PR_BODY] == MAPI_E_NOT_ENOUGH_MEMORY) &&
($messageprops[PR_RTF_COMPRESSED] == MAPI_E_NOT_ENOUGH_MEMORY) &&
($messageprops[PR_HTML] == MAPI_E_NOT_ENOUGH_MEMORY) &&
($messageprops[PR_RTF_IN_SYNC]))
return SYNC_BODYPREFERENCE_RTF;
elseif ( // 5
($messageprops[PR_BODY] == MAPI_E_NOT_ENOUGH_MEMORY) &&
($messageprops[PR_RTF_COMPRESSED] == MAPI_E_NOT_ENOUGH_MEMORY) &&
($messageprops[PR_HTML] == MAPI_E_NOT_ENOUGH_MEMORY) &&
(!$messageprops[PR_RTF_IN_SYNC]))
return SYNC_BODYPREFERENCE_HTML;
elseif ( // 6
($messageprops[PR_RTF_COMPRESSED] != MAPI_E_NOT_FOUND || $messageprops[PR_RTF_COMPRESSED] == MAPI_E_NOT_ENOUGH_MEMORY) &&
($messageprops[PR_HTML] != MAPI_E_NOT_FOUND || $messageprops[PR_HTML] == MAPI_E_NOT_ENOUGH_MEMORY) &&
($messageprops[PR_RTF_IN_SYNC]))
return SYNC_BODYPREFERENCE_RTF;
elseif ( // 7
($messageprops[PR_RTF_COMPRESSED] != MAPI_E_NOT_FOUND || $messageprops[PR_RTF_COMPRESSED] == MAPI_E_NOT_ENOUGH_MEMORY) &&
($messageprops[PR_HTML] != MAPI_E_NOT_FOUND || $messageprops[PR_HTML] == MAPI_E_NOT_ENOUGH_MEMORY) &&
(!$messageprops[PR_RTF_IN_SYNC]))
return SYNC_BODYPREFERENCE_HTML;
elseif ( // 8
($messageprops[PR_BODY] != MAPI_E_NOT_FOUND || $messageprops[PR_BODY] == MAPI_E_NOT_ENOUGH_MEMORY) &&
($messageprops[PR_RTF_COMPRESSED] != MAPI_E_NOT_FOUND || $messageprops[PR_RTF_COMPRESSED] == MAPI_E_NOT_ENOUGH_MEMORY) &&
($messageprops[PR_RTF_IN_SYNC]))
return SYNC_BODYPREFERENCE_RTF;
elseif ( // 9.1
($messageprops[PR_BODY] != MAPI_E_NOT_FOUND || $messageprops[PR_BODY] == MAPI_E_NOT_ENOUGH_MEMORY) &&
($messageprops[PR_RTF_COMPRESSED] != MAPI_E_NOT_FOUND || $messageprops[PR_RTF_COMPRESSED] == MAPI_E_NOT_ENOUGH_MEMORY) &&
(!$messageprops[PR_RTF_IN_SYNC]))
return SYNC_BODYPREFERENCE_PLAIN;
elseif ( // 9.2
($messageprops[PR_RTF_COMPRESSED] != MAPI_E_NOT_FOUND || $messageprops[PR_RTF_COMPRESSED] == MAPI_E_NOT_ENOUGH_MEMORY) &&
($messageprops[PR_BODY] == MAPI_E_NOT_FOUND) &&
($messageprops[PR_HTML] == MAPI_E_NOT_FOUND))
return SYNC_BODYPREFERENCE_RTF;
elseif ( // 9.3
($messageprops[PR_BODY] != MAPI_E_NOT_FOUND || $messageprops[PR_BODY] == MAPI_E_NOT_ENOUGH_MEMORY) &&
($messageprops[PR_RTF_COMPRESSED] == MAPI_E_NOT_FOUND) &&
($messageprops[PR_HTML] == MAPI_E_NOT_FOUND))
return SYNC_BODYPREFERENCE_PLAIN;
elseif ( // 9.4
($messageprops[PR_HTML] != MAPI_E_NOT_FOUND || $messageprops[PR_HTML] == MAPI_E_NOT_ENOUGH_MEMORY) &&
($messageprops[PR_BODY] == MAPI_E_NOT_FOUND) &&
($messageprops[PR_RTF_COMPRESSED] == MAPI_E_NOT_FOUND))
return SYNC_BODYPREFERENCE_HTML;
else // 10
return SYNC_BODYPREFERENCE_PLAIN;
}
/**
* Returns the error code for a given property. Helper for getNativeBodyType function.
*
* @param int $tag
* @param array $messageprops
*
* @access private
* @return int (MAPI_ERROR_CODE)
*/
private static function getError($tag, $messageprops) {
$prBodyError = mapi_prop_tag(PT_ERROR, mapi_prop_id($tag));
if(isset($messageprops[$prBodyError]) && mapi_is_error($messageprops[$prBodyError])) {
if($messageprops[$prBodyError] == MAPI_E_NOT_ENOUGH_MEMORY_32BIT ||
$messageprops[$prBodyError] == MAPI_E_NOT_ENOUGH_MEMORY_64BIT) {
return MAPI_E_NOT_ENOUGH_MEMORY;
}
}
return MAPI_E_NOT_FOUND;
}
}

View file

@ -1,720 +0,0 @@
<?php
/***********************************************
* File : tnefparser.php
* Project : Z-Push
* Descr : This is tnef implementation for z-push.
* It is based on Zarafa's tnef implementation.
* This class does only simple reading of a
* tnef stream. Most importantly, we currently
* only support properties within the message itself,
* and do not support recipient tables and
* attachment properties within the TNEF data.
* This class will accept TNEF streams with data about
* recipients and attachments, but the information
* will be ignored.
*
* Created : 21.06.2008
*
* 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 <http://www.gnu.org/licenses/>.
*
* Consult LICENSE file for details
************************************************/
/**
* For more information on tnef refer to:
* http://msdn.microsoft.com/en-us/library/ms530652(EXCHG.10).aspx
* http://msdn.microsoft.com/en-us/library/cc425498(EXCHG.80).aspx
*
* The mapping between Microsoft Mail IPM classes and those used in
* MAPI see: http://msdn2.microsoft.com/en-us/library/ms527360.aspx
*/
class TNEFParser {
const TNEF_SIGNATURE = 0x223e9f78;
const TNEF_LVL_MESSAGE = 0x01;
const TNEF_LVL_ATTACHMENT = 0x02;
const DWORD = 32;
const WORD = 16;
const BYTE = 8;
/**
* Constructor
* We need a store in order to get the namedpropers
*
* @param mapistore $store
* @param array &$props properties to be set
*
* @access public
*/
public function TNEFParser(&$store, &$props) {
$this->store = $store;
$this->props = $props;
}
/**
* Function reads tnef stream and puts mapi properties into an array.
*
* @param string $tnefstream
* @param array &$mapiprops mapi properties
*
* @access public
* @return int
*/
public function ExtractProps($tnefstream, &$mapiprops) {
$hresult = NOERROR;
$signature = 0; //tnef signature - 32 Bit
$key = 0; //a nonzero 16-bit unsigned integer
$type = 0; // 32-bit value
$size = 0; // 32-bit value
$checksum = 0; //16-bit value
$component = 0; //8-bit value - either self::TNEF_LVL_MESSAGE or self::TNEF_LVL_ATTACHMENT
$buffer = "";
//mapping between Microsoft Mail IPM classes and those in MAPI
$aClassMap = array(
"IPM.Microsoft Schedule.MtgReq" => "IPM.Schedule.Meeting.Request",
"IPM.Microsoft Schedule.MtgRespP" => "IPM.Schedule.Meeting.Resp.Pos",
"IPM.Microsoft Schedule.MtgRespN" => "IPM.Schedule.Meeting.Resp.Neg",
"IPM.Microsoft Schedule.MtgRespA" => "IPM.Schedule.Meeting.Resp.Tent",
"IPM.Microsoft Schedule.MtgCncl" => "IPM.Schedule.Meeting.Canceled",
"IPM.Microsoft Mail.Non-Delivery" => "Report.IPM.Note.NDR",
"IPM.Microsoft Mail.Read Receipt" => "Report.IPM.Note.IPNRN",
"IPM.Microsoft Mail.Note" => "IPM.Note",
"IPM.Microsoft Mail.Note" => "IPM",
);
//read signature
$hresult = $this->readFromTnefStream($tnefstream, self::DWORD, $signature);
if ($hresult !== NOERROR) {
ZLog::Write(LOGLEVEL_WARN, "TNEF: STREAM:".bin2hex($tnefstream));
ZLog::Write(LOGLEVEL_WARN, "TNEF: There was an error reading tnef signature");
return $hresult;
}
//check signature
if ($signature != self::TNEF_SIGNATURE) {
ZLog::Write(LOGLEVEL_WARN, "TNEF: Corrupt signature.");
return MAPI_E_CORRUPT_DATA;
}
//read key
$hresult = $this->readFromTnefStream($tnefstream, self::WORD, $key);
if ($hresult !== NOERROR) {
ZLog::Write(LOGLEVEL_WARN, "TNEF: There was an error reading tnef key.");
return $hresult;
}
// File is made of blocks, with each a type and size. Component and Key are ignored.
while(1) {
//the stream is empty. exit
if (strlen($tnefstream) == 0) return NOERROR;
//read component - it is either self::TNEF_LVL_MESSAGE or self::TNEF_LVL_ATTACHMENT
$hresult = $this->readFromTnefStream($tnefstream, self::BYTE, $component);
if ($hresult !== NOERROR) {
$hresult = NOERROR; //EOF -> no error
return $hresult;
break;
}
//read type
$hresult = $this->readFromTnefStream($tnefstream, self::DWORD, $type);
if ($hresult !== NOERROR) {
ZLog::Write(LOGLEVEL_WARN, "TNEF: There was an error reading stream property type");
return $hresult;
}
//read size
$hresult = $this->readFromTnefStream($tnefstream, self::DWORD, $size);
if ($hresult !== NOERROR) {
ZLog::Write(LOGLEVEL_WARN, "TNEF: There was an error reading stream property size");
return $hresult;
}
if ($size == 0) {
// do not allocate 0 size data block
ZLog::Write(LOGLEVEL_WARN, "TNEF: Size is 0. Corrupt data.");
return MAPI_E_CORRUPT_DATA;
}
//read buffer
$hresult = $this->readBuffer($tnefstream, $size, $buffer);
if ($hresult !== NOERROR) {
ZLog::Write(LOGLEVEL_WARN, "TNEF: There was an error reading stream property buffer");
return $hresult;
}
//read checksum
$hresult = $this->readFromTnefStream($tnefstream, self::WORD, $checksum);
if ($hresult !== NOERROR) {
ZLog::Write(LOGLEVEL_WARN, "TNEF: There was an error reading stream property checksum.");
return $hresult;
}
// Loop through all the blocks of the TNEF data. We are only interested
// in the properties block for now (0x00069003)
switch ($type) {
case 0x00069003:
$hresult = $this->readMapiProps($buffer, $size, $mapiprops);
if ($hresult !== NOERROR) {
ZLog::Write(LOGLEVEL_WARN, "TNEF: There was an error reading mapi properties' part.");
return $hresult;
}
break;
case 0x00078008: // PR_MESSAGE_CLASS
$msMailClass = trim($buffer);
if (array_key_exists($msMailClass, $aClassMap)) {
$messageClass = $aClassMap[$msMailClass];
}
else {
$messageClass = $msMailClass;
}
$mapiprops[PR_MESSAGE_CLASS] = $messageClass;
break;
case 0x00050008: // PR_OWNER_APPT_ID
$mapiprops[PR_OWNER_APPT_ID] = $buffer;
break;
case 0x00040009: // PR_RESPONSE_REQUESTED
$mapiprops[PR_RESPONSE_REQUESTED] = $buffer;
break;
// --- TNEF attachemnts ---
case 0x00069002:
break;
case 0x00018010: // PR_ATTACH_FILENAME
break;
case 0x00068011: // PR_ATTACH_RENDERING, extra icon information
break;
case 0x0006800f: // PR_ATTACH_DATA_BIN, will be set via OpenProperty() in ECTNEF::Finish()
break;
case 0x00069005: // Attachment property stream
break;
default:
// Ignore this block
break;
}
}
return NOERROR;
}
/**
* Reads a given number of bits from stream and converts them from little indian in a "normal" order. The function
* also cuts the tnef stream after reading.
*
* @param string &$tnefstream
* @param int $bits
* @param array &$element the read element
*
* @access private
* @return int
*/
private function readFromTnefStream(&$tnefstream, $bits, &$element) {
$bytes = $bits / 8;
$part = substr($tnefstream, 0, $bytes);
$packs = array();
switch ($bits) {
case self::DWORD:
$packs = unpack("V", $part);
break;
case self::WORD:
$packs = unpack("v", $part);
break;
case self::BYTE:
$packs[1] = ord($part[0]);
break;
default:
$packs = array();
break;
}
if (empty($packs) || !isset($packs[1])) return MAPI_E_CORRUPT_DATA;
$tnefstream = substr($tnefstream, $bytes);
$element = $packs[1];
return NOERROR;
}
/**
* Reads a given number of bytes from stream and puts them into $element. The function
* also cuts the tnef stream after reading.
*
* @param string &$tnefstream
* @param int $bytes
* @param array &$element the read element
*
* @access private
* @return int
*/
private function readBuffer(&$tnefstream, $bytes, &$element) {
$element = substr($tnefstream, 0, $bytes);
$tnefstream = substr($tnefstream, $bytes);
return NOERROR;
}
/**
* Reads mapi props from buffer into an anrray.
*
* @param string &$buffer
* @param int $size
* @param array &$mapiprops
*
* @access private
* @return int
*/
function readMapiProps(&$buffer, $size, &$mapiprops) {
$nrprops = 0;
//get number of mapi properties
$hresult = $this->readFromTnefStream($buffer, self::DWORD, $nrprops);
if ($hresult !== NOERROR) {
ZLog::Write(LOGLEVEL_WARN, "TNEF: There was an error getting the number of mapi properties in stream.");
return $hresult;
}
$size -= 4;
ZLog::Write(LOGLEVEL_DEBUG, "TNEF: nrprops:$nrprops");
//loop through all the properties and add them to our internal list
while($nrprops) {
$hresult = $this->readSingleMapiProp($buffer, $size, $read, $mapiprops);
if ($hresult !== NOERROR) {
ZLog::Write(LOGLEVEL_WARN, "TNEF: There was an error reading a mapi property.");
ZLog::Write(LOGLEVEL_WARN, "TNEF: result: " . sprintf("0x%X", $hresult));
return $hresult;
}
$nrprops--;
}
return NOERROR;
}
/**
* Reads a single mapi prop.
*
* @param string &$buffer
* @param int $size
* @param mixed &$read
* @param array &$mapiprops
*
* @access private
* @return int
*/
private function readSingleMapiProp(&$buffer, &$size, &$read, &$mapiprops) {
$propTag = 0;
$len = 0;
$origSize = $size;
$isNamedId = 0;
$namedProp = 0;
$count = 0;
$mvProp = 0;
$guid = 0;
if($size < 8) {
return MAPI_E_NOT_FOUND;
}
$hresult = $this->readFromTnefStream($buffer, self::DWORD, $propTag);
if ($hresult !== NOERROR) {
ZLog::Write(LOGLEVEL_WARN, "TNEF: There was an error reading a mapi property tag from the stream.");
return $hresult;
}
$size -= 4;
ZLog::Write(LOGLEVEL_DEBUG, "TNEF: mapi prop type:".dechex(mapi_prop_type($propTag)));
ZLog::Write(LOGLEVEL_DEBUG, "TNEF: mapi prop tag: 0x".sprintf("%04x", mapi_prop_id($propTag)));
if (mapi_prop_id($propTag) >= 0x8000) {
// Named property, first read GUID, then name/id
if($size < 24) {
ZLog::Write(LOGLEVEL_WARN, "TNEF: Corrupt guid size for named property:".dechex($propTag));
return MAPI_E_CORRUPT_DATA;
}
//strip GUID & name/id
$hresult = $this->readBuffer($buffer, 16, $guid);
if ($hresult !== NOERROR) {
ZLog::Write(LOGLEVEL_WARN, "TNEF: There was an error reading stream property buffer");
return $hresult;
}
$size -= 16;
//it is not used and is here only for eventual debugging
$readableGuid = unpack("VV/v2v/n4n", $guid);
$readableGuid = sprintf("{%08x-%04x-%04x-%04x-%04x%04x%04x}",$readableGuid['V'], $readableGuid['v1'], $readableGuid['v2'],$readableGuid['n1'],$readableGuid['n2'],$readableGuid['n3'],$readableGuid['n4']);
ZLog::Write(LOGLEVEL_DEBUG, "TNEF: guid:$readableGuid");
$hresult = $this->readFromTnefStream($buffer, self::DWORD, $isNamedId);
if ($hresult !== NOERROR) {
ZLog::Write(LOGLEVEL_WARN, "TNEF: There was an error reading stream property checksum.");
return $hresult;
}
$size -= 4;
if($isNamedId != 0) {
// A string name follows
//read length of the property
$hresult = $this->readFromTnefStream($buffer, self::DWORD, $len);
if ($hresult !== NOERROR) {
ZLog::Write(LOGLEVEL_WARN, "TNEF: There was an error reading mapi property's length");
return $hresult;
}
$size -= 4;
if ($size < $len) {
return MAPI_E_CORRUPT_DATA;
}
//read the name of the property, eg Keywords
$hresult = $this->readBuffer($buffer, $len, $namedProp);
if ($hresult !== NOERROR) {
ZLog::Write(LOGLEVEL_WARN, "TNEF: There was an error reading stream property buffer");
return $hresult;
}
$size -= $len;
//Re-align
$buffer = substr($buffer, ($len & 3 ? 4 - ($len & 3) : 0));
$size -= $len & 3 ? 4 - ($len & 3) : 0;
}
else {
$hresult = $this->readFromTnefStream($buffer, self::DWORD, $namedProp);
if ($hresult !== NOERROR) {
ZLog::Write(LOGLEVEL_WARN, "TNEF: There was an error reading mapi property's length");
return $hresult;
}
ZLog::Write(LOGLEVEL_DEBUG, "TNEF: named: 0x".sprintf("%04x", $namedProp));
$size -= 4;
}
if ($this->store !== false) {
$named = mapi_getidsfromnames($this->store, array($namedProp), array(makeguid($readableGuid)));
$propTag = mapi_prop_tag(mapi_prop_type($propTag), mapi_prop_id($named[0]));
}
else {
ZLog::Write(LOGLEVEL_WARN, "TNEF: Store not available. It is impossible to get named properties");
}
}
ZLog::Write(LOGLEVEL_DEBUG, "TNEF: mapi prop tag: 0x".sprintf("%04x", mapi_prop_id($propTag))." ".sprintf("%04x", mapi_prop_type($propTag)));
if($propTag & MV_FLAG) {
if($size < 4) {
return MAPI_E_CORRUPT_DATA;
}
//read the number of properties
$hresult = $this->readFromTnefStream($buffer, self::DWORD, $count);
if ($hresult !== NOERROR) {
ZLog::Write(LOGLEVEL_WARN, "TNEF: There was an error reading number of properties for:".dechex($propTag));
return $hresult;
}
$size -= 4;
}
else {
$count = 1;
}
for ($mvProp = 0; $mvProp < $count; $mvProp++) {
switch(mapi_prop_type($propTag) & ~MV_FLAG ) {
case PT_I2:
case PT_LONG:
$hresult = $this->readBuffer($buffer, 4, $value);
if ($hresult !== NOERROR) {
ZLog::Write(LOGLEVEL_WARN, "TNEF: There was an error reading stream property buffer");
return $hresult;
}
$value = unpack("V", $value);
$value = intval($value[1], 16);
if($propTag & MV_FLAG) {
$mapiprops[$propTag][] = $value;
}
else {
$mapiprops[$propTag] = $value;
}
$size -= 4;
ZLog::Write(LOGLEVEL_DEBUG, "TNEF: int or long propvalue:".$value);
break;
case PT_R4:
if($propTag & MV_FLAG) {
$hresult = $this->readBuffer($buffer, 4, $mapiprops[$propTag][]);
if ($hresult !== NOERROR) {
ZLog::Write(LOGLEVEL_WARN, "TNEF: There was an error reading stream property buffer");
return $hresult;
}
}
else {
$hresult = $this->readBuffer($buffer, 4, $mapiprops[$propTag]);
if ($hresult !== NOERROR) {
ZLog::Write(LOGLEVEL_WARN, "TNEF: There was an error reading stream property buffer");
return $hresult;
}
}
$size -= 4;
ZLog::Write(LOGLEVEL_DEBUG, "TNEF: propvalue:".$mapiprops[$propTag]);
break;
case PT_BOOLEAN:
$hresult = $this->readBuffer($buffer, 4, $mapiprops[$propTag]);
if ($hresult !== NOERROR) {
ZLog::Write(LOGLEVEL_WARN, "TNEF: There was an error reading stream property buffer");
return $hresult;
}
$size -= 4;
//reported by dw2412
//cast to integer as it evaluates to 1 or 0 because
//a non empty string evaluates to true :(
$mapiprops[$propTag] = (integer) bin2hex($mapiprops[$propTag]{0});
ZLog::Write(LOGLEVEL_DEBUG, "TNEF: propvalue:".$mapiprops[$propTag]);
break;
case PT_SYSTIME:
if($size < 8) {
return MAPI_E_CORRUPT_DATA;
}
if($propTag & MV_FLAG) {
$hresult = $this->readBuffer($buffer, 8, $mapiprops[$propTag][]);
if ($hresult !== NOERROR) {
ZLog::Write(LOGLEVEL_WARN, "TNEF: There was an error reading stream property buffer");
return $hresult;
}
}
else {
$hresult = $this->readBuffer($buffer, 8, $mapiprops[$propTag]);
if ($hresult !== NOERROR) {
ZLog::Write(LOGLEVEL_WARN, "TNEF: There was an error reading stream property buffer");
return $hresult;
}
}
//we have to convert the filetime to an unixtime timestamp
$filetime = unpack("V2v", $mapiprops[$propTag]);
//php on 64-bit systems converts unsigned values differently than on 32 bit systems
//we need this "fix" in order to get the same values on both types of systems
$filetime['v2'] = substr(sprintf("%08x",$filetime['v2']), -8);
$filetime['v1'] = substr(sprintf("%08x",$filetime['v1']), -8);
$filetime = hexdec($filetime['v2'].$filetime['v1']);
$filetime = ($filetime - 116444736000000000) / 10000000;
$mapiprops[$propTag] = $filetime;
// we have to set the start and end times separately because the standard PR_START_DATE and PR_END_DATE aren't enough
if ($propTag == PR_START_DATE) {
$mapiprops[$this->props["starttime"]] = $mapiprops[$this->props["commonstart"]] = $filetime;
}
if ($propTag == PR_END_DATE) {
$mapiprops[$this->props["endtime"]] = $mapiprops[$this->props["commonend"]] = $filetime;
}
$size -= 8;
ZLog::Write(LOGLEVEL_DEBUG, "TNEF: propvalue:".$mapiprops[$propTag]);
break;
case PT_DOUBLE:
case PT_CURRENCY:
case PT_I8:
case PT_APPTIME:
if($size < 8) {
return MAPI_E_CORRUPT_DATA;
}
if($propTag & MV_FLAG) {
$hresult = $this->readBuffer($buffer, 8, $mapiprops[$propTag][]);
if ($hresult !== NOERROR) {
ZLog::Write(LOGLEVEL_WARN, "TNEF: There was an error reading stream property buffer");
return $hresult;
}
}
else {
$hresult = $this->readBuffer($buffer, 8, $mapiprops[$propTag]);
if ($hresult !== NOERROR) {
ZLog::Write(LOGLEVEL_WARN, "TNEF: There was an error reading stream property buffer");
return $hresult;
}
}
$size -= 8;
ZLog::Write(LOGLEVEL_DEBUG, "TNEF: propvalue:".$mapiprops[$propTag]);
break;
case PT_STRING8:
if($size < 8) {
return MAPI_E_CORRUPT_DATA;
}
// Skip next 4 bytes, it's always '1' (ULONG)
$buffer = substr($buffer, 4);
$size -= 4;
//read length of the property
$hresult = $this->readFromTnefStream($buffer, self::DWORD, $len);
if ($hresult !== NOERROR) {
ZLog::Write(LOGLEVEL_WARN, "TNEF: There was an error reading mapi property's length");
return $hresult;
}
$size -= 4;
if ($size < $len) {
return MAPI_E_CORRUPT_DATA;
}
if ($propTag & MV_FLAG) {
$hresult = $this->readBuffer($buffer, $len, $mapiprops[$propTag][]);
if ($hresult !== NOERROR) {
ZLog::Write(LOGLEVEL_WARN, "TNEF: There was an error reading stream property buffer");
return $hresult;
}
}
else {
$hresult = $this->readBuffer($buffer, $len, $mapiprops[$propTag]);
if ($hresult !== NOERROR) {
ZLog::Write(LOGLEVEL_WARN, "TNEF: There was an error reading stream property buffer");
return $hresult;
}
}
//location fix. it looks like tnef uses this value for location
if (mapi_prop_id($propTag) == 0x8342) {
$mapiprops[$this->props["location"]] = $mapiprops[$propTag];
unset($mapiprops[$propTag]);
}
$size -= $len;
//Re-align
$buffer = substr($buffer, ($len & 3 ? 4 - ($len & 3) : 0));
$size -= $len & 3 ? 4 - ($len & 3) : 0;
ZLog::Write(LOGLEVEL_DEBUG, "TNEF: propvalue:".$mapiprops[$propTag]);
break;
case PT_UNICODE:
if($size < 8) {
return MAPI_E_CORRUPT_DATA;
}
// Skip next 4 bytes, it's always '1' (ULONG)
$buffer = substr($buffer, 4);
$size -= 4;
//read length of the property
$hresult = $this->readFromTnefStream($buffer, self::DWORD, $len);
if ($hresult !== NOERROR) {
ZLog::Write(LOGLEVEL_WARN, "TNEF: There was an error reading mapi property's length");
return $hresult;
}
$size -= 4;
if ($size < $len) {
return MAPI_E_CORRUPT_DATA;
}
//currently unicode strings are not supported bz mapi_setprops, so we'll use PT_STRING8
$propTag = mapi_prop_tag(PT_STRING8, mapi_prop_id($propTag));
if ($propTag & MV_FLAG) {
$hresult = $this->readBuffer($buffer, $len, $mapiprops[$propTag][]);
if ($hresult !== NOERROR) {
ZLog::Write(LOGLEVEL_WARN, "TNEF: There was an error reading stream property buffer");
return $hresult;
}
}
else {
$hresult = $this->readBuffer($buffer, $len, $mapiprops[$propTag]);
if ($hresult !== NOERROR) {
ZLog::Write(LOGLEVEL_WARN, "TNEF: There was an error reading stream property buffer");
return $hresult;
}
}
//location fix. it looks like tnef uses this value for location
if (mapi_prop_id($propTag) == 0x8342) {
$mapiprops[$this->props["location"]] = iconv("UCS-2","windows-1252", $mapiprops[$propTag]);
unset($mapiprops[$propTag]);
}
//convert from unicode to windows encoding
if (isset($mapiprops[$propTag])) $mapiprops[$propTag] = iconv("UCS-2","windows-1252", $mapiprops[$propTag]);
$size -= $len;
//Re-align
$buffer = substr($buffer, ($len & 3 ? 4 - ($len & 3) : 0));
$size -= $len & 3 ? 4 - ($len & 3) : 0;
if (isset($mapiprops[$propTag])) ZLog::Write(LOGLEVEL_DEBUG, "TNEF: propvalue:".$mapiprops[$propTag]);
break;
case PT_OBJECT: // PST sends PT_OBJECT data. Treat as PT_BINARY
case PT_BINARY:
if($size < self::BYTE) {
return MAPI_E_CORRUPT_DATA;
}
// Skip next 4 bytes, it's always '1' (ULONG)
$buffer = substr($buffer, 4);
$size -= 4;
//read length of the property
$hresult = $this->readFromTnefStream($buffer, self::DWORD, $len);
if ($hresult !== NOERROR) {
ZLog::Write(LOGLEVEL_WARN, "TNEF: There was an error reading mapi property's length");
return $hresult;
}
$size -= 4;
if (mapi_prop_type($propTag) == PT_OBJECT) {
// IMessage guid [ 0x00020307 C000 0000 0000 0000 00 00 00 46 ]
$buffer = substr($buffer, 16);
$size -= 16;
$len -= 16;
}
if ($size < $len) {
return MAPI_E_CORRUPT_DATA;
}
if ($propTag & MV_FLAG) {
$hresult = $this->readBuffer($buffer, $len, $mapiprops[$propTag][]);
if ($hresult !== NOERROR) {
ZLog::Write(LOGLEVEL_WARN, "TNEF: There was an error reading stream property buffer");
return $hresult;
}
}
else {
$hresult = $this->readBuffer($buffer, $len, $mapiprops[$propTag]);
if ($hresult !== NOERROR) {
ZLog::Write(LOGLEVEL_WARN, "TNEF: There was an error reading stream property buffer");
return $hresult;
}
}
$size -= $len;
//Re-align
$buffer = substr($buffer, ($len & 3 ? 4 - ($len & 3) : 0));
$size -= $len & 3 ? 4 - ($len & 3) : 0;
ZLog::Write(LOGLEVEL_DEBUG, "TNEF: propvalue:".bin2hex($mapiprops[$propTag]));
break;
default:
return MAPI_E_INVALID_PARAMETER;
break;
}
}
return NOERROR;
}
}

File diff suppressed because it is too large Load diff

View file

@ -1,6 +0,0 @@
{
"autoload": {
"classmap": ["autodiscover/", "include/", "lib/"],
"files": ["version.php", "lib/core/zpush-utils.php", "lib/core/zpushdefs.php", "lib/utils/compat.php"]
}
}

View file

@ -1,369 +0,0 @@
<?php
/***********************************************
* File : config.php
* Project : Z-Push
* Descr : Main configuration file
*
* Created : 01.10.2007
*
* 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 <http://www.gnu.org/licenses/>.
*
* Consult LICENSE file for details
************************************************/
/**********************************************************************************
* Default settings
*/
// Defines the default time zone, change e.g. to "Europe/London" if necessary
define('TIMEZONE', '');
// Defines the base path on the server
define('BASE_PATH', dirname(__FILE__) . '/');
// Try to set unlimited timeout
define('SCRIPT_TIMEOUT', 0);
// Your PHP could have a bug when base64 encoding: https://bugs.php.net/bug.php?id=68532
// NOTE: Run "php testing/testing-bug68532fixed.php" to know what value put here
define('BUG68532FIXED', true);
// When accessing through a proxy, the "X-Forwarded-For" header contains the original remote IP
define('USE_X_FORWARDED_FOR_HEADER', false);
// When using client certificates, we can check if the login sent matches the owner of the certificate.
// This setting specifies the owner parameter in the certificate to look at.
define("CERTIFICATE_OWNER_PARAMETER", "SSL_CLIENT_S_DN_CN");
// Location of the trusted CA, e.g. '/etc/ssl/certs/EmailCA.pem'
// Uncomment and modify the following line if the validation of the certificates fails.
// define('CAINFO', '/etc/ssl/certs/EmailCA.pem');
/*
* Whether to use the complete email address as a login name
* (e.g. user@company.com) or the username only (user).
* This is required for Z-Push to work properly after autodiscover.
* Possible values:
* false - use the username only (default).
* true - use the complete email address.
*/
define('USE_FULLEMAIL_FOR_LOGIN', true);
/**********************************************************************************
* Device pre-authorization. Useful when using Z-Push as a standalone product.
*
* It will use the STATE_MACHINE specified below, to store the users/devices
* FILE => STATE_DIR/PreAuthUserDevices
* SQL => auth_users
*
* FALSE => default
* TRUE
*/
define('PRE_AUTHORIZE_USERS', false);
// New users are pre-authorized automatically
define('PRE_AUTHORIZE_NEW_USERS', false);
// New devices are pre-authorized automatically for pre-authorized users
define('PRE_AUTHORIZE_NEW_DEVICES', false);
// Max number of devices pre-authorized for user, you can pre-authorize more manually
define('PRE_AUTHORIZE_MAX_DEVICES', 5);
/**********************************************************************************
* Select StateMachine mechanism
*
* FILE => FileStateMachine, default
* SQL => SqlStateMachine
*/
define('STATE_MACHINE', 'FILE');
/**********************************************************************************
* Default FileStateMachine settings
*/
define('STATE_DIR', '/var/lib/z-push/');
/**********************************************************************************
* Optional SqlStateMachine settings
*
* DSN: formatted PDO connection string
* mysql:host=xxx;port=xxx;dbname=xxx
* DON'T FORGET TO INSTALL THE PHP-DRIVER PACKAGE!!!
* USER: username to DB
* PASSWORD: password to DB
* OPTIONS: array with options needed
*/
define('STATE_SQL_DSN', '');
define('STATE_SQL_USER', '');
define('STATE_SQL_PASSWORD', '');
define('STATE_SQL_OPTIONS', serialize(array(PDO::ATTR_PERSISTENT => true)));
/**********************************************************************************
* Logging settings
* Possible LOGLEVEL and LOGUSERLEVEL values are:
* LOGLEVEL_OFF - no logging
* LOGLEVEL_FATAL - log only critical errors
* LOGLEVEL_ERROR - logs events which might require corrective actions
* LOGLEVEL_WARN - might lead to an error or require corrective actions in the future
* LOGLEVEL_INFO - usually completed actions
* LOGLEVEL_DEBUG - debugging information, typically only meaningful to developers
* LOGLEVEL_WBXML - also prints the WBXML sent to/from the device
* LOGLEVEL_DEVICEID - also prints the device id for every log entry
* LOGLEVEL_WBXMLSTACK - also prints the contents of WBXML stack
*
* The verbosity increases from top to bottom. More verbose levels include less verbose
* ones, e.g. setting to LOGLEVEL_DEBUG will also output LOGLEVEL_FATAL, LOGLEVEL_ERROR,
* LOGLEVEL_WARN and LOGLEVEL_INFO level entries.
*/
define('LOGFILEDIR', '/var/log/z-push/');
define('LOGFILE', LOGFILEDIR . 'z-push.log');
define('LOGERRORFILE', LOGFILEDIR . 'z-push-error.log');
define('LOGLEVEL', LOGLEVEL_INFO);
define('LOGAUTHFAIL', false);
// To save e.g. WBXML data only for selected users, add the usernames to the array
// The data will be saved into a dedicated file per user in the LOGFILEDIR
// Users have to be encapusulated in quotes, several users are comma separated, like:
// $specialLogUsers = array('info@domain.com', 'myusername');
define('LOGUSERLEVEL', LOGLEVEL_DEVICEID);
$specialLogUsers = array();
// If you want to disable log to file, and log to syslog instead
define('LOG_SYSLOG_ENABLED', false);
// false will log to local syslog, otherwise put the remote syslog IP here
define('LOG_SYSLOG_HOST', false);
// Syslog port
define('LOG_SYSLOG_PORT', 514);
// Program showed in the syslog. Useful if you have more than one instance login to the same syslog
define('LOG_SYSLOG_PROGRAM', '[z-push]');
define('LOG_MEMORY_PROFILER', true);
define('LOG_MEMORY_PROFILER_FILE', '/var/log/z-push/memory_profile');
/**********************************************************************************
* Mobile settings
*/
// Device Provisioning
define('PROVISIONING', true);
// This option allows the 'loose enforcement' of the provisioning policies for older
// devices which don't support provisioning (like WM 5 and HTC Android Mail) - dw2412 contribution
// false (default) - Enforce provisioning for all devices
// true - allow older devices, but enforce policies on devices which support it
define('LOOSE_PROVISIONING', false);
// Default conflict preference
// Some devices allow to set if the server or PIM (mobile)
// should win in case of a synchronization conflict
// SYNC_CONFLICT_OVERWRITE_SERVER - Server is overwritten, PIM wins
// SYNC_CONFLICT_OVERWRITE_PIM - PIM is overwritten, Server wins (default)
define('SYNC_CONFLICT_DEFAULT', SYNC_CONFLICT_OVERWRITE_PIM);
// Global limitation of items to be synchronized
// The mobile can define a sync back period for calendar and email items
// For large stores with many items the time period could be limited to a max value
// If the mobile transmits a wider time period, the defined max value is used
// Applicable values:
// SYNC_FILTERTYPE_ALL (default, no limitation)
// SYNC_FILTERTYPE_1DAY, SYNC_FILTERTYPE_3DAYS, SYNC_FILTERTYPE_1WEEK, SYNC_FILTERTYPE_2WEEKS,
// SYNC_FILTERTYPE_1MONTH, SYNC_FILTERTYPE_3MONTHS, SYNC_FILTERTYPE_6MONTHS
define('SYNC_FILTERTIME_MAX', SYNC_FILTERTYPE_ALL);
// Interval in seconds before checking if there are changes on the server when in Ping.
// It means the highest time span before a change is pushed to a mobile. Set it to
// a higher value if you have a high load on the server.
define('PING_INTERVAL', 30);
// Interval in seconds to force a re-check of potentially missed notifications when
// using a changes sink. Default are 300 seconds (every 5 min).
// This can also be disabled by setting it to false
define('SINK_FORCERECHECK', 300);
// Set the fileas (save as) order for contacts in the webaccess/webapp/outlook.
// It will only affect new/modified contacts on the mobile which then are synced to the server.
// Possible values are:
// SYNC_FILEAS_FIRSTLAST - fileas will be "Firstname Middlename Lastname"
// SYNC_FILEAS_LASTFIRST - fileas will be "Lastname, Firstname Middlename"
// SYNC_FILEAS_COMPANYONLY - fileas will be "Company"
// SYNC_FILEAS_COMPANYLAST - fileas will be "Company (Lastname, Firstname Middlename)"
// SYNC_FILEAS_COMPANYFIRST - fileas will be "Company (Firstname Middlename Lastname)"
// SYNC_FILEAS_LASTCOMPANY - fileas will be "Lastname, Firstname Middlename (Company)"
// SYNC_FILEAS_FIRSTCOMPANY - fileas will be "Firstname Middlename Lastname (Company)"
// The company-fileas will only be set if a contact has a company set. If one of
// company-fileas is selected and a contact doesn't have a company set, it will default
// to SYNC_FILEAS_FIRSTLAST or SYNC_FILEAS_LASTFIRST (depending on if last or first
// option is selected for company).
// If SYNC_FILEAS_COMPANYONLY is selected and company of the contact is not set
// SYNC_FILEAS_LASTFIRST will be used
define('FILEAS_ORDER', SYNC_FILEAS_LASTFIRST);
// Amount of items to be synchronized per request
// Normally this value is requested by the mobile. Common values are 5, 25, 50 or 100.
// Exporting too much items can cause mobile timeout on busy systems.
// Z-Push will use the lowest value, either set here or by the mobile.
// default: 100 - value used if mobile does not limit amount of items
define('SYNC_MAX_ITEMS', 100);
// The devices usually send a list of supported properties for calendar and contact
// items. If a device does not includes such a supported property in Sync request,
// it means the property's value will be deleted on the server.
// However some devices do not send a list of supported properties. It is then impossible
// to tell if a property was deleted or it was not set at all if it does not appear in Sync.
// This parameter defines Z-Push behaviour during Sync if a device does not issue a list with
// supported properties.
// See also https://jira.zarafa.com/browse/ZP-302.
// Possible values:
// false - do not unset properties which are not sent during Sync (default)
// true - unset properties which are not sent during Sync
define('UNSET_UNDEFINED_PROPERTIES', false);
// ActiveSync specifies that a contact photo may not exceed 48 KB. This value is checked
// in the semantic sanity checks and contacts with larger photos are not synchronized.
// This limitation is not being followed by the ActiveSync clients which set much bigger
// contact photos. You can override the default value of the max photo size.
// default: 5242880 - 5 MB default max photo size in bytes
define('SYNC_CONTACTS_MAXPICTURESIZE', 5242880);
// Over the WebserviceUsers command it is possible to retrieve a list of all
// known devices and users on this Z-Push system. The authenticated user needs to have
// admin rights and a public folder must exist.
// In multicompany environments this enable an admin user of any company to retrieve
// this full list, so this feature is disabled by default. Enable with care.
define('ALLOW_WEBSERVICE_USERS_ACCESS', false);
// Users with many folders can use the 'partial foldersync' feature, where the server
// actively stops processing the folder list if it takes too long. Other requests are
// then redirected to the FolderSync to synchronize the remaining items.
// Device compatibility for this procedure is not fully understood.
// NOTE: THIS IS AN EXPERIMENTAL FEATURE WHICH COULD PREVENT YOUR MOBILES FROM SYNCHRONIZING.
define('USE_PARTIAL_FOLDERSYNC', false);
/**********************************************************************************
* Backend settings
*/
// the backend data provider
define('BACKEND_PROVIDER', '');
// top collector backend class name
// Default is: TopCollector
// Options: ["TopCollector", "TopCollectorRedis"]
define('TOP_COLLECTOR_BACKEND', 'TopCollector');
// ping tracking backend class name
// Default is: PingTracking
// Options: ["PingTracking", "PingTrackingRedis"]
define('PING_TRACKING_BACKEND', 'PingTracking');
// loop detection backend class name
// Default is: LoopDetection
// Options: ["LoopDetection", "LoopDetectionRedis"]
define('LOOP_DETECTION_BACKEND', 'LoopDetection');
// If using the Redis backends (for top, ping and lookp) make sure to set this values as necessary
define('IPC_REDIS_IP', '127.0.0.1');
define('IPC_REDIS_PORT', 6379);
// Database name/index in Redis: 0 by default
// NOTE: this database must be exclusive for z-push, since its content will be ERASED. You are warned.
define('IPC_REDIS_DATABASE', 0);
/**********************************************************************************
* Search provider settings
*
* Alternative backend to perform SEARCH requests (GAL search)
* By default the main Backend defines the preferred search functionality.
* If set, the Search Provider will always be preferred.
* Use 'BackendSearchLDAP' to search in a LDAP directory (see backend/searchldap/config.php)
*/
define('SEARCH_PROVIDER', '');
// Time in seconds for the server search. Setting it too high might result in timeout.
// Setting it too low might not return all results. Default is 10.
define('SEARCH_WAIT', 10);
// The maximum number of results to send to the client. Setting it too high
// might result in timeout. Default is 10.
define('SEARCH_MAXRESULTS', 10);
/**********************************************************************************
* Synchronize additional folders to all mobiles
*
* With this feature, special folders can be synchronized to all mobiles.
* This is useful for e.g. global company contacts.
*
* This feature is supported only by certain devices, like iPhones.
* Check the compatibility list for supported devices:
* http://z-push.sf.net/compatibility
*
* To synchronize a folder, add a section setting all parameters as below:
* store: the ressource where the folder is located.
* Zarafa users use 'SYSTEM' for the 'Public Folder'
* folderid: folder id of the folder to be synchronized
* name: name to be displayed on the mobile device
* type: supported types are:
* SYNC_FOLDER_TYPE_USER_CONTACT
* SYNC_FOLDER_TYPE_USER_APPOINTMENT
* SYNC_FOLDER_TYPE_USER_TASK
* SYNC_FOLDER_TYPE_USER_MAIL
*
* Additional notes:
* - on Zarafa systems use backend/zarafa/listfolders.php script to get a list
* of available folders
*
* - all Z-Push users must have full writing permissions (secretary rights) so
* the configured folders can be synchronized to the mobile
*
* - this feature is only partly suitable for multi-tenancy environments,
* as ALL users from ALL tenents need access to the configured store & folder.
* When configuring a public folder, this will cause problems, as each user has
* a different public folder in his tenant, so the folder are not available.
* - changing this configuration could cause HIGH LOAD on the system, as all
* connected devices will be updated and load the data contained in the
* added/modified folders.
*/
$additionalFolders = array(
// demo entry for the synchronization of contacts from the public folder.
// uncomment (remove '/*' '*/') and fill in the folderid
/*
array(
'store' => "SYSTEM",
'folderid' => "",
'name' => "Public Contacts",
'type' => SYNC_FOLDER_TYPE_USER_CONTACT,
),
*/
);

View file

@ -1,103 +0,0 @@
# Docker Images
You can run a Z-Push server using Docker containers. That is really usefull for developing, but it also can be used in production servers.
Here are the basic instructions for a Nginx+PHP-FPM deployment. Feel free to contribute your Apache or other server approach.
## Using Docker Composer (In progress)
### Create and build basic images
docker-compose -f basic.yml up
## Manual method
### Build a PHP-FPM image
cd php-fpm
docker build -t fmbiete/centos_zpush_php_fpm .
### Build a NGINX image
cd nginx
docker build -t fmbiete/centos_zpush_nginx .
**NOTE**: this includes a SSL self-signed certificate (2048 bytes - valid until 2025), but it's intended only for development or testing uses. In production replace it with a real one.
### Create MariaDB container (optional for SQLStateMachine)
docker run --name zpush_mariadb -e MYSQL_ROOT_PASSWORD=root_password -e MYSQL_USER=user_name -e MYSQL_PASSWORD=user_password -e MYSQL_DATABASE=database -v mariadb_lib:/var/lib/mysql -p3306:3306 -d fbiete/centos_epel_mariadb:10
**TODO**: Replace *mariadb_lib* with the full path when you will store the database files
**TODO**: If using selinux remember to change the context type for *mariadb_lib*
**TODO**: Replace *root_password*, *user_name*, *user_password*, *database* with the right values
#### Load database schema
mysql -u root -proot_password database -h 127.0.0.1 < sql/mysql.sql
### Create Redis container (optional for TopCollectorRedis, LoopDetectionRedis or PingTrackingRedis)
docker run --name zpush_redis -v redis_data:/data -p 6379:6379 -d fbiete/centos_epel_redis:2.8
**TODO**: Replace *redis_data* with the full path when you will store the database files
**TODO**: If using selinux remember to change the context type for *redis_data*
### Create PHP-FPM container
docker run -d --name zpush_php_fpm -v zpush_repo:/var/www/z-push fmbiete/centos_zpush_php_fpm
#### With MariaDB
docker run -d --name zpush_php_fpm -v zpush_repo:/var/www/z-push --link zpush_mariadb:zpushmariadb fmbiete/centos_zpush_php_fpm
#### With Redis
docker run -d --name zpush_php_fpm -v zpush_repo:/var/www/z-push --link zpush_redis:zpushredis fmbiete/centos_zpush_php_fpm
**TODO**: Replace *zpush_repo* with the full path to Z-Push code
**TODO**: Remember to zpushmariadb and zpushredis as server name in the config for MariaDB and Redis
### Create NGINX container
docker run -d --name zpush_nginx -v zpush_repo:/var/www/z-push --link zpush_php_fpm:zpushphpfpm -p 443:443 fmbiete/centos_zpush_nginx
**TODO**: Replace *zpush_repo* with the full path to Z-Push code
### Stop containers
docker stop zpush_nginx
docker stop zpush_php_fpm
docker stop zpush_mariadb
docker stop zpush_redis
### Start containers
docker start zpush_mariadb
docker start zpush_redis
docker start zpush_php_fpm
docker start zpush_nginx
### Remove containers
docker rm zpush_nginx
docker rm zpush_php_fpm
docker rm zpush_mariadb
docker rm zpush_redis
**NOTE**: The order of the containers in the operation is important

View file

@ -1,13 +0,0 @@
phpfpm:
build: php-fpm/
volumes:
- ..:/var/www/z-push:Z
nginx:
build: nginx/
volumes_from:
- phpfpm
links:
- phpfpm:zpushphpfpm
ports:
- "80:80"
- "443:443"

View file

@ -1,16 +0,0 @@
FROM fbiete/centos_epel_nginx:1.8
MAINTAINER Francisco Miguel Biete <fbiete@gmail.com>
RUN mkdir /var/www /var/www/z-push /etc/ssl/nginx \
&& chown -R nginx:nginx /var/www
COPY localhost.crt /etc/ssl/nginx/localhost.crt
COPY localhost.key /etc/ssl/nginx/localhost.key
COPY nginx.conf /etc/nginx/
VOLUME /var/www/z-push
CMD [ "nginx", "-g", "daemon off;" ]

View file

@ -1,21 +0,0 @@
-----BEGIN CERTIFICATE-----
MIIDfzCCAmegAwIBAgIJAMNks+T7RrPhMA0GCSqGSIb3DQEBCwUAMFYxCzAJBgNV
BAYTAlhYMRUwEwYDVQQHDAxEZWZhdWx0IENpdHkxHDAaBgNVBAoME0RlZmF1bHQg
Q29tcGFueSBMdGQxEjAQBgNVBAMMCWxvY2FsaG9zdDAeFw0xNTAzMTYwNzQwMzZa
Fw0yNTAzMTMwNzQwMzZaMFYxCzAJBgNVBAYTAlhYMRUwEwYDVQQHDAxEZWZhdWx0
IENpdHkxHDAaBgNVBAoME0RlZmF1bHQgQ29tcGFueSBMdGQxEjAQBgNVBAMMCWxv
Y2FsaG9zdDCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBALhw96+bbz0I
5tPNAUJo/TG3Qi8RzkHyeGeg+dbMckS2Y3gcaa4E8nsnuKisbmHZu6Zgc7P8sr9d
V6qTsz07aJuy5pdt3+y9oiHPd05EZ4aZSXbfdGLAwr94D6R3AI6zv6lA+cCAHIIu
A4tgOmjtykre632dDDVLyDyxVTekn28q6ag+6vDnj9gyABsvER7WsJpi1Af6HxH2
/tM1EtCKam5SNVy9+lQs3/pXk8r8kKvKVyrewhTzy4F8IRVi0vXtcW7wtkDwO1Ti
+CN1C1ETQZ2jfTk7Z9xGaFbS5cIEbHH3AmBgJjT396pUBQEqQVHBsHxvmhFhKMBi
ejvFbTYFz8cCAwEAAaNQME4wHQYDVR0OBBYEFECo2oRuFvk9sUOwRzZ+BeH48YJR
MB8GA1UdIwQYMBaAFECo2oRuFvk9sUOwRzZ+BeH48YJRMAwGA1UdEwQFMAMBAf8w
DQYJKoZIhvcNAQELBQADggEBAK5ZATJ3Oh+0bXXdPMSTCZDgsGYpm5/BrUiAbqXX
mMRWyx6nUF6QqDu6Fku+Jgo0RwhXz7VfwI1JNXWvDsoEnjCbWJ2+njH08qBn9Xex
wtxL+kwnjXVeZgskUa9sAP+nr2gyzhjyRFjx1W3gZQeJ9VY2pDKLpW2NTkUOEhOH
YzLSUzVlXdQUauiYglzqip7dUId5VeDXHC8merB7Iq8h7QxD0WVHyjlgSjWEH8Gq
MDaY+n6CyPXkmusNJlQoWB/CJcLfr3tSVvaqmZ49K3OZph3DCKiGnSqqFi5OqKLg
YkcculYQGwfUkqZPqTb++MTsKkuaQPk4UDbPYAYhHJnBT+A=
-----END CERTIFICATE-----

View file

@ -1,27 +0,0 @@
-----BEGIN RSA PRIVATE KEY-----
MIIEpAIBAAKCAQEAuHD3r5tvPQjm080BQmj9MbdCLxHOQfJ4Z6D51sxyRLZjeBxp
rgTyeye4qKxuYdm7pmBzs/yyv11XqpOzPTtom7Lml23f7L2iIc93TkRnhplJdt90
YsDCv3gPpHcAjrO/qUD5wIAcgi4Di2A6aO3KSt7rfZ0MNUvIPLFVN6SfbyrpqD7q
8OeP2DIAGy8RHtawmmLUB/ofEfb+0zUS0IpqblI1XL36VCzf+leTyvyQq8pXKt7C
FPPLgXwhFWLS9e1xbvC2QPA7VOL4I3ULURNBnaN9OTtn3EZoVtLlwgRscfcCYGAm
NPf3qlQFASpBUcGwfG+aEWEowGJ6O8VtNgXPxwIDAQABAoIBACbhMWUkN9u+36Gw
Kl7McOsk/V+cukTujvERXvknmcLgS7GLE7/qLQ9G/UcZKh+YXVUiKeG8GBX84DkF
75etyUxg9vje4YAvLVlBOZ4XD1exQmo7inYyuhrQfUOnDkgGnhVYrA0nNFtAxeCA
hW+PCMClozCUhXlKo0gf/Z3AJxewsVm4x+G73aU9t/yxm6A4Z2OEYGfD240uhfC/
fEVNpL3xs2TPP4W90yw+Q+oZ0g/hmXKgFQ1PujD5+hGtiRLQ3v1Apb1LhPTz3pPg
OK/VSWbDXBSESqU5aFfk3NP7KSnVfxkUVQk9MAnofq2p3BrPv0ovjgliyMHOV88/
6xdkIBkCgYEA5QFSH63n9WuQz2ZHR7YrU/i8ATrQ9ex1RawLAVZA+YN+GC2/Kd+c
GDyB1ed2v7o/DzCbiVE9WGhCLUE2WDp8J+MVIbgVDYnCx6wXLaMtkzSMtQMT6UV4
+ks6FqvrjmzwdvtrX31XfHCvsGkPhHKeoUU0SnIwvbyxq2sNBdH0E2UCgYEAzi7Y
KIQ/0ryekr6Mf8pAOaprxJX4ykArvTTGwzVazaumTNW0AArEnKNdebB+kVVko4h0
2kUPbJTNwGTxJiuu7HJGE1vla++wlpyPryqghVPIgwVIQOjkV3guq8BbOPyB/xtv
9yVJZqeJc0Jj+ZV7Cm+kQ514F3e1cpidYUUjQbsCgYA+Jj+dbVr4Vfr07nMF2UCl
B2oug0HWnBevkuNht4DmtnLwKOoqeQ8p3LH31Vt66RbYDn8Ho06cwZ7EHWCcTTMI
uC4x+n1sMSj1e5TGw/RIcQiGz5EFy97rPqNDJ+FDw/j2sYEQZznpAcQMgla9wUWf
yuJIGfl0ZNNrDCB6peIxqQKBgQCKTo4dj6koefJ9SWkCB+/RPuqPsnJzaVxtzUtP
gyjoMi6Z9/iI1rBQyp1XlfcxEnEx6cVI7W6NTbw/RPcmvcLXRUiQj+Jz5xMz1M3l
mNiY1zz39sEjGZaivjHAcIZA0dF6CTOwO8jjHZtsP6rEr2sb8wvjd2wpgdmrh4h6
yV//JQKBgQDL4q0becCvRcC8HYPt5LkxWHLvvOlP4Z8x19DzMw0xhvhqvtk7cuxy
ioQckj/9Qa5icYqXGUY1eg2wSMe4mJdCbosXFDdPi3pW7eXJwmQmVCwHx0INDq6z
Xn5hG0ZRioytwV8aqbq8k1PLHm6mmY71dH+riou2JwFD2py7RqBJqQ==
-----END RSA PRIVATE KEY-----

View file

@ -1,91 +0,0 @@
user nginx;
worker_processes 1;
pid /var/run/nginx.pid;
events {
worker_connections 1024;
}
http {
include /etc/nginx/mime.types;
default_type application/octet-stream;
log_format main '$remote_addr - $remote_user [$time_local] "$request" '
'$status $body_bytes_sent "$http_referer" '
'"$http_user_agent" "$http_x_forwarded_for"';
sendfile on;
keepalive_timeout 65;
# max_execution_time is 900
proxy_read_timeout 910;
# Disable SSLv3 to protect us from POODLE & company
ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
server {
listen *:443;
server_name localhost;
ssl on;
ssl_certificate /etc/ssl/nginx/localhost.crt;
ssl_certificate_key /etc/ssl/nginx/localhost.key;
root /var/www/z-push;
index index.php;
error_log /var/log/nginx/zpush-error.log;
access_log /var/log/nginx/zpush-access.log;
# Attachments 20MB max
client_max_body_size 20m;
client_body_buffer_size 128k;
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 zpushphpfpm:9000;
# PHP max_execution_time is set to 900
fastcgi_read_timeout 910;
}
# You don't really need all of the next, but I have them in my server
location = /robots.txt {
access_log off;
log_not_found off;
}
location = /favicon.ico {
return 204;
access_log off;
log_not_found off;
}
location ~ ^\. {
access_log on;
log_not_found off;
deny all;
}
location ~ ~$ {
access_log off;
log_not_found off;
deny all;
}
}
}

View file

@ -1,21 +0,0 @@
FROM fbiete/centos_epel_php_fpm:5.6
MAINTAINER Francisco Miguel Biete <fbiete@gmail.com>
RUN yum clean all \
&& yum install -y --enablerepo=remi-php56 \
php-pecl-memprof \
mailcap \
&& yum clean all \
&& cd /usr/local/src \
&& curl -LSs https://gitlab.com/davical-project/awl/repository/archive.tar.gz | tar xz \
&& echo "include_path=.:/usr/share/pear:/usr/share/php:/usr/local/src/awl.git/inc" >> /etc/php.ini \
&& sed -i 's/max_execution_time = 30/max_execution_time = 900/g' /etc/php.ini \
&& sed -i 's/max_input_time = 60/max_input_time = 300/g' /etc/php.ini \
&& sed -i 's/post_max_size = 8M/post_max_size = 20M/g' /etc/php.ini \
&& sed -i 's/upload_max_filesize = 2M/upload_max_filesize = 20M/g' /etc/php.ini \
&& mkdir /var/log/z-push /var/lib/z-push \
&& chown -R apache:apache /var/log/z-push /var/lib/z-push
VOLUME /var/www/z-push

View file

@ -1,148 +0,0 @@
<?php
// +-----------------------------------------------------------------------+
// | Copyright (c) 2002-2003 Richard Heyes |
// | All rights reserved. |
// | |
// | Redistribution and use in source and binary forms, with or without |
// | modification, are permitted provided that the following conditions |
// | are met: |
// | |
// | o Redistributions of source code must retain the above copyright |
// | notice, this list of conditions and the following disclaimer. |
// | o Redistributions in binary form must reproduce the above copyright |
// | notice, this list of conditions and the following disclaimer in the |
// | documentation and/or other materials provided with the distribution.|
// | o The names of the authors may not be used to endorse or promote |
// | products derived from this software without specific prior written |
// | permission. |
// | |
// | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
// | "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
// | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
// | A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT |
// | OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
// | SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
// | LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
// | DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
// | THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
// | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
// | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
// | |
// +-----------------------------------------------------------------------+
// | Author: Richard Heyes <richard@php.net> |
// +-----------------------------------------------------------------------+
//
// $Id$
/**
* Client implementation of various SASL mechanisms
*
* @author Richard Heyes <richard@php.net>
* @access public
* @version 1.0
* @package Auth_SASL
*/
/**
* Z-Push changes
*
* removed PEAR dependency by implementing own raiseError()
*
* Reference implementation used:
* http://download.pear.php.net/package/Auth_SASL-1.0.6.tgz
*
*
*/
//require_once('PEAR.php');
class Auth_SASL
{
/**
* Factory class. Returns an object of the request
* type.
*
* @param string $type One of: Anonymous
* Plain
* CramMD5
* DigestMD5
* SCRAM-* (any mechanism of the SCRAM family)
* Types are not case sensitive
*/
function &factory($type)
{
switch (strtolower($type)) {
case 'anonymous':
$filename = 'include/Auth/SASL/Anonymous.php';
$classname = 'Auth_SASL_Anonymous';
break;
case 'login':
$filename = 'include/Auth/SASL/Login.php';
$classname = 'Auth_SASL_Login';
break;
case 'plain':
$filename = 'include/Auth/SASL/Plain.php';
$classname = 'Auth_SASL_Plain';
break;
case 'external':
$filename = 'include/Auth/SASL/External.php';
$classname = 'Auth_SASL_External';
break;
case 'crammd5':
// $msg = 'Deprecated mechanism name. Use IANA-registered name: CRAM-MD5.';
// trigger_error($msg, E_USER_DEPRECATED);
case 'cram-md5':
$filename = 'include/Auth/SASL/CramMD5.php';
$classname = 'Auth_SASL_CramMD5';
break;
case 'digestmd5':
// $msg = 'Deprecated mechanism name. Use IANA-registered name: DIGEST-MD5.';
// trigger_error($msg, E_USER_DEPRECATED);
case 'digest-md5':
// $msg = 'DIGEST-MD5 is a deprecated SASL mechanism as per RFC-6331. Using it could be a security risk.';
// trigger_error($msg, E_USER_NOTICE);
$filename = 'include/Auth/SASL/DigestMD5.php';
$classname = 'Auth_SASL_DigestMD5';
break;
default:
$scram = '/^SCRAM-(.{1,9})$/i';
if (preg_match($scram, $type, $matches))
{
$hash = $matches[1];
$filename = 'include/Auth/SASL/SCRAM.php';
$classname = 'Auth_SASL_SCRAM';
$parameter = $hash;
break;
}
return Auth_SASL::raiseError('Invalid SASL mechanism type');
break;
}
require_once($filename);
if (isset($parameter))
$obj = new $classname($parameter);
else
$obj = new $classname();
return $obj;
}
/**
* Z-Push helper for error logging
* removing PEAR dependency
*
* @param string debug message
* @return boolean always false as there was an error
* @access private
*/
static function raiseError($message) {
ZLog::Write(LOGLEVEL_ERROR, "Auth_SASL error: ". $message);
return false;
}
}

View file

@ -1,68 +0,0 @@
<?php
// +-----------------------------------------------------------------------+
// | Copyright (c) 2002-2003 Richard Heyes |
// | All rights reserved. |
// | |
// | Redistribution and use in source and binary forms, with or without |
// | modification, are permitted provided that the following conditions |
// | are met: |
// | |
// | o Redistributions of source code must retain the above copyright |
// | notice, this list of conditions and the following disclaimer. |
// | o Redistributions in binary form must reproduce the above copyright |
// | notice, this list of conditions and the following disclaimer in the |
// | documentation and/or other materials provided with the distribution.|
// | o The names of the authors may not be used to endorse or promote |
// | products derived from this software without specific prior written |
// | permission. |
// | |
// | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
// | "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
// | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
// | A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT |
// | OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
// | SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
// | LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
// | DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
// | THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
// | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
// | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
// | |
// +-----------------------------------------------------------------------+
// | Author: Richard Heyes <richard@php.net> |
// +-----------------------------------------------------------------------+
//
// $Id$
/**
* Implmentation of ANONYMOUS SASL mechanism
*
* @author Richard Heyes <richard@php.net>
* @access public
* @version 1.0
* @package Auth_SASL
*/
class Auth_SASL_Anonymous extends Auth_SASL_Common
{
/**
* Not much to do here except return the token supplied.
* No encoding, hashing or encryption takes place for this
* mechanism, simply one of:
* o An email address
* o An opaque string not containing "@" that can be interpreted
* by the sysadmin
* o Nothing
*
* We could have some logic here for the second option, but this
* would by no means create something interpretable.
*
* @param string $token Optional email address or string to provide
* as trace information.
* @return string The unaltered input token
*/
function getResponse($token = '')
{
return $token;
}
}

View file

@ -1,128 +0,0 @@
<?php
// +-----------------------------------------------------------------------+
// | Copyright (c) 2002-2003 Richard Heyes |
// | All rights reserved. |
// | |
// | Redistribution and use in source and binary forms, with or without |
// | modification, are permitted provided that the following conditions |
// | are met: |
// | |
// | o Redistributions of source code must retain the above copyright |
// | notice, this list of conditions and the following disclaimer. |
// | o Redistributions in binary form must reproduce the above copyright |
// | notice, this list of conditions and the following disclaimer in the |
// | documentation and/or other materials provided with the distribution.|
// | o The names of the authors may not be used to endorse or promote |
// | products derived from this software without specific prior written |
// | permission. |
// | |
// | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
// | "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
// | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
// | A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT |
// | OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
// | SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
// | LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
// | DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
// | THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
// | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
// | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
// | |
// +-----------------------------------------------------------------------+
// | Author: Richard Heyes <richard@php.net> |
// +-----------------------------------------------------------------------+
//
// $Id$
/**
* Common functionality to SASL mechanisms
*
* @author Richard Heyes <richard@php.net>
* @access public
* @version 1.0
* @package Auth_SASL
*/
/**
* Z-Push changes
*
* removed PEAR dependency by implementing own raiseError()
*
* Reference implementation used:
* http://download.pear.php.net/package/Auth_SASL-1.0.6.tgz
*
*
*/
class Auth_SASL_Common
{
/**
* Function which implements HMAC MD5 digest
*
* @param string $key The secret key
* @param string $data The data to hash
* @param bool $raw_output Whether the digest is returned in binary or hexadecimal format.
*
* @return string The HMAC-MD5 digest
*/
function _HMAC_MD5($key, $data, $raw_output = FALSE)
{
if (strlen($key) > 64) {
$key = pack('H32', md5($key));
}
if (strlen($key) < 64) {
$key = str_pad($key, 64, chr(0));
}
$k_ipad = substr($key, 0, 64) ^ str_repeat(chr(0x36), 64);
$k_opad = substr($key, 0, 64) ^ str_repeat(chr(0x5C), 64);
$inner = pack('H32', md5($k_ipad . $data));
$digest = md5($k_opad . $inner, $raw_output);
return $digest;
}
/**
* Function which implements HMAC-SHA-1 digest
*
* @param string $key The secret key
* @param string $data The data to hash
* @param bool $raw_output Whether the digest is returned in binary or hexadecimal format.
* @return string The HMAC-SHA-1 digest
* @author Jehan <jehan.marmottard@gmail.com>
* @access protected
*/
protected function _HMAC_SHA1($key, $data, $raw_output = FALSE)
{
if (strlen($key) > 64) {
$key = sha1($key, TRUE);
}
if (strlen($key) < 64) {
$key = str_pad($key, 64, chr(0));
}
$k_ipad = substr($key, 0, 64) ^ str_repeat(chr(0x36), 64);
$k_opad = substr($key, 0, 64) ^ str_repeat(chr(0x5C), 64);
$inner = pack('H40', sha1($k_ipad . $data));
$digest = sha1($k_opad . $inner, $raw_output);
return $digest;
}
/**
* Z-Push helper for error logging
* removing PEAR dependency
*
* @param string debug message
* @return boolean always false as there was an error
* @access private
*/
function raiseError($message) {
ZLog::Write(LOGLEVEL_ERROR, "SCRAM error: ". $message);
return false;
}
}

View file

@ -1,65 +0,0 @@
<?php
// +-----------------------------------------------------------------------+
// | Copyright (c) 2002-2003 Richard Heyes |
// | All rights reserved. |
// | |
// | Redistribution and use in source and binary forms, with or without |
// | modification, are permitted provided that the following conditions |
// | are met: |
// | |
// | o Redistributions of source code must retain the above copyright |
// | notice, this list of conditions and the following disclaimer. |
// | o Redistributions in binary form must reproduce the above copyright |
// | notice, this list of conditions and the following disclaimer in the |
// | documentation and/or other materials provided with the distribution.|
// | o The names of the authors may not be used to endorse or promote |
// | products derived from this software without specific prior written |
// | permission. |
// | |
// | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
// | "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
// | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
// | A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT |
// | OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
// | SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
// | LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
// | DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
// | THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
// | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
// | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
// | |
// +-----------------------------------------------------------------------+
// | Author: Richard Heyes <richard@php.net> |
// +-----------------------------------------------------------------------+
//
// $Id$
/**
* Implmentation of CRAM-MD5 SASL mechanism
*
* @author Richard Heyes <richard@php.net>
* @access public
* @version 1.0
* @package Auth_SASL
*/
class Auth_SASL_CramMD5 extends Auth_SASL_Common
{
/**
* Implements the CRAM-MD5 SASL mechanism
* This DOES NOT base64 encode the return value,
* you will need to do that yourself.
*
* @param string $user Username
* @param string $pass Password
* @param string $challenge The challenge supplied by the server.
* this should be already base64_decoded.
*
* @return string The string to pass back to the server, of the form
* "<user> <digest>". This is NOT base64_encoded.
*/
function getResponse($user, $pass, $challenge)
{
return $user . ' ' . $this->_HMAC_MD5($pass, $challenge);
}
}

View file

@ -1,194 +0,0 @@
<?php
// +-----------------------------------------------------------------------+
// | Copyright (c) 2002-2003 Richard Heyes |
// | All rights reserved. |
// | |
// | Redistribution and use in source and binary forms, with or without |
// | modification, are permitted provided that the following conditions |
// | are met: |
// | |
// | o Redistributions of source code must retain the above copyright |
// | notice, this list of conditions and the following disclaimer. |
// | o Redistributions in binary form must reproduce the above copyright |
// | notice, this list of conditions and the following disclaimer in the |
// | documentation and/or other materials provided with the distribution.|
// | o The names of the authors may not be used to endorse or promote |
// | products derived from this software without specific prior written |
// | permission. |
// | |
// | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
// | "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
// | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
// | A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT |
// | OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
// | SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
// | LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
// | DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
// | THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
// | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
// | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
// | |
// +-----------------------------------------------------------------------+
// | Author: Richard Heyes <richard@php.net> |
// +-----------------------------------------------------------------------+
//
// $Id$
/**
* Implmentation of DIGEST-MD5 SASL mechanism
*
* @author Richard Heyes <richard@php.net>
* @access public
* @version 1.0
* @package Auth_SASL
*/
class Auth_SASL_DigestMD5 extends Auth_SASL_Common
{
/**
* Provides the (main) client response for DIGEST-MD5
* requires a few extra parameters than the other
* mechanisms, which are unavoidable.
*
* @param string $authcid Authentication id (username)
* @param string $pass Password
* @param string $challenge The digest challenge sent by the server
* @param string $hostname The hostname of the machine you're connecting to
* @param string $service The servicename (eg. imap, pop, acap etc)
* @param string $authzid Authorization id (username to proxy as)
* @return string The digest response (NOT base64 encoded)
* @access public
*/
function getResponse($authcid, $pass, $challenge, $hostname, $service, $authzid = '')
{
$challenge = $this->_parseChallenge($challenge);
$authzid_string = '';
if ($authzid != '') {
$authzid_string = ',authzid="' . $authzid . '"';
}
if (!empty($challenge)) {
$cnonce = $this->_getCnonce();
$digest_uri = sprintf('%s/%s', $service, $hostname);
$response_value = $this->_getResponseValue($authcid, $pass, $challenge['realm'], $challenge['nonce'], $cnonce, $digest_uri, $authzid);
if ($challenge['realm']) {
return sprintf('username="%s",realm="%s"' . $authzid_string .
',nonce="%s",cnonce="%s",nc=00000001,qop=auth,digest-uri="%s",response=%s,maxbuf=%d', $authcid, $challenge['realm'], $challenge['nonce'], $cnonce, $digest_uri, $response_value, $challenge['maxbuf']);
} else {
return sprintf('username="%s"' . $authzid_string . ',nonce="%s",cnonce="%s",nc=00000001,qop=auth,digest-uri="%s",response=%s,maxbuf=%d', $authcid, $challenge['nonce'], $cnonce, $digest_uri, $response_value, $challenge['maxbuf']);
}
} else {
return $this->raiseError('Invalid digest challenge');
}
}
/**
* Parses and verifies the digest challenge*
*
* @param string $challenge The digest challenge
* @return array The parsed challenge as an assoc
* array in the form "directive => value".
* @access private
*/
function _parseChallenge($challenge)
{
$tokens = array();
while (preg_match('/^([a-z-]+)=("[^"]+(?<!\\\)"|[^,]+)/i', $challenge, $matches)) {
// Ignore these as per rfc2831
if ($matches[1] == 'opaque' OR $matches[1] == 'domain') {
$challenge = substr($challenge, strlen($matches[0]) + 1);
continue;
}
// Allowed multiple "realm" and "auth-param"
if (!empty($tokens[$matches[1]]) AND ($matches[1] == 'realm' OR $matches[1] == 'auth-param')) {
if (is_array($tokens[$matches[1]])) {
$tokens[$matches[1]][] = preg_replace('/^"(.*)"$/', '\\1', $matches[2]);
} else {
$tokens[$matches[1]] = array($tokens[$matches[1]], preg_replace('/^"(.*)"$/', '\\1', $matches[2]));
}
// Any other multiple instance = failure
} elseif (!empty($tokens[$matches[1]])) {
$tokens = array();
break;
} else {
$tokens[$matches[1]] = preg_replace('/^"(.*)"$/', '\\1', $matches[2]);
}
// Remove the just parsed directive from the challenge
$challenge = substr($challenge, strlen($matches[0]) + 1);
}
/**
* Defaults and required directives
*/
// Realm
if (empty($tokens['realm'])) {
$tokens['realm'] = "";
}
// Maxbuf
if (empty($tokens['maxbuf'])) {
$tokens['maxbuf'] = 65536;
}
// Required: nonce, algorithm
if (empty($tokens['nonce']) OR empty($tokens['algorithm'])) {
return array();
}
return $tokens;
}
/**
* Creates the response= part of the digest response
*
* @param string $authcid Authentication id (username)
* @param string $pass Password
* @param string $realm Realm as provided by the server
* @param string $nonce Nonce as provided by the server
* @param string $cnonce Client nonce
* @param string $digest_uri The digest-uri= value part of the response
* @param string $authzid Authorization id
* @return string The response= part of the digest response
* @access private
*/
function _getResponseValue($authcid, $pass, $realm, $nonce, $cnonce, $digest_uri, $authzid = '')
{
if ($authzid == '') {
$A1 = sprintf('%s:%s:%s', pack('H32', md5(sprintf('%s:%s:%s', $authcid, $realm, $pass))), $nonce, $cnonce);
} else {
$A1 = sprintf('%s:%s:%s:%s', pack('H32', md5(sprintf('%s:%s:%s', $authcid, $realm, $pass))), $nonce, $cnonce, $authzid);
}
$A2 = 'AUTHENTICATE:' . $digest_uri;
return md5(sprintf('%s:%s:00000001:%s:auth:%s', md5($A1), $nonce, $cnonce, md5($A2)));
}
/**
* Creates the client nonce for the response
*
* @return string The cnonce value
* @access private
*/
function _getCnonce()
{
if (@file_exists('/dev/urandom') && $fd = @fopen('/dev/urandom', 'r')) {
return base64_encode(fread($fd, 32));
} elseif (@file_exists('/dev/random') && $fd = @fopen('/dev/random', 'r')) {
return base64_encode(fread($fd, 32));
} else {
$str = '';
for ($i=0; $i<32; $i++) {
$str .= chr(mt_rand(0, 255));
}
return base64_encode($str);
}
}
}

View file

@ -1,60 +0,0 @@
<?php
// +-----------------------------------------------------------------------+
// | Copyright (c) 2008 Christoph Schulz |
// | All rights reserved. |
// | |
// | Redistribution and use in source and binary forms, with or without |
// | modification, are permitted provided that the following conditions |
// | are met: |
// | |
// | o Redistributions of source code must retain the above copyright |
// | notice, this list of conditions and the following disclaimer. |
// | o Redistributions in binary form must reproduce the above copyright |
// | notice, this list of conditions and the following disclaimer in the |
// | documentation and/or other materials provided with the distribution.|
// | o The names of the authors may not be used to endorse or promote |
// | products derived from this software without specific prior written |
// | permission. |
// | |
// | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
// | "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
// | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
// | A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT |
// | OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
// | SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
// | LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
// | DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
// | THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
// | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
// | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
// | |
// +-----------------------------------------------------------------------+
// | Author: Christoph Schulz <develop@kristov.de> |
// +-----------------------------------------------------------------------+
//
// $Id$
/**
* Implmentation of EXTERNAL SASL mechanism
*
* @author Christoph Schulz <develop@kristov.de>
* @access public
* @version 1.0.3
* @package Auth_SASL
*/
class Auth_SASL_External extends Auth_SASL_Common
{
/**
* Returns EXTERNAL response
*
* @param string $authcid Authentication id (username)
* @param string $pass Password
* @param string $authzid Autorization id
* @return string EXTERNAL Response
*/
function getResponse($authcid, $pass, $authzid = '')
{
return $authzid;
}
}

View file

@ -1,62 +0,0 @@
<?php
// +-----------------------------------------------------------------------+
// | Copyright (c) 2002-2003 Richard Heyes |
// | All rights reserved. |
// | |
// | Redistribution and use in source and binary forms, with or without |
// | modification, are permitted provided that the following conditions |
// | are met: |
// | |
// | o Redistributions of source code must retain the above copyright |
// | notice, this list of conditions and the following disclaimer. |
// | o Redistributions in binary form must reproduce the above copyright |
// | notice, this list of conditions and the following disclaimer in the |
// | documentation and/or other materials provided with the distribution.|
// | o The names of the authors may not be used to endorse or promote |
// | products derived from this software without specific prior written |
// | permission. |
// | |
// | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
// | "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
// | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
// | A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT |
// | OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
// | SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
// | LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
// | DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
// | THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
// | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
// | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
// | |
// +-----------------------------------------------------------------------+
// | Author: Richard Heyes <richard@php.net> |
// +-----------------------------------------------------------------------+
//
// $Id$
/**
* This is technically not a SASL mechanism, however
* it's used by Net_Sieve, Net_Cyrus and potentially
* other protocols , so here is a good place to abstract
* it.
*
* @author Richard Heyes <richard@php.net>
* @access public
* @version 1.0
* @package Auth_SASL
*/
class Auth_SASL_Login extends Auth_SASL_Common
{
/**
* Pseudo SASL LOGIN mechanism
*
* @param string $user Username
* @param string $pass Password
* @return string LOGIN string
*/
function getResponse($user, $pass)
{
return sprintf('LOGIN %s %s', $user, $pass);
}
}

View file

@ -1,60 +0,0 @@
<?php
// +-----------------------------------------------------------------------+
// | Copyright (c) 2002-2003 Richard Heyes |
// | All rights reserved. |
// | |
// | Redistribution and use in source and binary forms, with or without |
// | modification, are permitted provided that the following conditions |
// | are met: |
// | |
// | o Redistributions of source code must retain the above copyright |
// | notice, this list of conditions and the following disclaimer. |
// | o Redistributions in binary form must reproduce the above copyright |
// | notice, this list of conditions and the following disclaimer in the |
// | documentation and/or other materials provided with the distribution.|
// | o The names of the authors may not be used to endorse or promote |
// | products derived from this software without specific prior written |
// | permission. |
// | |
// | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
// | "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
// | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
// | A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT |
// | OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
// | SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
// | LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
// | DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
// | THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
// | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
// | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
// | |
// +-----------------------------------------------------------------------+
// | Author: Richard Heyes <richard@php.net> |
// +-----------------------------------------------------------------------+
//
// $Id$
/**
* Implmentation of PLAIN SASL mechanism
*
* @author Richard Heyes <richard@php.net>
* @access public
* @version 1.0
* @package Auth_SASL
*/
class Auth_SASL_Plain extends Auth_SASL_Common
{
/**
* Returns PLAIN response
*
* @param string $authcid Authentication id (username)
* @param string $pass Password
* @param string $authzid Autorization id
* @return string PLAIN Response
*/
function getResponse($authcid, $pass, $authzid = '')
{
return $authzid . chr(0) . $authcid . chr(0) . $pass;
}
}

View file

@ -1,301 +0,0 @@
<?php
// +-----------------------------------------------------------------------+
// | Copyright (c) 2011 Jehan |
// | All rights reserved. |
// | |
// | Redistribution and use in source and binary forms, with or without |
// | modification, are permitted provided that the following conditions |
// | are met: |
// | |
// | o Redistributions of source code must retain the above copyright |
// | notice, this list of conditions and the following disclaimer. |
// | o Redistributions in binary form must reproduce the above copyright |
// | notice, this list of conditions and the following disclaimer in the |
// | documentation and/or other materials provided with the distribution.|
// | o The names of the authors may not be used to endorse or promote |
// | products derived from this software without specific prior written |
// | permission. |
// | |
// | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
// | "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
// | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
// | A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT |
// | OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
// | SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
// | LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
// | DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
// | THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
// | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
// | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
// | |
// +-----------------------------------------------------------------------+
// | Author: Jehan <jehan.marmottard@gmail.com |
// +-----------------------------------------------------------------------+
//
// $Id$
/**
* Implementation of SCRAM-* SASL mechanisms.
* SCRAM mechanisms have 3 main steps (initial response, response to the server challenge, then server signature
* verification) which keep state-awareness. Therefore a single class instanciation must be done and reused for the whole
* authentication process.
*
* @author Jehan <jehan.marmottard@gmail.com>
* @access public
* @version 1.0
* @package Auth_SASL
*/
class Auth_SASL_SCRAM extends Auth_SASL_Common
{
/**
* Construct a SCRAM-H client where 'H' is a cryptographic hash function.
*
* @param string $hash The name cryptographic hash function 'H' as registered by IANA in the "Hash Function Textual
* Names" registry.
* @link http://www.iana.org/assignments/hash-function-text-names/hash-function-text-names.xml "Hash Function Textual
* Names"
* format of core PHP hash function.
* @access public
*/
function __construct($hash)
{
// Though I could be strict, I will actually also accept the naming used in the PHP core hash framework.
// For instance "sha1" is accepted, while the registered hash name should be "SHA-1".
$hash = strtolower($hash);
$hashes = array('md2' => 'md2',
'md5' => 'md5',
'sha-1' => 'sha1',
'sha1' => 'sha1',
'sha-224' > 'sha224',
'sha224' > 'sha224',
'sha-256' => 'sha256',
'sha256' => 'sha256',
'sha-384' => 'sha384',
'sha384' => 'sha384',
'sha-512' => 'sha512',
'sha512' => 'sha512');
if (function_exists('hash_hmac') && isset($hashes[$hash]))
{
$this->hash = create_function('$data', 'return hash("' . $hashes[$hash] . '", $data, TRUE);');
$this->hmac = create_function('$key,$str,$raw', 'return hash_hmac("' . $hashes[$hash] . '", $str, $key, $raw);');
}
elseif ($hash == 'md5')
{
$this->hash = create_function('$data', 'return md5($data, true);');
$this->hmac = array($this, '_HMAC_MD5');
}
elseif (in_array($hash, array('sha1', 'sha-1')))
{
$this->hash = create_function('$data', 'return sha1($data, true);');
$this->hmac = array($this, '_HMAC_SHA1');
}
else
return $this->raiseError('Invalid SASL mechanism type');
}
/**
* Provides the (main) client response for SCRAM-H.
*
* @param string $authcid Authentication id (username)
* @param string $pass Password
* @param string $challenge The challenge sent by the server.
* If the challenge is NULL or an empty string, the result will be the "initial response".
* @param string $authzid Authorization id (username to proxy as)
* @return string|false The response (binary, NOT base64 encoded)
* @access public
*/
public function getResponse($authcid, $pass, $challenge = NULL, $authzid = NULL)
{
$authcid = $this->_formatName($authcid);
if (empty($authcid))
{
return false;
}
if (!empty($authzid))
{
$authzid = $this->_formatName($authzid);
if (empty($authzid))
{
return false;
}
}
if (empty($challenge))
{
return $this->_generateInitialResponse($authcid, $authzid);
}
else
{
return $this->_generateResponse($challenge, $pass);
}
}
/**
* Prepare a name for inclusion in a SCRAM response.
*
* @param string $username a name to be prepared.
* @return string the reformated name.
* @access private
*/
private function _formatName($username)
{
// TODO: prepare through the SASLprep profile of the stringprep algorithm.
// See RFC-4013.
$username = str_replace('=', '=3D', $username);
$username = str_replace(',', '=2C', $username);
return $username;
}
/**
* Generate the initial response which can be either sent directly in the first message or as a response to an empty
* server challenge.
*
* @param string $authcid Prepared authentication identity.
* @param string $authzid Prepared authorization identity.
* @return string The SCRAM response to send.
* @access private
*/
private function _generateInitialResponse($authcid, $authzid)
{
$init_rep = '';
$gs2_cbind_flag = 'n,'; // TODO: support channel binding.
$this->gs2_header = $gs2_cbind_flag . (!empty($authzid)? 'a=' . $authzid : '') . ',';
// I must generate a client nonce and "save" it for later comparison on second response.
$this->cnonce = $this->_getCnonce();
// XXX: in the future, when mandatory and/or optional extensions are defined in any updated RFC,
// this message can be updated.
$this->first_message_bare = 'n=' . $authcid . ',r=' . $this->cnonce;
return $this->gs2_header . $this->first_message_bare;
}
/**
* Parses and verifies a non-empty SCRAM challenge.
*
* @param string $challenge The SCRAM challenge
* @return string|false The response to send; false in case of wrong challenge or if an initial response has not
* been generated first.
* @access private
*/
private function _generateResponse($challenge, $password)
{
// XXX: as I don't support mandatory extension, I would fail on them.
// And I simply ignore any optional extension.
$server_message_regexp = "#^r=([\x21-\x2B\x2D-\x7E]+),s=((?:[A-Za-z0-9/+]{4})*(?:[A-Za-z0-9]{3}=|[A-Xa-z0-9]{2}==)?),i=([0-9]*)(,[A-Za-z]=[^,])*$#";
if (!isset($this->cnonce, $this->gs2_header)
|| !preg_match($server_message_regexp, $challenge, $matches))
{
return false;
}
$nonce = $matches[1];
$salt = base64_decode($matches[2]);
if (!$salt)
{
// Invalid Base64.
return false;
}
$i = intval($matches[3]);
$cnonce = substr($nonce, 0, strlen($this->cnonce));
if ($cnonce <> $this->cnonce)
{
// Invalid challenge! Are we under attack?
return false;
}
$channel_binding = 'c=' . base64_encode($this->gs2_header); // TODO: support channel binding.
$final_message = $channel_binding . ',r=' . $nonce; // XXX: no extension.
// TODO: $password = $this->normalize($password); // SASLprep profile of stringprep.
$saltedPassword = $this->hi($password, $salt, $i);
$this->saltedPassword = $saltedPassword;
$clientKey = call_user_func($this->hmac, $saltedPassword, "Client Key", TRUE);
$storedKey = call_user_func($this->hash, $clientKey, TRUE);
$authMessage = $this->first_message_bare . ',' . $challenge . ',' . $final_message;
$this->authMessage = $authMessage;
$clientSignature = call_user_func($this->hmac, $storedKey, $authMessage, TRUE);
$clientProof = $clientKey ^ $clientSignature;
$proof = ',p=' . base64_encode($clientProof);
return $final_message . $proof;
}
/**
* SCRAM has also a server verification step. On a successful outcome, it will send additional data which must
* absolutely be checked against this function. If this fails, the entity which we are communicating with is probably
* not the server as it has not access to your ServerKey.
*
* @param string $data The additional data sent along a successful outcome.
* @return bool Whether the server has been authenticated.
* If false, the client must close the connection and consider to be under a MITM attack.
* @access public
*/
public function processOutcome($data)
{
$verifier_regexp = '#^v=((?:[A-Za-z0-9/+]{4})*(?:[A-Za-z0-9]{3}=|[A-Xa-z0-9]{2}==)?)$#';
if (!isset($this->saltedPassword, $this->authMessage)
|| !preg_match($verifier_regexp, $data, $matches))
{
// This cannot be an outcome, you never sent the challenge's response.
return false;
}
$verifier = $matches[1];
$proposed_serverSignature = base64_decode($verifier);
$serverKey = call_user_func($this->hmac, $this->saltedPassword, "Server Key", true);
$serverSignature = call_user_func($this->hmac, $serverKey, $this->authMessage, TRUE);
return ($proposed_serverSignature === $serverSignature);
}
/**
* Hi() call, which is essentially PBKDF2 (RFC-2898) with HMAC-H() as the pseudorandom function.
*
* @param string $str The string to hash.
* @param string $hash The hash value.
* @param int $i The iteration count.
* @access private
*/
private function hi($str, $salt, $i)
{
$int1 = "\0\0\0\1";
$ui = call_user_func($this->hmac, $str, $salt . $int1, true);
$result = $ui;
for ($k = 1; $k < $i; $k++)
{
$ui = call_user_func($this->hmac, $str, $ui, true);
$result = $result ^ $ui;
}
return $result;
}
/**
* Creates the client nonce for the response
*
* @return string The cnonce value
* @access private
* @author Richard Heyes <richard@php.net>
*/
private function _getCnonce()
{
// TODO: I reused the nonce function from the DigestMD5 class.
// I should probably make this a protected function in Common.
if (@file_exists('/dev/urandom') && $fd = @fopen('/dev/urandom', 'r')) {
return base64_encode(fread($fd, 32));
} elseif (@file_exists('/dev/random') && $fd = @fopen('/dev/random', 'r')) {
return base64_encode(fread($fd, 32));
} else {
$str = '';
for ($i=0; $i<32; $i++) {
$str .= chr(mt_rand(0, 255));
}
return base64_encode($str);
}
}
}

View file

@ -1,299 +0,0 @@
<?php
/**
* PEAR's Mail:: interface.
*
* PHP versions 4 and 5
*
* LICENSE:
*
* Copyright (c) 2002-2007, Richard Heyes
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* o Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* o Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* o The names of the authors may not be used to endorse or promote
* products derived from this software without specific prior written
* permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* @category Mail
* @package Mail
* @author Chuck Hagenbuch <chuck@horde.org>
* @copyright 1997-2010 Chuck Hagenbuch
* @license http://opensource.org/licenses/bsd-license.php New BSD License
* @version CVS: $Id: Mail.php 307489 2011-01-14 19:06:57Z alec $
* @link http://pear.php.net/package/Mail/
*/
/**
* Z-Push changes
*
* removed PEAR dependency by implementing own raiseError()
*
* Reference implementation used:
* http://download.pear.php.net/package/Mail-1.2.0.tgz
* SVN trunk version r333509
*
*
*/
/**
* PEAR's Mail:: interface. Defines the interface for implementing
* mailers under the PEAR hierarchy, and provides supporting functions
* useful in multiple mailer backends.
*
* @access public
* @version $Revision: 307489 $
* @package Mail
*/
class Mail
{
/**
* Line terminator used for separating header lines.
* @var string
*/
var $sep = "\r\n";
/**
* Provides an interface for generating Mail:: objects of various
* types
*
* @param string $driver The kind of Mail:: object to instantiate.
* @param array $params The parameters to pass to the Mail:: object.
* @return object Mail a instance of the driver class or if fails a PEAR Error
* @access public
*/
static function &factory($driver, $params = array())
{
$driver = strtolower($driver);
$class = 'Mail_' . $driver;
if (class_exists($class)) {
$mailer = new $class($params);
return $mailer;
} else {
return Mail::raiseError('Unable to find class for driver ' . $driver);
}
}
/**
* Implements Mail::send() function using php's built-in mail()
* command.
*
* @param mixed $recipients Either a comma-seperated list of recipients
* (RFC822 compliant), or an array of recipients,
* each RFC822 valid. This may contain recipients not
* specified in the headers, for Bcc:, resending
* messages, etc.
*
* @param array $headers The array of headers to send with the mail, in an
* associative array, where the array key is the
* header name (ie, 'Subject'), and the array value
* is the header value (ie, 'test'). The header
* produced from those values would be 'Subject:
* test'.
*
* @param string $body The full text of the message body, including any
* Mime parts, etc.
*
* @return mixed Returns true on success, or a PEAR_Error
* containing a descriptive error message on
* failure.
*
* @access public
* @deprecated use Mail_mail::send instead
*/
function send($recipients, $headers, $body)
{
if (!is_array($headers)) {
return Mail::raiseError('$headers must be an array');
}
$result = $this->_sanitizeHeaders($headers);
//if (is_a($result, 'PEAR_Error')) {
if ($result === false) {
return $result;
}
// if we're passed an array of recipients, implode it.
if (is_array($recipients)) {
$recipients = implode(', ', $recipients);
}
// get the Subject out of the headers array so that we can
// pass it as a seperate argument to mail().
$subject = '';
if (isset($headers['Subject'])) {
$subject = $headers['Subject'];
unset($headers['Subject']);
}
// flatten the headers out.
list(, $text_headers) = Mail::prepareHeaders($headers);
return mail($recipients, $subject, $body, $text_headers);
}
/**
* Sanitize an array of mail headers by removing any additional header
* strings present in a legitimate header's value. The goal of this
* filter is to prevent mail injection attacks.
*
* @param array $headers The associative array of headers to sanitize.
*
* @access private
*/
function _sanitizeHeaders(&$headers)
{
foreach ($headers as $key => $value) {
$headers[$key] =
preg_replace('=((<CR>|<LF>|0x0A/%0A|0x0D/%0D|\\n|\\r)\S).*=i',
null, $value);
}
return true;
}
/**
* Take an array of mail headers and return a string containing
* text usable in sending a message.
*
* @param array $headers The array of headers to prepare, in an associative
* array, where the array key is the header name (ie,
* 'Subject'), and the array value is the header
* value (ie, 'test'). The header produced from those
* values would be 'Subject: test'.
*
* @return mixed Returns false if it encounters a bad address,
* otherwise returns an array containing two
* elements: Any From: address found in the headers,
* and the plain text version of the headers.
* @access private
*/
function prepareHeaders($headers)
{
$lines = array();
$from = null;
foreach ($headers as $key => $value) {
if (strcasecmp($key, 'From') === 0) {
$parser = new Mail_RFC822();
$addresses = $parser->parseAddressList($value, 'localhost', false);
//if (is_a($addresses, 'PEAR_Error')) {
if ($addresses === false) {
return $addresses;
}
$from = $addresses[0]->mailbox . '@' . $addresses[0]->host;
// Reject envelope From: addresses with spaces.
if (strstr($from, ' ')) {
return false;
}
$lines[] = $key . ': ' . $value;
} elseif (strcasecmp($key, 'Received') === 0) {
$received = array();
if (is_array($value)) {
foreach ($value as $line) {
$received[] = $key . ': ' . $line;
}
}
else {
$received[] = $key . ': ' . $value;
}
// Put Received: headers at the top. Spam detectors often
// flag messages with Received: headers after the Subject:
// as spam.
$lines = array_merge($received, $lines);
} else {
// If $value is an array (i.e., a list of addresses), convert
// it to a comma-delimited string of its elements (addresses).
if (is_array($value)) {
$value = implode(', ', $value);
}
$lines[] = $key . ': ' . $value;
}
}
return array($from, join($this->sep, $lines));
}
/**
* Take a set of recipients and parse them, returning an array of
* bare addresses (forward paths) that can be passed to sendmail
* or an smtp server with the rcpt to: command.
*
* @param mixed Either a comma-seperated list of recipients
* (RFC822 compliant), or an array of recipients,
* each RFC822 valid.
*
* @return mixed An array of forward paths (bare addresses) or a PEAR_Error
* object if the address list could not be parsed.
* @access private
*/
function parseRecipients($recipients)
{
// if we're passed an array, assume addresses are valid and
// implode them before parsing.
if (is_array($recipients)) {
$recipients = implode(', ', $recipients);
}
// Parse recipients, leaving out all personal info. This is
// for smtp recipients, etc. All relevant personal information
// should already be in the headers.
$parser = new Mail_RFC822();
$addresses = $parser->parseAddressList($recipients, 'localhost', false);
// If parseAddressList() returned a PEAR_Error object, just return it.
//if (is_a($addresses, 'PEAR_Error')) {
if ($addresses === false) {
return $addresses;
}
$recipients = array();
if (is_array($addresses)) {
foreach ($addresses as $ob) {
$recipients[] = $ob->mailbox . '@' . $ob->host;
}
}
// Remove duplicated
$recipients = array_unique($recipients);
return $recipients;
}
/**
* Z-Push helper for error logging
* removing PEAR dependency
*
* @param string debug message
* @return boolean always false as there was an error
* @access private
*/
static function raiseError($message) {
ZLog::Write(LOGLEVEL_ERROR, "Mail error: ". $message);
return false;
}
}

View file

@ -1,193 +0,0 @@
<?php
/**
* internal PHP-mail() implementation of the PEAR Mail:: interface.
*
* PHP versions 4 and 5
*
* LICENSE:
*
* Copyright (c) 2010 Chuck Hagenbuch
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* o Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* o Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* o The names of the authors may not be used to endorse or promote
* products derived from this software without specific prior written
* permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* @category Mail
* @package Mail
* @author Chuck Hagenbuch <chuck@horde.org>
* @copyright 2010 Chuck Hagenbuch
* @license http://opensource.org/licenses/bsd-license.php New BSD License
* @version CVS: $Id: mail.php 294747 2010-02-08 08:18:33Z clockwerx $
* @link http://pear.php.net/package/Mail/
*/
/**
* Z-Push changes
*
* removed PEAR dependency by implementing own raiseError()
*
* Reference implementation used:
* http://download.pear.php.net/package/Mail-1.2.0.tgz
*
*
*/
/**
* internal PHP-mail() implementation of the PEAR Mail:: interface.
* @package Mail
* @version $Revision: 294747 $
*/
class Mail_mail extends Mail {
/**
* Any arguments to pass to the mail() function.
* @var string
*/
var $_params = '';
/**
* Constructor.
*
* Instantiates a new Mail_mail:: object based on the parameters
* passed in.
*
* @param array $params Extra arguments for the mail() function.
*/
function Mail_mail($params = null)
{
// The other mail implementations accept parameters as arrays.
// In the interest of being consistent, explode an array into
// a string of parameter arguments.
if (is_array($params)) {
$this->_params = join(' ', $params);
} else {
$this->_params = $params;
}
/* Because the mail() function may pass headers as command
* line arguments, we can't guarantee the use of the standard
* "\r\n" separator. Instead, we use the system's native line
* separator. */
if (defined('PHP_EOL')) {
$this->sep = PHP_EOL;
} else {
$this->sep = (strpos(PHP_OS, 'WIN') === false) ? "\n" : "\r\n";
}
}
/**
* Implements Mail_mail::send() function using php's built-in mail()
* command.
*
* @param mixed $recipients Either a comma-seperated list of recipients
* (RFC822 compliant), or an array of recipients,
* each RFC822 valid. This may contain recipients not
* specified in the headers, for Bcc:, resending
* messages, etc.
*
* @param array $headers The array of headers to send with the mail, in an
* associative array, where the array key is the
* header name (ie, 'Subject'), and the array value
* is the header value (ie, 'test'). The header
* produced from those values would be 'Subject:
* test'.
*
* @param string $body The full text of the message body, including any
* Mime parts, etc.
*
* @return mixed Returns true on success, or a PEAR_Error
* containing a descriptive error message on
* failure.
*
* @access public
*/
function send($recipients, $headers, $body)
{
if (!is_array($headers)) {
return Mail_mail::raiseError('$headers must be an array');
}
$result = $this->_sanitizeHeaders($headers);
//if (is_a($result, 'PEAR_Error')) {
if ($result === false) {
return $result;
}
// If we're passed an array of recipients, implode it.
if (is_array($recipients)) {
$recipients = implode(', ', $recipients);
}
// Get the Subject out of the headers array so that we can
// pass it as a seperate argument to mail().
$subject = '';
if (isset($headers['Subject'])) {
$subject = $headers['Subject'];
unset($headers['Subject']);
}
// Also remove the To: header. The mail() function will add its own
// To: header based on the contents of $recipients.
unset($headers['To']);
// Flatten the headers out.
$headerElements = $this->prepareHeaders($headers);
//if (is_a($headerElements, 'PEAR_Error')) {
if ($headerElements === false) {
return $headerElements;
}
list(, $text_headers) = $headerElements;
// We only use mail()'s optional fifth parameter if the additional
// parameters have been provided and we're not running in safe mode.
if (empty($this->_params) || ini_get('safe_mode')) {
$result = mail($recipients, $subject, $body, $text_headers);
} else {
$result = mail($recipients, $subject, $body, $text_headers,
$this->_params);
}
// If the mail() function returned failure, we need to create a
// PEAR_Error object and return it instead of the boolean result.
if ($result === false) {
$result = Mail_mail::raiseError('mail() returned failure');
}
return $result;
}
/**
* Z-Push helper for error logging
* removing PEAR dependency
*
* @param string debug message
* @return boolean always false as there was an error
* @access private
*/
static function raiseError($message) {
ZLog::Write(LOGLEVEL_ERROR, "Mail<mail> error: ". $message);
return false;
}
}

View file

@ -1,197 +0,0 @@
<?php
//
// +----------------------------------------------------------------------+
// | PHP Version 4 |
// +----------------------------------------------------------------------+
// | Copyright (c) 1997-2003 The PHP Group |
// +----------------------------------------------------------------------+
// | This source file is subject to version 2.02 of the PHP license, |
// | that is bundled with this package in the file LICENSE, and is |
// | available at through the world-wide-web at |
// | http://www.php.net/license/2_02.txt. |
// | If you did not receive a copy of the PHP license and are unable to |
// | obtain it through the world-wide-web, please send a note to |
// | license@php.net so we can mail you a copy immediately. |
// +----------------------------------------------------------------------+
// | Author: Chuck Hagenbuch <chuck@horde.org> |
// +----------------------------------------------------------------------+
/**
* Z-Push changes
*
* removed PEAR dependency by implementing own raiseError()
*
* Reference implementation used:
* http://download.pear.php.net/package/Mail-1.2.0.tgz
*
*
*/
/**
* Sendmail implementation of the PEAR Mail:: interface.
* @access public
* @package Mail
* @version $Revision: 294744 $
*/
class Mail_sendmail extends Mail {
/**
* The location of the sendmail or sendmail wrapper binary on the
* filesystem.
* @var string
*/
var $sendmail_path = '/usr/sbin/sendmail';
/**
* Any extra command-line parameters to pass to the sendmail or
* sendmail wrapper binary.
* @var string
*/
var $sendmail_args = '-i';
/**
* Constructor.
*
* Instantiates a new Mail_sendmail:: object based on the parameters
* passed in. It looks for the following parameters:
* sendmail_path The location of the sendmail binary on the
* filesystem. Defaults to '/usr/sbin/sendmail'.
*
* sendmail_args Any extra parameters to pass to the sendmail
* or sendmail wrapper binary.
*
* If a parameter is present in the $params array, it replaces the
* default.
*
* @param array $params Hash containing any parameters different from the
* defaults.
* @access public
*/
function Mail_sendmail($params)
{
if (isset($params['sendmail_path'])) {
$this->sendmail_path = $params['sendmail_path'];
}
if (isset($params['sendmail_args'])) {
$this->sendmail_args = $params['sendmail_args'];
}
/*
* Because we need to pass message headers to the sendmail program on
* the commandline, we can't guarantee the use of the standard "\r\n"
* separator. Instead, we use the system's native line separator.
*/
if (defined('PHP_EOL')) {
$this->sep = PHP_EOL;
} else {
$this->sep = (strpos(PHP_OS, 'WIN') === false) ? "\n" : "\r\n";
}
}
/**
* Implements Mail::send() function using the sendmail
* command-line binary.
*
* @param mixed $recipients Either a comma-seperated list of recipients
* (RFC822 compliant), or an array of recipients,
* each RFC822 valid. This may contain recipients not
* specified in the headers, for Bcc:, resending
* messages, etc.
*
* @param array $headers The array of headers to send with the mail, in an
* associative array, where the array key is the
* header name (ie, 'Subject'), and the array value
* is the header value (ie, 'test'). The header
* produced from those values would be 'Subject:
* test'.
*
* @param string $body The full text of the message body, including any
* Mime parts, etc.
*
* @return mixed Returns true on success, or a PEAR_Error
* containing a descriptive error message on
* failure.
* @access public
*/
function send($recipients, $headers, $body)
{
if (!is_array($headers)) {
return Mail_sendmail::raiseError('$headers must be an array');
}
$result = $this->_sanitizeHeaders($headers);
//if (is_a($result, 'PEAR_Error')) {
if ($result === false) {
return $result;
}
$recipients = $this->parseRecipients($recipients);
//if (is_a($recipients, 'PEAR_Error')) {
if ($recipients === false) {
return $recipients;
}
$recipients = implode(' ', array_map('escapeshellarg', $recipients));
$headerElements = $this->prepareHeaders($headers);
//if (is_a($headerElements, 'PEAR_Error')) {
if ($headerElements === false) {
return $headerElements;
}
list($from, $text_headers) = $headerElements;
/* Since few MTAs are going to allow this header to be forged
* unless it's in the MAIL FROM: exchange, we'll use
* Return-Path instead of From: if it's set. */
if (!empty($headers['Return-Path'])) {
$from = $headers['Return-Path'];
}
if (!isset($from)) {
return Mail_sendmail::raiseError('No from address given.');
} elseif (strpos($from, ' ') !== false ||
strpos($from, ';') !== false ||
strpos($from, '&') !== false ||
strpos($from, '`') !== false) {
return Mail_sendmail::raiseError('From address specified with dangerous characters.');
}
$from = escapeshellarg($from); // Security bug #16200
$mail = @popen($this->sendmail_path . (!empty($this->sendmail_args) ? ' ' . $this->sendmail_args : '') . " -f$from -- $recipients", 'w');
if (!$mail) {
return Mail_sendmail::raiseError('Failed to open sendmail [' . $this->sendmail_path . '] for execution.');
}
// Write the headers following by two newlines: one to end the headers
// section and a second to separate the headers block from the body.
fputs($mail, $text_headers . $this->sep . $this->sep);
fputs($mail, $body);
$result = pclose($mail);
if (version_compare(phpversion(), '4.2.3') == -1) {
// With older php versions, we need to shift the pclose
// result to get the exit code.
$result = $result >> 8 & 0xFF;
}
if ($result != 0) {
return Mail_sendmail::raiseError('sendmail returned error code ' . $result,
$result);
}
return true;
}
/**
* Z-Push helper for error logging
* removing PEAR dependency
*
* @param string debug message
* @return boolean always false as there was an error
* @access private
*/
static function raiseError($message) {
ZLog::Write(LOGLEVEL_ERROR, "Mail<sendmail> error: ". $message);
return false;
}
}

View file

@ -1,500 +0,0 @@
<?php
/**
* SMTP implementation of the PEAR Mail interface. Requires the Net_SMTP class.
*
* PHP versions 4 and 5
*
* LICENSE:
*
* Copyright (c) 2010, Chuck Hagenbuch
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* o Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* o Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* o The names of the authors may not be used to endorse or promote
* products derived from this software without specific prior written
* permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* @category HTTP
* @package HTTP_Request
* @author Jon Parise <jon@php.net>
* @author Chuck Hagenbuch <chuck@horde.org>
* @copyright 2010 Chuck Hagenbuch
* @license http://opensource.org/licenses/bsd-license.php New BSD License
* @version CVS: $Id: smtp.php 307488 2011-01-14 19:00:54Z alec $
* @link http://pear.php.net/package/Mail/
*/
/**
* Z-Push changes
*
* removed PEAR dependency by implementing own raiseError()
*
* Reference implementation used:
* http://download.pear.php.net/package/Mail-1.2.0.tgz
*
*
*/
/** Error: Failed to create a Net_SMTP object */
define('PEAR_MAIL_SMTP_ERROR_CREATE', 10000);
/** Error: Failed to connect to SMTP server */
define('PEAR_MAIL_SMTP_ERROR_CONNECT', 10001);
/** Error: SMTP authentication failure */
define('PEAR_MAIL_SMTP_ERROR_AUTH', 10002);
/** Error: No From: address has been provided */
define('PEAR_MAIL_SMTP_ERROR_FROM', 10003);
/** Error: Failed to set sender */
define('PEAR_MAIL_SMTP_ERROR_SENDER', 10004);
/** Error: Failed to add recipient */
define('PEAR_MAIL_SMTP_ERROR_RECIPIENT', 10005);
/** Error: Failed to send data */
define('PEAR_MAIL_SMTP_ERROR_DATA', 10006);
/**
* SMTP implementation of the PEAR Mail interface. Requires the Net_SMTP class.
* @access public
* @package Mail
* @version $Revision: 307488 $
*/
class Mail_smtp extends Mail {
/**
* SMTP connection object.
*
* @var object
* @access private
*/
var $_smtp = null;
/**
* The list of service extension parameters to pass to the Net_SMTP
* mailFrom() command.
* @var array
*/
var $_extparams = array();
/**
* The SMTP host to connect to.
* @var string
*/
var $host = 'localhost';
/**
* The port the SMTP server is on.
* @var integer
*/
var $port = 25;
/**
* Should SMTP authentication be used?
*
* This value may be set to true, false or the name of a specific
* authentication method.
*
* If the value is set to true, the Net_SMTP package will attempt to use
* the best authentication method advertised by the remote SMTP server.
*
* @var mixed
*/
var $auth = false;
/**
* The username to use if the SMTP server requires authentication.
* @var string
*/
var $username = '';
/**
* The password to use if the SMTP server requires authentication.
* @var string
*/
var $password = '';
/**
* Hostname or domain that will be sent to the remote SMTP server in the
* HELO / EHLO message.
*
* @var string
*/
var $localhost = 'localhost';
/**
* SMTP connection timeout value. NULL indicates no timeout.
*
* @var integer
*/
var $timeout = null;
/**
* Turn on Net_SMTP debugging?
*
* @var boolean $debug
*/
var $debug = false;
/**
* Indicates whether or not the SMTP connection should persist over
* multiple calls to the send() method.
*
* @var boolean
*/
var $persist = false;
/**
* Use SMTP command pipelining (specified in RFC 2920) if the SMTP server
* supports it. This speeds up delivery over high-latency connections. By
* default, use the default value supplied by Net_SMTP.
* @var bool
*/
var $pipelining;
/**
* Require verification of SSL certificate used.
* @var bool
*/
var $verify_peer = true;
/**
* Require verification of peer name
* @var bool
*/
var $verify_peer_name = true;
/**
* Allow self-signed certificates. Requires verify_peer
* @var bool
*/
var $allow_self_signed = false;
/**
* Constructor.
*
* Instantiates a new Mail_smtp:: object based on the parameters
* passed in. It looks for the following parameters:
* host The server to connect to. Defaults to localhost.
* port The port to connect to. Defaults to 25.
* auth SMTP authentication. Defaults to none.
* username The username to use for SMTP auth. No default.
* password The password to use for SMTP auth. No default.
* localhost The local hostname / domain. Defaults to localhost.
* timeout The SMTP connection timeout. Defaults to none.
* verp Whether to use VERP or not. Defaults to false.
* DEPRECATED as of 1.2.0 (use setMailParams()).
* debug Activate SMTP debug mode? Defaults to false.
* persist Should the SMTP connection persist?
* pipelining Use SMTP command pipelining
*
* If a parameter is present in the $params array, it replaces the
* default.
*
* @param array Hash containing any parameters different from the
* defaults.
* @access public
*/
function Mail_smtp($params)
{
if (isset($params['host'])) $this->host = $params['host'];
if (isset($params['port'])) $this->port = $params['port'];
if (isset($params['auth'])) $this->auth = $params['auth'];
if (isset($params['username'])) $this->username = $params['username'];
if (isset($params['password'])) $this->password = $params['password'];
if (isset($params['localhost'])) $this->localhost = $params['localhost'];
if (isset($params['timeout'])) $this->timeout = $params['timeout'];
if (isset($params['debug'])) $this->debug = (bool)$params['debug'];
if (isset($params['persist'])) $this->persist = (bool)$params['persist'];
if (isset($params['pipelining'])) $this->pipelining = (bool)$params['pipelining'];
if (isset($params['verify_peer'])) $this->verify_peer = (bool)$params['verify_peer'];
if (isset($params['verify_peer_name'])) $this->verify_peer_name = (bool)$params['verify_peer_name'];
if (isset($params['allow_self_signed'])) $this->allow_self_signed = (bool)$params['allow_self_signed'];
// Deprecated options
if (isset($params['verp'])) {
$this->addServiceExtensionParameter('XVERP', is_bool($params['verp']) ? null : $params['verp']);
}
register_shutdown_function(array(&$this, '_Mail_smtp'));
}
/**
* Destructor implementation to ensure that we disconnect from any
* potentially-alive persistent SMTP connections.
*/
function _Mail_smtp()
{
$this->disconnect();
}
/**
* Implements Mail::send() function using SMTP.
*
* @param mixed $recipients Either a comma-seperated list of recipients
* (RFC822 compliant), or an array of recipients,
* each RFC822 valid. This may contain recipients not
* specified in the headers, for Bcc:, resending
* messages, etc.
*
* @param array $headers The array of headers to send with the mail, in an
* associative array, where the array key is the
* header name (e.g., 'Subject'), and the array value
* is the header value (e.g., 'test'). The header
* produced from those values would be 'Subject:
* test'.
*
* @param string $body The full text of the message body, including any
* MIME parts, etc.
*
* @return mixed Returns true on success, or a PEAR_Error
* containing a descriptive error message on
* failure.
* @access public
*/
function send($recipients, $headers, $body)
{
/* If we don't already have an SMTP object, create one. */
$result = &$this->getSMTPObject();
//if (PEAR::isError($result)) {
if ($result === false) {
return $result;
}
if (!is_array($headers)) {
return Mail_smtp::raiseError('$headers must be an array');
}
$this->_sanitizeHeaders($headers);
$headerElements = $this->prepareHeaders($headers);
//if (is_a($headerElements, 'PEAR_Error')) {
if ($headerElements === false) {
$this->_smtp->rset();
return $headerElements;
}
list($from, $textHeaders) = $headerElements;
/* Since few MTAs are going to allow this header to be forged
* unless it's in the MAIL FROM: exchange, we'll use
* Return-Path instead of From: if it's set. */
if (!empty($headers['Return-Path'])) {
$from = $headers['Return-Path'];
}
if (!isset($from)) {
$this->_smtp->rset();
return Mail_smtp::raiseError('No From: address has been provided',
PEAR_MAIL_SMTP_ERROR_FROM);
}
$params = null;
if (!empty($this->_extparams)) {
foreach ($this->_extparams as $key => $val) {
$params .= ' ' . $key . (is_null($val) ? '' : '=' . $val);
}
}
//if (PEAR::isError($res = $this->_smtp->mailFrom($from, ltrim($params)))) {
if (($res = $this->_smtp->mailFrom($from, ltrim($params))) === false) {
$error = $this->_error("Failed to set sender: $from", $res);
$this->_smtp->rset();
return Mail_smtp::raiseError($error, PEAR_MAIL_SMTP_ERROR_SENDER);
}
$recipients = $this->parseRecipients($recipients);
//if (is_a($recipients, 'PEAR_Error')) {
if ($recipients === false) {
$this->_smtp->rset();
return $recipients;
}
foreach ($recipients as $recipient) {
$res = $this->_smtp->rcptTo($recipient);
//if (is_a($res, 'PEAR_Error')) {
if ($res === false) {
$error = $this->_error("Failed to add recipient: $recipient", $res);
$this->_smtp->rset();
return Mail_smtp::raiseError($error, PEAR_MAIL_SMTP_ERROR_RECIPIENT);
}
}
/* Send the message's headers and the body as SMTP data. */
$res = $this->_smtp->data($body, $textHeaders);
list(,$args) = $this->_smtp->getResponse();
if (preg_match("/Ok: queued as (.*)/", $args, $queued)) {
$this->queued_as = $queued[1];
}
/* we need the greeting; from it we can extract the authorative name of the mail server we've really connected to.
* ideal if we're connecting to a round-robin of relay servers and need to track which exact one took the email */
$this->greeting = $this->_smtp->getGreeting();
//if (is_a($res, 'PEAR_Error')) {
if ($res === false) {
$error = $this->_error('Failed to send data', $res);
$this->_smtp->rset();
return Mail_smtp::raiseError($error, PEAR_MAIL_SMTP_ERROR_DATA);
}
/* If persistent connections are disabled, destroy our SMTP object. */
if ($this->persist === false) {
$this->disconnect();
}
return true;
}
/**
* Connect to the SMTP server by instantiating a Net_SMTP object.
*
* @return mixed Returns a reference to the Net_SMTP object on success, or
* a PEAR_Error containing a descriptive error message on
* failure.
*
* @since 1.2.0
* @access public
*/
function &getSMTPObject()
{
if (is_object($this->_smtp) !== false) {
return $this->_smtp;
}
$this->_smtp = &new Net_SMTP($this->host,
$this->port,
$this->localhost,
$this->pipelining,
0, //timeout
null, //socket_options
$this->verify_peer,
$this->verify_peer_name,
$this->allow_self_signed);
/* If we still don't have an SMTP object at this point, fail. */
if (is_object($this->_smtp) === false) {
return Mail_smtp::raiseError('Failed to create a Net_SMTP object',
PEAR_MAIL_SMTP_ERROR_CREATE);
}
/* Configure the SMTP connection. */
if ($this->debug) {
$this->_smtp->setDebug(true);
}
/* Attempt to connect to the configured SMTP server. */
//if (PEAR::isError($res = $this->_smtp->connect($this->timeout))) {
if (($res = $this->_smtp->connect($this->timeout)) === false) {
$error = $this->_error('Failed to connect to ' .
$this->host . ':' . $this->port,
$res);
return Mail_smtp::raiseError($error, PEAR_MAIL_SMTP_ERROR_CONNECT);
}
/* Attempt to authenticate if authentication has been enabled. */
if ($this->auth) {
$method = is_string($this->auth) ? $this->auth : '';
//if (PEAR::isError($res = $this->_smtp->auth($this->username, $this->password, $method))) {
if (($res = $this->_smtp->auth($this->username, $this->password, $method)) === false) {
$error = $this->_error("$method authentication failure",
$res);
$this->_smtp->rset();
return Mail_smtp::raiseError($error, PEAR_MAIL_SMTP_ERROR_AUTH);
}
}
return $this->_smtp;
}
/**
* Add parameter associated with a SMTP service extension.
*
* @param string Extension keyword.
* @param string Any value the keyword needs.
*
* @since 1.2.0
* @access public
*/
function addServiceExtensionParameter($keyword, $value = null)
{
$this->_extparams[$keyword] = $value;
}
/**
* Disconnect and destroy the current SMTP connection.
*
* @return boolean True if the SMTP connection no longer exists.
*
* @since 1.1.9
* @access public
*/
function disconnect()
{
/* If we have an SMTP object, disconnect and destroy it. */
if (is_object($this->_smtp) && $this->_smtp->disconnect()) {
$this->_smtp = null;
}
/* We are disconnected if we no longer have an SMTP object. */
return ($this->_smtp === null);
}
/**
* Build a standardized string describing the current SMTP error.
*
* @param string $text Custom string describing the error context.
* @param object $error Reference to the current PEAR_Error object.
*
* @return string A string describing the current SMTP error.
*
* @since 1.1.7
* @access private
*/
function _error($text, &$error)
{
/* Split the SMTP response into a code and a response string. */
list($code, $response) = $this->_smtp->getResponse();
/* Build our standardized error string. */
return $text
// . ' [SMTP: ' . $error->getMessage()
. ' [SMTP: '
. " (code: $code, response: $response)]";
}
/**
* Z-Push helper for error logging
* removing PEAR dependency
*
* @param string debug message
* @return boolean always false as there was an error
* @access private
*/
static function raiseError($message) {
ZLog::Write(LOGLEVEL_ERROR, "Mail<smtp> error: ". $message);
return false;
}
}

File diff suppressed because it is too large Load diff

View file

@ -1,726 +0,0 @@
<?php
/**
* Net_Socket
*
* PHP Version 4
*
* Copyright (c) 1997-2013 The PHP Group
*
* This source file is subject to version 2.0 of the PHP license,
* that is bundled with this package in the file LICENSE, and is
* available at through the world-wide-web at
* http://www.php.net/license/2_02.txt.
* If you did not receive a copy of the PHP license and are unable to
* obtain it through the world-wide-web, please send a note to
* license@php.net so we can mail you a copy immediately.
*
* Authors: Stig Bakken <ssb@php.net>
* Chuck Hagenbuch <chuck@horde.org>
*
* @category Net
* @package Net_Socket
* @author Stig Bakken <ssb@php.net>
* @author Chuck Hagenbuch <chuck@horde.org>
* @copyright 1997-2003 The PHP Group
* @license http://www.php.net/license/2_02.txt PHP 2.02
* @link http://pear.php.net/packages/Net_Socket
*/
/**
* Z-Push changes
*
* removed PEAR dependency by implementing own raiseError(), and defining OS_WINDOWS
*
* Reference implementation used:
* http://download.pear.php.net/package/Net_Socket-1.0.14.tgz
*
*
*/
//require_once 'PEAR.php';
if (substr(PHP_OS, 0, 3) == 'WIN') {
define('OS_WINDOWS', true);
} else {
define('OS_WINDOWS', false);
}
define('NET_SOCKET_READ', 1);
define('NET_SOCKET_WRITE', 2);
define('NET_SOCKET_ERROR', 4);
/**
* Generalized Socket class.
*
* @category Net
* @package Net_Socket
* @author Stig Bakken <ssb@php.net>
* @author Chuck Hagenbuch <chuck@horde.org>
* @copyright 1997-2003 The PHP Group
* @license http://www.php.net/license/2_02.txt PHP 2.02
* @link http://pear.php.net/packages/Net_Socket
*/
//class Net_Socket extends PEAR
class Net_Socket
{
/**
* Socket file pointer.
* @var resource $fp
*/
var $fp = null;
/**
* Whether the socket is blocking. Defaults to true.
* @var boolean $blocking
*/
var $blocking = true;
/**
* Whether the socket is persistent. Defaults to false.
* @var boolean $persistent
*/
var $persistent = false;
/**
* The IP address to connect to.
* @var string $addr
*/
var $addr = '';
/**
* The port number to connect to.
* @var integer $port
*/
var $port = 0;
/**
* Number of seconds to wait on socket operations before assuming
* there's no more data. Defaults to no timeout.
* @var integer|float $timeout
*/
var $timeout = null;
/**
* Number of bytes to read at a time in readLine() and
* readAll(). Defaults to 2048.
* @var integer $lineLength
*/
var $lineLength = 2048;
/**
* The string to use as a newline terminator. Usually "\r\n" or "\n".
* @var string $newline
*/
var $newline = "\r\n";
/**
* Connect to the specified port. If called when the socket is
* already connected, it disconnects and connects again.
*
* @param string $addr IP address or host name (may be with protocol prefix).
* @param integer $port TCP port number.
* @param boolean $persistent (optional) Whether the connection is
* persistent (kept open between requests
* by the web server).
* @param integer $timeout (optional) Connection socket timeout.
* @param array $options See options for stream_context_create.
*
* @access public
*
* @return boolean|PEAR_Error True on success or a PEAR_Error on failure.
*/
function connect($addr, $port = 0, $persistent = null,
$timeout = null, $options = null)
{
if (is_resource($this->fp)) {
@fclose($this->fp);
$this->fp = null;
}
if (!$addr) {
return $this->raiseError('$addr cannot be empty');
} else if (strspn($addr, ':.0123456789') == strlen($addr)) {
$this->addr = strpos($addr, ':') !== false ? '['.$addr.']' : $addr;
} else {
$this->addr = $addr;
}
$this->port = $port % 65536;
if ($persistent !== null) {
$this->persistent = $persistent;
}
$openfunc = $this->persistent ? 'pfsockopen' : 'fsockopen';
$errno = 0;
$errstr = '';
$old_track_errors = @ini_set('track_errors', 1);
if ($timeout <= 0) {
$timeout = @ini_get('default_socket_timeout');
}
if ($options && function_exists('stream_context_create')) {
$context = stream_context_create($options);
// Since PHP 5 fsockopen doesn't allow context specification
if (function_exists('stream_socket_client')) {
$flags = STREAM_CLIENT_CONNECT;
if ($this->persistent) {
$flags = STREAM_CLIENT_PERSISTENT;
}
$addr = $this->addr . ':' . $this->port;
$fp = stream_socket_client($addr, $errno, $errstr,
$timeout, $flags, $context);
} else {
$fp = @$openfunc($this->addr, $this->port, $errno,
$errstr, $timeout, $context);
}
} else {
$fp = @$openfunc($this->addr, $this->port, $errno, $errstr, $timeout);
}
if (!$fp) {
if ($errno == 0 && !strlen($errstr) && isset($php_errormsg)) {
$errstr = $php_errormsg;
}
@ini_set('track_errors', $old_track_errors);
return $this->raiseError($errstr, $errno);
}
@ini_set('track_errors', $old_track_errors);
$this->fp = $fp;
$this->setTimeout();
return $this->setBlocking($this->blocking);
}
/**
* Disconnects from the peer, closes the socket.
*
* @access public
* @return mixed true on success or a PEAR_Error instance otherwise
*/
function disconnect()
{
if (!is_resource($this->fp)) {
return $this->raiseError('not connected');
}
@fclose($this->fp);
$this->fp = null;
return true;
}
/**
* Set the newline character/sequence to use.
*
* @param string $newline Newline character(s)
* @return boolean True
*/
function setNewline($newline)
{
$this->newline = $newline;
return true;
}
/**
* Find out if the socket is in blocking mode.
*
* @access public
* @return boolean The current blocking mode.
*/
function isBlocking()
{
return $this->blocking;
}
/**
* Sets whether the socket connection should be blocking or
* not. A read call to a non-blocking socket will return immediately
* if there is no data available, whereas it will block until there
* is data for blocking sockets.
*
* @param boolean $mode True for blocking sockets, false for nonblocking.
*
* @access public
* @return mixed true on success or a PEAR_Error instance otherwise
*/
function setBlocking($mode)
{
if (!is_resource($this->fp)) {
return $this->raiseError('not connected');
}
$this->blocking = $mode;
stream_set_blocking($this->fp, (int)$this->blocking);
return true;
}
/**
* Sets the timeout value on socket descriptor,
* expressed in the sum of seconds and microseconds
*
* @param integer $seconds Seconds.
* @param integer $microseconds Microseconds, optional.
*
* @access public
* @return mixed True on success or false on failure or
* a PEAR_Error instance when not connected
*/
function setTimeout($seconds = null, $microseconds = null)
{
if (!is_resource($this->fp)) {
return $this->raiseError('not connected');
}
if ($seconds === null && $microseconds === null) {
$seconds = (int) $this->timeout;
$microseconds = (int) (($this->timeout - $seconds) * 1000000);
} else {
$this->timeout = $seconds + $microseconds/1000000;
}
if ($this->timeout > 0) {
return stream_set_timeout($this->fp, (int) $seconds, (int) $microseconds);
}
else {
return false;
}
}
/**
* Sets the file buffering size on the stream.
* See php's stream_set_write_buffer for more information.
*
* @param integer $size Write buffer size.
*
* @access public
* @return mixed on success or an PEAR_Error object otherwise
*/
function setWriteBuffer($size)
{
if (!is_resource($this->fp)) {
return $this->raiseError('not connected');
}
$returned = stream_set_write_buffer($this->fp, $size);
if ($returned == 0) {
return true;
}
return $this->raiseError('Cannot set write buffer.');
}
/**
* Returns information about an existing socket resource.
* Currently returns four entries in the result array:
*
* <p>
* timed_out (bool) - The socket timed out waiting for data<br>
* blocked (bool) - The socket was blocked<br>
* eof (bool) - Indicates EOF event<br>
* unread_bytes (int) - Number of bytes left in the socket buffer<br>
* </p>
*
* @access public
* @return mixed Array containing information about existing socket
* resource or a PEAR_Error instance otherwise
*/
function getStatus()
{
if (!is_resource($this->fp)) {
return $this->raiseError('not connected');
}
return stream_get_meta_data($this->fp);
}
/**
* Get a specified line of data
*
* @param int $size Reading ends when size - 1 bytes have been read,
* or a newline or an EOF (whichever comes first).
* If no size is specified, it will keep reading from
* the stream until it reaches the end of the line.
*
* @access public
* @return mixed $size bytes of data from the socket, or a PEAR_Error if
* not connected. If an error occurs, FALSE is returned.
*/
function gets($size = null)
{
if (!is_resource($this->fp)) {
return $this->raiseError('not connected');
}
if (is_null($size)) {
return @fgets($this->fp);
} else {
return @fgets($this->fp, $size);
}
}
/**
* Read a specified amount of data. This is guaranteed to return,
* and has the added benefit of getting everything in one fread()
* chunk; if you know the size of the data you're getting
* beforehand, this is definitely the way to go.
*
* @param integer $size The number of bytes to read from the socket.
*
* @access public
* @return $size bytes of data from the socket, or a PEAR_Error if
* not connected.
*/
function read($size)
{
if (!is_resource($this->fp)) {
return $this->raiseError('not connected');
}
return @fread($this->fp, $size);
}
/**
* Write a specified amount of data.
*
* @param string $data Data to write.
* @param integer $blocksize Amount of data to write at once.
* NULL means all at once.
*
* @access public
* @return mixed If the socket is not connected, returns an instance of
* PEAR_Error.
* If the write succeeds, returns the number of bytes written.
* If the write fails, returns false.
* If the socket times out, returns an instance of PEAR_Error.
*/
function write($data, $blocksize = null)
{
if (!is_resource($this->fp)) {
return $this->raiseError('not connected');
}
if (is_null($blocksize) && !OS_WINDOWS) {
$written = @fwrite($this->fp, $data);
// Check for timeout or lost connection
if (!$written) {
$meta_data = $this->getStatus();
if (!is_array($meta_data)) {
return $meta_data; // PEAR_Error
}
if (!empty($meta_data['timed_out'])) {
return $this->raiseError('timed out');
}
}
return $written;
} else {
if (is_null($blocksize)) {
$blocksize = 1024;
}
$pos = 0;
$size = strlen($data);
while ($pos < $size) {
$written = @fwrite($this->fp, substr($data, $pos, $blocksize));
// Check for timeout or lost connection
if (!$written) {
$meta_data = $this->getStatus();
if (!is_array($meta_data)) {
return $meta_data; // PEAR_Error
}
if (!empty($meta_data['timed_out'])) {
return $this->raiseError('timed out');
}
return $written;
}
$pos += $written;
}
return $pos;
}
}
/**
* Write a line of data to the socket, followed by a trailing newline.
*
* @param string $data Data to write
*
* @access public
* @return mixed fwrite() result, or PEAR_Error when not connected
*/
function writeLine($data)
{
if (!is_resource($this->fp)) {
return $this->raiseError('not connected');
}
return fwrite($this->fp, $data . $this->newline);
}
/**
* Tests for end-of-file on a socket descriptor.
*
* Also returns true if the socket is disconnected.
*
* @access public
* @return bool
*/
function eof()
{
return (!is_resource($this->fp) || feof($this->fp));
}
/**
* Reads a byte of data
*
* @access public
* @return 1 byte of data from the socket, or a PEAR_Error if
* not connected.
*/
function readByte()
{
if (!is_resource($this->fp)) {
return $this->raiseError('not connected');
}
return ord(@fread($this->fp, 1));
}
/**
* Reads a word of data
*
* @access public
* @return 1 word of data from the socket, or a PEAR_Error if
* not connected.
*/
function readWord()
{
if (!is_resource($this->fp)) {
return $this->raiseError('not connected');
}
$buf = @fread($this->fp, 2);
return (ord($buf[0]) + (ord($buf[1]) << 8));
}
/**
* Reads an int of data
*
* @access public
* @return integer 1 int of data from the socket, or a PEAR_Error if
* not connected.
*/
function readInt()
{
if (!is_resource($this->fp)) {
return $this->raiseError('not connected');
}
$buf = @fread($this->fp, 4);
return (ord($buf[0]) + (ord($buf[1]) << 8) +
(ord($buf[2]) << 16) + (ord($buf[3]) << 24));
}
/**
* Reads a zero-terminated string of data
*
* @access public
* @return string, or a PEAR_Error if
* not connected.
*/
function readString()
{
if (!is_resource($this->fp)) {
return $this->raiseError('not connected');
}
$string = '';
while (($char = @fread($this->fp, 1)) != "\x00") {
$string .= $char;
}
return $string;
}
/**
* Reads an IP Address and returns it in a dot formatted string
*
* @access public
* @return Dot formatted string, or a PEAR_Error if
* not connected.
*/
function readIPAddress()
{
if (!is_resource($this->fp)) {
return $this->raiseError('not connected');
}
$buf = @fread($this->fp, 4);
return sprintf('%d.%d.%d.%d', ord($buf[0]), ord($buf[1]),
ord($buf[2]), ord($buf[3]));
}
/**
* Read until either the end of the socket or a newline, whichever
* comes first. Strips the trailing newline from the returned data.
*
* @access public
* @return All available data up to a newline, without that
* newline, or until the end of the socket, or a PEAR_Error if
* not connected.
*/
function readLine()
{
if (!is_resource($this->fp)) {
return $this->raiseError('not connected');
}
$line = '';
$timeout = time() + $this->timeout;
while (!feof($this->fp) && (!$this->timeout || time() < $timeout)) {
$line .= @fgets($this->fp, $this->lineLength);
if (substr($line, -1) == "\n") {
return rtrim($line, $this->newline);
}
}
return $line;
}
/**
* Read until the socket closes, or until there is no more data in
* the inner PHP buffer. If the inner buffer is empty, in blocking
* mode we wait for at least 1 byte of data. Therefore, in
* blocking mode, if there is no data at all to be read, this
* function will never exit (unless the socket is closed on the
* remote end).
*
* @access public
*
* @return string All data until the socket closes, or a PEAR_Error if
* not connected.
*/
function readAll()
{
if (!is_resource($this->fp)) {
return $this->raiseError('not connected');
}
$data = '';
while (!feof($this->fp)) {
$data .= @fread($this->fp, $this->lineLength);
}
return $data;
}
/**
* Runs the equivalent of the select() system call on the socket
* with a timeout specified by tv_sec and tv_usec.
*
* @param integer $state Which of read/write/error to check for.
* @param integer $tv_sec Number of seconds for timeout.
* @param integer $tv_usec Number of microseconds for timeout.
*
* @access public
* @return False if select fails, integer describing which of read/write/error
* are ready, or PEAR_Error if not connected.
*/
function select($state, $tv_sec, $tv_usec = 0)
{
if (!is_resource($this->fp)) {
return $this->raiseError('not connected');
}
$read = null;
$write = null;
$except = null;
if ($state & NET_SOCKET_READ) {
$read[] = $this->fp;
}
if ($state & NET_SOCKET_WRITE) {
$write[] = $this->fp;
}
if ($state & NET_SOCKET_ERROR) {
$except[] = $this->fp;
}
if (false === ($sr = stream_select($read, $write, $except,
$tv_sec, $tv_usec))) {
return false;
}
$result = 0;
if (count($read)) {
$result |= NET_SOCKET_READ;
}
if (count($write)) {
$result |= NET_SOCKET_WRITE;
}
if (count($except)) {
$result |= NET_SOCKET_ERROR;
}
return $result;
}
/**
* Turns encryption on/off on a connected socket.
*
* @param bool $enabled Set this parameter to true to enable encryption
* and false to disable encryption.
* @param integer $type Type of encryption. See stream_socket_enable_crypto()
* for values.
* @param boolean $verify_peer Require verification of SSL certificate used
* @param boolean $verify_peer_name Require verification of peer name
* @param boolean $allow_self_signed Allow self-signed certificates. Requires verify_peer
*
* @see http://se.php.net/manual/en/function.stream-socket-enable-crypto.php
* @access public
* @return false on error, true on success and 0 if there isn't enough data
* and the user should try again (non-blocking sockets only).
* A PEAR_Error object is returned if the socket is not
* connected
*/
function enableCrypto($enabled, $type, $verify_peer, $verify_peer_name, $allow_self_signed)
{
if (version_compare(phpversion(), "5.1.0", ">=")) {
if (!is_resource($this->fp)) {
return $this->raiseError('not connected');
}
// 5.6.0 Added verify_peer_name. verify_peer default changed to TRUE.
if (version_compare(phpversion(), "5.6.0", ">="))
stream_context_set_option($this->fp, array('ssl' => array('verify_peer' => $verify_peer, 'verify_peer_name' => $verify_peer_name, 'allow_self_signed' => $allow_self_signed)));
else
stream_context_set_option($this->fp, array('ssl' => array('verify_peer' => $verify_peer, 'allow_self_signed' => $allow_self_signed)));
return @stream_socket_enable_crypto($this->fp, $enabled, $type);
} else {
$msg = 'Net_Socket::enableCrypto() requires php version >= 5.1.0';
return $this->raiseError($msg);
}
}
/**
* Z-Push helper for error logging
* removing PEAR dependency
*
* @param string debug message
* @return boolean always false as there was an error
* @access private
*/
function raiseError($message) {
ZLog::Write(LOGLEVEL_ERROR, "Net_Socket error: ". $message);
return false;
}
}

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

View file

@ -1,945 +0,0 @@
<?php
/**
* RFC 822 Email address list validation Utility
*
* PHP version 5
*
* LICENSE:
*
* Copyright (c) 2001-2010, Richard Heyes
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* o Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* o Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* o The names of the authors may not be used to endorse or promote
* products derived from this software without specific prior written
* permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* @category Mail
* @package Mail
* @author Richard Heyes <richard@phpguru.org>
* @author Chuck Hagenbuch <chuck@horde.org
* @copyright 2001-2010 Richard Heyes
* @license http://opensource.org/licenses/bsd-license.php New BSD License
* @version CVS: $Id: RFC822.php 294749 2010-02-08 08:22:25Z clockwerx $
* @link http://pear.php.net/package/Mail/
*/
/**
* RFC 822 Email address list validation Utility
*
* What is it?
*
* This class will take an address string, and parse it into it's consituent
* parts, be that either addresses, groups, or combinations. Nested groups
* are not supported. The structure it returns is pretty straight forward,
* and is similar to that provided by the imap_rfc822_parse_adrlist(). Use
* print_r() to view the structure.
*
* How do I use it?
*
* $address_string = 'My Group: "Richard" <richard@localhost> (A comment), ted@example.com (Ted Bloggs), Barney;';
* $structure = Mail_RFC822::parseAddressList($address_string, 'example.com', true)
* print_r($structure);
*
* @author Richard Heyes <richard@phpguru.org>
* @author Chuck Hagenbuch <chuck@horde.org>
* @version $Revision$
* @license BSD
* @package Mail
*/
class Mail_RFC822 {
/**
* The address being parsed by the RFC822 object.
* @var string $address
*/
var $address = '';
/**
* The default domain to use for unqualified addresses.
* @var string $default_domain
*/
var $default_domain = 'localhost';
/**
* Should we return a nested array showing groups, or flatten everything?
* @var boolean $nestGroups
*/
var $nestGroups = true;
/**
* Whether or not to validate atoms for non-ascii characters.
* @var boolean $validate
*/
var $validate = true;
/**
* The array of raw addresses built up as we parse.
* @var array $addresses
*/
var $addresses = array();
/**
* The final array of parsed address information that we build up.
* @var array $structure
*/
var $structure = array();
/**
* The current error message, if any.
* @var string $error
*/
var $error = null;
/**
* An internal counter/pointer.
* @var integer $index
*/
var $index = null;
/**
* The number of groups that have been found in the address list.
* @var integer $num_groups
*/
var $num_groups = 0;
/**
* A variable so that we can tell whether or not we're inside a
* Mail_RFC822 object.
* @var boolean $mailRFC822
*/
var $mailRFC822 = true;
/**
* A limit after which processing stops
* @var int $limit
*/
var $limit = null;
/**
* Sets up the object. The address must either be set here or when
* calling parseAddressList(). One or the other.
*
* @param string $address The address(es) to validate.
* @param string $default_domain Default domain/host etc. If not supplied, will be set to localhost.
* @param boolean $nest_groups Whether to return the structure with groups nested for easier viewing.
* @param boolean $validate Whether to validate atoms. Turn this off if you need to run addresses through before encoding the personal names, for instance.
*
* @return object Mail_RFC822 A new Mail_RFC822 object.
*/
public function __construct($address = null, $default_domain = null, $nest_groups = null, $validate = null, $limit = null)
{
if (isset($address)) $this->address = $address;
if (isset($default_domain)) $this->default_domain = $default_domain;
if (isset($nest_groups)) $this->nestGroups = $nest_groups;
if (isset($validate)) $this->validate = $validate;
if (isset($limit)) $this->limit = $limit;
}
/**
* Starts the whole process. The address must either be set here
* or when creating the object. One or the other.
*
* @param string $address The address(es) to validate.
* @param string $default_domain Default domain/host etc.
* @param boolean $nest_groups Whether to return the structure with groups nested for easier viewing.
* @param boolean $validate Whether to validate atoms. Turn this off if you need to run addresses through before encoding the personal names, for instance.
*
* @return array A structured array of addresses.
*/
public function parseAddressList($address = null, $default_domain = null, $nest_groups = null, $validate = null, $limit = null)
{
if (!isset($this) || !isset($this->mailRFC822)) {
$obj = new Mail_RFC822($address, $default_domain, $nest_groups, $validate, $limit);
return $obj->parseAddressList();
}
if (isset($address)) $this->address = $address;
// z-push addition
if (strlen(trim($this->address)) == 0) return array();
if (isset($default_domain)) $this->default_domain = $default_domain;
if (isset($nest_groups)) $this->nestGroups = $nest_groups;
if (isset($validate)) $this->validate = $validate;
if (isset($limit)) $this->limit = $limit;
$this->structure = array();
$this->addresses = array();
$this->error = null;
$this->index = null;
// Unfold any long lines in $this->address.
$this->address = preg_replace('/\r?\n/', "\r\n", $this->address);
$this->address = preg_replace('/\r\n(\t| )+/', ' ', $this->address);
while ($this->address = $this->_splitAddresses($this->address));
if ($this->address === false || isset($this->error)) {
//require_once 'PEAR.php';
return $this->raiseError($this->error);
}
// Validate each address individually. If we encounter an invalid
// address, stop iterating and return an error immediately.
foreach ($this->addresses as $address) {
$valid = $this->_validateAddress($address);
if ($valid === false || isset($this->error)) {
//require_once 'PEAR.php';
return $this->raiseError($this->error);
}
if (!$this->nestGroups) {
$this->structure = array_merge($this->structure, $valid);
} else {
$this->structure[] = $valid;
}
}
return $this->structure;
}
/**
* Splits an address into separate addresses.
*
* @param string $address The addresses to split.
* @return boolean Success or failure.
*/
protected function _splitAddresses($address)
{
if (!empty($this->limit) && count($this->addresses) == $this->limit) {
return '';
}
if ($this->_isGroup($address) && !isset($this->error)) {
$split_char = ';';
$is_group = true;
} elseif (!isset($this->error)) {
$split_char = ',';
$is_group = false;
} elseif (isset($this->error)) {
return false;
}
// Split the string based on the above ten or so lines.
$parts = explode($split_char, $address);
$string = $this->_splitCheck($parts, $split_char);
// If a group...
if ($is_group) {
// If $string does not contain a colon outside of
// brackets/quotes etc then something's fubar.
// First check there's a colon at all:
if (strpos($string, ':') === false) {
$this->error = 'Invalid address: ' . $string;
return false;
}
// Now check it's outside of brackets/quotes:
if (!$this->_splitCheck(explode(':', $string), ':')) {
return false;
}
// We must have a group at this point, so increase the counter:
$this->num_groups++;
}
// $string now contains the first full address/group.
// Add to the addresses array.
$this->addresses[] = array(
'address' => trim($string),
'group' => $is_group
);
// Remove the now stored address from the initial line, the +1
// is to account for the explode character.
$address = trim(substr($address, strlen($string) + 1));
// If the next char is a comma and this was a group, then
// there are more addresses, otherwise, if there are any more
// chars, then there is another address.
if ($is_group && substr($address, 0, 1) == ','){
$address = trim(substr($address, 1));
return $address;
} elseif (strlen($address) > 0) {
return $address;
} else {
return '';
}
// If you got here then something's off
return false;
}
/**
* Checks for a group at the start of the string.
*
* @param string $address The address to check.
* @return boolean Whether or not there is a group at the start of the string.
*/
protected function _isGroup($address)
{
// First comma not in quotes, angles or escaped:
$parts = explode(',', $address);
$string = $this->_splitCheck($parts, ',');
// Now we have the first address, we can reliably check for a
// group by searching for a colon that's not escaped or in
// quotes or angle brackets.
if (count($parts = explode(':', $string)) > 1) {
$string2 = $this->_splitCheck($parts, ':');
return ($string2 !== $string);
} else {
return false;
}
}
/**
* A common function that will check an exploded string.
*
* @param array $parts The exloded string.
* @param string $char The char that was exploded on.
* @return mixed False if the string contains unclosed quotes/brackets, or the string on success.
*/
protected function _splitCheck($parts, $char)
{
$string = $parts[0];
for ($i = 0; $i < count($parts); $i++) {
if ($this->_hasUnclosedQuotes($string)
|| $this->_hasUnclosedBrackets($string, '<>')
|| $this->_hasUnclosedBrackets($string, '[]')
|| $this->_hasUnclosedBrackets($string, '()')
|| substr($string, -1) == '\\') {
if (isset($parts[$i + 1])) {
$string = $string . $char . $parts[$i + 1];
} else {
$this->error = 'Invalid address spec. Unclosed bracket or quotes';
return false;
}
} else {
$this->index = $i;
break;
}
}
return $string;
}
/**
* Checks if a string has unclosed quotes or not.
*
* @param string $string The string to check.
* @return boolean True if there are unclosed quotes inside the string,
* false otherwise.
*/
protected function _hasUnclosedQuotes($string)
{
$string = trim($string);
$iMax = strlen($string);
$in_quote = false;
$i = $slashes = 0;
for (; $i < $iMax; ++$i) {
switch ($string[$i]) {
case '\\':
++$slashes;
break;
case '"':
if ($slashes % 2 == 0) {
$in_quote = !$in_quote;
}
// Fall through to default action below.
default:
$slashes = 0;
break;
}
}
return $in_quote;
}
/**
* Checks if a string has an unclosed brackets or not. IMPORTANT:
* This function handles both angle brackets and square brackets;
*
* @param string $string The string to check.
* @param string $chars The characters to check for.
* @return boolean True if there are unclosed brackets inside the string, false otherwise.
*/
protected function _hasUnclosedBrackets($string, $chars)
{
$num_angle_start = substr_count($string, $chars[0]);
$num_angle_end = substr_count($string, $chars[1]);
$this->_hasUnclosedBracketsSub($string, $num_angle_start, $chars[0]);
$this->_hasUnclosedBracketsSub($string, $num_angle_end, $chars[1]);
if ($num_angle_start < $num_angle_end) {
$this->error = 'Invalid address spec. Unmatched quote or bracket (' . $chars . ')';
return false;
} else {
return ($num_angle_start > $num_angle_end);
}
}
/**
* Sub function that is used only by hasUnclosedBrackets().
*
* @param string $string The string to check.
* @param integer &$num The number of occurences.
* @param string $char The character to count.
* @return integer The number of occurences of $char in $string, adjusted for backslashes.
*/
protected function _hasUnclosedBracketsSub($string, &$num, $char)
{
$parts = explode($char, $string);
for ($i = 0; $i < count($parts); $i++){
if (substr($parts[$i], -1) == '\\' || $this->_hasUnclosedQuotes($parts[$i]))
$num--;
if (isset($parts[$i + 1]))
$parts[$i + 1] = $parts[$i] . $char . $parts[$i + 1];
}
return $num;
}
/**
* Function to begin checking the address.
*
* @param string $address The address to validate.
* @return mixed False on failure, or a structured array of address information on success.
*/
protected function _validateAddress($address)
{
$is_group = false;
$addresses = array();
if ($address['group']) {
$is_group = true;
// Get the group part of the name
$parts = explode(':', $address['address']);
$groupname = $this->_splitCheck($parts, ':');
$structure = array();
// And validate the group part of the name.
if (!$this->_validatePhrase($groupname)){
$this->error = 'Group name did not validate.';
return false;
} else {
// Don't include groups if we are not nesting
// them. This avoids returning invalid addresses.
if ($this->nestGroups) {
$structure = new stdClass;
$structure->groupname = $groupname;
}
}
$address['address'] = ltrim(substr($address['address'], strlen($groupname . ':')));
}
// If a group then split on comma and put into an array.
// Otherwise, Just put the whole address in an array.
if ($is_group) {
while (strlen($address['address']) > 0) {
$parts = explode(',', $address['address']);
$addresses[] = $this->_splitCheck($parts, ',');
$address['address'] = trim(substr($address['address'], strlen(end($addresses) . ',')));
}
} else {
$addresses[] = $address['address'];
}
// Trim the whitespace from all of the address strings.
array_map('trim', $addresses);
// Validate each mailbox.
// Format could be one of: name <geezer@domain.com>
// geezer@domain.com
// geezer
// ... or any other format valid by RFC 822.
for ($i = 0; $i < count($addresses); $i++) {
if (!$this->validateMailbox($addresses[$i])) {
if (empty($this->error)) {
$this->error = 'Validation failed for: ' . $addresses[$i];
}
return false;
}
}
// Nested format
if ($this->nestGroups) {
if ($is_group) {
$structure->addresses = $addresses;
} else {
$structure = $addresses[0];
}
// Flat format
} else {
if ($is_group) {
$structure = array_merge($structure, $addresses);
} else {
$structure = $addresses;
}
}
return $structure;
}
/**
* Function to validate a phrase.
*
* @param string $phrase The phrase to check.
* @return boolean Success or failure.
*/
protected function _validatePhrase($phrase)
{
// Splits on one or more Tab or space.
$parts = preg_split('/[ \\x09]+/', $phrase, -1, PREG_SPLIT_NO_EMPTY);
$phrase_parts = array();
while (count($parts) > 0){
$phrase_parts[] = $this->_splitCheck($parts, ' ');
for ($i = 0; $i < $this->index + 1; $i++)
array_shift($parts);
}
foreach ($phrase_parts as $part) {
// If quoted string:
if (substr($part, 0, 1) == '"') {
if (!$this->_validateQuotedString($part)) {
return false;
}
continue;
}
// Otherwise it's an atom:
if (!$this->_validateAtom($part)) return false;
}
return true;
}
/**
* Function to validate an atom which from rfc822 is:
* atom = 1*<any CHAR except specials, SPACE and CTLs>
*
* If validation ($this->validate) has been turned off, then
* validateAtom() doesn't actually check anything. This is so that you
* can split a list of addresses up before encoding personal names
* (umlauts, etc.), for example.
*
* @param string $atom The string to check.
* @return boolean Success or failure.
*/
protected function _validateAtom($atom)
{
if (!$this->validate) {
// Validation has been turned off; assume the atom is okay.
return true;
}
// Check for any char from ASCII 0 - ASCII 127
if (!preg_match('/^[\\x00-\\x7E]+$/i', $atom, $matches)) {
return false;
}
// Check for specials:
if (preg_match('/[][()<>@,;\\:". ]/', $atom)) {
return false;
}
// Check for control characters (ASCII 0-31):
if (preg_match('/[\\x00-\\x1F]+/', $atom)) {
return false;
}
return true;
}
/**
* Function to validate quoted string, which is:
* quoted-string = <"> *(qtext/quoted-pair) <">
*
* @param string $qstring The string to check
* @return boolean Success or failure.
*/
protected function _validateQuotedString($qstring)
{
// Leading and trailing "
$qstring = substr($qstring, 1, -1);
// Perform check, removing quoted characters first.
return !preg_match('/[\x0D\\\\"]/', preg_replace('/\\\\./', '', $qstring));
}
/**
* Function to validate a mailbox, which is:
* mailbox = addr-spec ; simple address
* / phrase route-addr ; name and route-addr
*
* @param string &$mailbox The string to check.
* @return boolean Success or failure.
*/
public function validateMailbox(&$mailbox)
{
// A couple of defaults.
$phrase = '';
$comment = '';
$comments = array();
// Catch any RFC822 comments and store them separately.
$_mailbox = $mailbox;
while (strlen(trim($_mailbox)) > 0) {
$parts = explode('(', $_mailbox);
$before_comment = $this->_splitCheck($parts, '(');
if ($before_comment != $_mailbox) {
// First char should be a (.
$comment = substr(str_replace($before_comment, '', $_mailbox), 1);
$parts = explode(')', $comment);
$comment = $this->_splitCheck($parts, ')');
$comments[] = $comment;
// +2 is for the brackets
$_mailbox = substr($_mailbox, strpos($_mailbox, '('.$comment)+strlen($comment)+2);
} else {
break;
}
}
foreach ($comments as $comment) {
$mailbox = str_replace("($comment)", '', $mailbox);
}
$mailbox = trim($mailbox);
// Check for name + route-addr
if (substr($mailbox, -1) == '>' && substr($mailbox, 0, 1) != '<') {
$parts = explode('<', $mailbox);
$name = $this->_splitCheck($parts, '<');
$phrase = trim($name);
$route_addr = trim(substr($mailbox, strlen($name.'<'), -1));
//z-push fix for umlauts and other special chars
if (substr($phrase, 0, 1) != '"' && substr($phrase, -1) != '"') {
$phrase = '"'.$phrase.'"';
}
if ($this->_validatePhrase($phrase) === false || ($route_addr = $this->_validateRouteAddr($route_addr)) === false) {
return false;
}
// Only got addr-spec
} else {
// First snip angle brackets if present.
if (substr($mailbox, 0, 1) == '<' && substr($mailbox, -1) == '>') {
$addr_spec = substr($mailbox, 1, -1);
} else {
$addr_spec = $mailbox;
}
if (($addr_spec = $this->_validateAddrSpec($addr_spec)) === false) {
return false;
}
}
// Construct the object that will be returned.
$mbox = new stdClass();
// Add the phrase (even if empty) and comments
$mbox->personal = $phrase;
$mbox->comment = isset($comments) ? $comments : array();
if (isset($route_addr)) {
$mbox->mailbox = $route_addr['local_part'];
$mbox->host = $route_addr['domain'];
$route_addr['adl'] !== '' ? $mbox->adl = $route_addr['adl'] : '';
} else {
$mbox->mailbox = $addr_spec['local_part'];
$mbox->host = $addr_spec['domain'];
}
$mailbox = $mbox;
return true;
}
/**
* This function validates a route-addr which is:
* route-addr = "<" [route] addr-spec ">"
*
* Angle brackets have already been removed at the point of
* getting to this function.
*
* @param string $route_addr The string to check.
* @return mixed False on failure, or an array containing validated address/route information on success.
*/
protected function _validateRouteAddr($route_addr)
{
// Check for colon.
if (strpos($route_addr, ':') !== false) {
$parts = explode(':', $route_addr);
$route = $this->_splitCheck($parts, ':');
} else {
$route = $route_addr;
}
// If $route is same as $route_addr then the colon was in
// quotes or brackets or, of course, non existent.
if ($route === $route_addr){
unset($route);
$addr_spec = $route_addr;
if (($addr_spec = $this->_validateAddrSpec($addr_spec)) === false) {
return false;
}
} else {
// Validate route part.
if (($route = $this->_validateRoute($route)) === false) {
return false;
}
$addr_spec = substr($route_addr, strlen($route . ':'));
// Validate addr-spec part.
if (($addr_spec = $this->_validateAddrSpec($addr_spec)) === false) {
return false;
}
}
if (isset($route)) {
$return['adl'] = $route;
} else {
$return['adl'] = '';
}
$return = array_merge($return, $addr_spec);
return $return;
}
/**
* Function to validate a route, which is:
* route = 1#("@" domain) ":"
*
* @param string $route The string to check.
* @return mixed False on failure, or the validated $route on success.
*/
protected function _validateRoute($route)
{
// Split on comma.
$domains = explode(',', trim($route));
foreach ($domains as $domain) {
$domain = str_replace('@', '', trim($domain));
if (!$this->_validateDomain($domain)) return false;
}
return $route;
}
/**
* Function to validate a domain, though this is not quite what
* you expect of a strict internet domain.
*
* domain = sub-domain *("." sub-domain)
*
* @param string $domain The string to check.
* @return mixed False on failure, or the validated domain on success.
*/
protected function _validateDomain($domain)
{
// Note the different use of $subdomains and $sub_domains
$subdomains = explode('.', $domain);
while (count($subdomains) > 0) {
$sub_domains[] = $this->_splitCheck($subdomains, '.');
for ($i = 0; $i < $this->index + 1; $i++)
array_shift($subdomains);
}
foreach ($sub_domains as $sub_domain) {
if (!$this->_validateSubdomain(trim($sub_domain)))
return false;
}
// Managed to get here, so return input.
return $domain;
}
/**
* Function to validate a subdomain:
* subdomain = domain-ref / domain-literal
*
* @param string $subdomain The string to check.
* @return boolean Success or failure.
*/
protected function _validateSubdomain($subdomain)
{
if (preg_match('|^\[(.*)]$|', $subdomain, $arr)){
if (!$this->_validateDliteral($arr[1])) return false;
} else {
if (!$this->_validateAtom($subdomain)) return false;
}
// Got here, so return successful.
return true;
}
/**
* Function to validate a domain literal:
* domain-literal = "[" *(dtext / quoted-pair) "]"
*
* @param string $dliteral The string to check.
* @return boolean Success or failure.
*/
protected function _validateDliteral($dliteral)
{
return !preg_match('/(.)[][\x0D\\\\]/', $dliteral, $matches) && $matches[1] != '\\';
}
/**
* Function to validate an addr-spec.
*
* addr-spec = local-part "@" domain
*
* @param string $addr_spec The string to check.
* @return mixed False on failure, or the validated addr-spec on success.
*/
protected function _validateAddrSpec($addr_spec)
{
$addr_spec = trim($addr_spec);
// Split on @ sign if there is one.
if (strpos($addr_spec, '@') !== false) {
$parts = explode('@', $addr_spec);
$local_part = $this->_splitCheck($parts, '@');
$domain = substr($addr_spec, strlen($local_part . '@'));
// No @ sign so assume the default domain.
} else {
$local_part = $addr_spec;
$domain = $this->default_domain;
}
if (($local_part = $this->_validateLocalPart($local_part)) === false) return false;
if (($domain = $this->_validateDomain($domain)) === false) return false;
// Got here so return successful.
return array('local_part' => $local_part, 'domain' => $domain);
}
/**
* Function to validate the local part of an address:
* local-part = word *("." word)
*
* @param string $local_part
* @return mixed False on failure, or the validated local part on success.
*/
protected function _validateLocalPart($local_part)
{
$parts = explode('.', $local_part);
$words = array();
// Split the local_part into words.
while (count($parts) > 0) {
$words[] = $this->_splitCheck($parts, '.');
for ($i = 0; $i < $this->index + 1; $i++) {
array_shift($parts);
}
}
// Validate each word.
foreach ($words as $word) {
// word cannot be empty (#17317)
if ($word === '') {
return false;
}
// If this word contains an unquoted space, it is invalid. (6.2.4)
if (strpos($word, ' ') && $word[0] !== '"')
{
return false;
}
if ($this->_validatePhrase(trim($word)) === false) return false;
}
// Managed to get here, so return the input.
return $local_part;
}
/**
* Returns an approximate count of how many addresses are in the
* given string. This is APPROXIMATE as it only splits based on a
* comma which has no preceding backslash. Could be useful as
* large amounts of addresses will end up producing *large*
* structures when used with parseAddressList().
*
* @param string $data Addresses to count
* @return int Approximate count
*/
function approximateCount($data)
{
return count(preg_split('/(?<!\\\\),/', $data));
}
/**
* This is a email validating function separate to the rest of the
* class. It simply validates whether an email is of the common
* internet form: <user>@<domain>. This can be sufficient for most
* people. Optional stricter mode can be utilised which restricts
* mailbox characters allowed to alphanumeric, full stop, hyphen
* and underscore.
*
* @param string $data Address to check
* @param boolean $strict Optional stricter mode
* @return mixed False if it fails, an indexed array
* username/domain if it matches
*/
function isValidInetAddress($data, $strict = false)
{
$regex = $strict ? '/^([.0-9a-z_+-]+)@(([0-9a-z-]+\.)+[0-9a-z]{2,})$/i' : '/^([*+!.&#$|\'\\%\/0-9a-z^_`{}=?~:-]+)@(([0-9a-z-]+\.)+[0-9a-z]{2,})$/i';
if (preg_match($regex, trim($data), $matches)) {
return array($matches[1], $matches[2]);
} else {
return false;
}
}
/**
* Z-Push helper for error logging
* removing PEAR dependency
*
* @param string debug message
* @return boolean always false as there was an error
* @access private
*/
function raiseError($message) {
ZLog::Write(LOGLEVEL_ERROR, "z_RFC822 error: ". $message);
return false;
}
}

View file

@ -1,704 +0,0 @@
<?php
/*
This class contains code from rtfclass.php that was written by Markus Fischer and placed by him under
GPLv2 License.
=======================================NOTES FROM ORIGINAL AUTHOR====================================
Rich Text Format - Parsing Class
================================
(c) 2000 Markus Fischer
<mfischer@josefine.ben.tuwien.ac.at>
http://josefine.ben.tuwien.ac.at/~mfischer/
Latest versions of this class can always be found at
http://josefine.ben.tuwien.ac.at/~mfischer/developing/php/rtf/rtfclass.phps
Testing suite is available at
http://josefine.ben.tuwien.ac.at/~mfischer/developing/php/rtf/
License: GPLv2
Specification:
http://msdn.microsoft.com/library/default.asp?URL=/library/specs/rtfspec.htm
General Notes:
==============
Unknown or unspupported control symbols are silently gnored
Group stacking is still not supported :(
group stack logic implemented; however not really used yet
=====================================================================================================
It was modified by me (Andreas Brodowski) to allow compressed RTF being uncompressed by code I ported from
Java to PHP and adapted according the needs of Z-Push.
Currently it is being used to detect empty RTF Streams from Nokia Phones in MfE Clients
It needs to be used by other backend writers that needs to have notes in calendar, appointment or tasks
objects to be written to their databases since devices send them usually in RTF Format... With Zarafa
you can write them directly to DB and Zarafa is doing the conversion job. Other Groupware systems usually
don't have this possibility...
*/
class rtf {
var $LZRTF_HDR_DATA = "{\\rtf1\\ansi\\mac\\deff0\\deftab720{\\fonttbl;}{\\f0\\fnil \\froman \\fswiss \\fmodern \\fscript \\fdecor MS Sans SerifSymbolArialTimes New RomanCourier{\\colortbl\\red0\\green0\\blue0\n\r\\par \\pard\\plain\\f0\\fs20\\b\\i\\u\\tab\\tx";
var $LZRTF_HDR_LEN = 207;
var $CRC32_TABLE = array( 0x00000000,0x77073096,0xEE0E612C,0x990951BA,0x076DC419,0x706AF48F,0xE963A535,0x9E6495A3,
0x0EDB8832,0x79DCB8A4,0xE0D5E91E,0x97D2D988,0x09B64C2B,0x7EB17CBD,0xE7B82D07,0x90BF1D91,
0x1DB71064,0x6AB020F2,0xF3B97148,0x84BE41DE,0x1ADAD47D,0x6DDDE4EB,0xF4D4B551,0x83D385C7,
0x136C9856,0x646BA8C0,0xFD62F97A,0x8A65C9EC,0x14015C4F,0x63066CD9,0xFA0F3D63,0x8D080DF5,
0x3B6E20C8,0x4C69105E,0xD56041E4,0xA2677172,0x3C03E4D1,0x4B04D447,0xD20D85FD,0xA50AB56B,
0x35B5A8FA,0x42B2986C,0xDBBBC9D6,0xACBCF940,0x32D86CE3,0x45DF5C75,0xDCD60DCF,0xABD13D59,
0x26D930AC,0x51DE003A,0xC8D75180,0xBFD06116,0x21B4F4B5,0x56B3C423,0xCFBA9599,0xB8BDA50F,
0x2802B89E,0x5F058808,0xC60CD9B2,0xB10BE924,0x2F6F7C87,0x58684C11,0xC1611DAB,0xB6662D3D,
0x76DC4190,0x01DB7106,0x98D220BC,0xEFD5102A,0x71B18589,0x06B6B51F,0x9FBFE4A5,0xE8B8D433,
0x7807C9A2,0x0F00F934,0x9609A88E,0xE10E9818,0x7F6A0DBB,0x086D3D2D,0x91646C97,0xE6635C01,
0x6B6B51F4,0x1C6C6162,0x856530D8,0xF262004E,0x6C0695ED,0x1B01A57B,0x8208F4C1,0xF50FC457,
0x65B0D9C6,0x12B7E950,0x8BBEB8EA,0xFCB9887C,0x62DD1DDF,0x15DA2D49,0x8CD37CF3,0xFBD44C65,
0x4DB26158,0x3AB551CE,0xA3BC0074,0xD4BB30E2,0x4ADFA541,0x3DD895D7,0xA4D1C46D,0xD3D6F4FB,
0x4369E96A,0x346ED9FC,0xAD678846,0xDA60B8D0,0x44042D73,0x33031DE5,0xAA0A4C5F,0xDD0D7CC9,
0x5005713C,0x270241AA,0xBE0B1010,0xC90C2086,0x5768B525,0x206F85B3,0xB966D409,0xCE61E49F,
0x5EDEF90E,0x29D9C998,0xB0D09822,0xC7D7A8B4,0x59B33D17,0x2EB40D81,0xB7BD5C3B,0xC0BA6CAD,
0xEDB88320,0x9ABFB3B6,0x03B6E20C,0x74B1D29A,0xEAD54739,0x9DD277AF,0x04DB2615,0x73DC1683,
0xE3630B12,0x94643B84,0x0D6D6A3E,0x7A6A5AA8,0xE40ECF0B,0x9309FF9D,0x0A00AE27,0x7D079EB1,
0xF00F9344,0x8708A3D2,0x1E01F268,0x6906C2FE,0xF762575D,0x806567CB,0x196C3671,0x6E6B06E7,
0xFED41B76,0x89D32BE0,0x10DA7A5A,0x67DD4ACC,0xF9B9DF6F,0x8EBEEFF9,0x17B7BE43,0x60B08ED5,
0xD6D6A3E8,0xA1D1937E,0x38D8C2C4,0x4FDFF252,0xD1BB67F1,0xA6BC5767,0x3FB506DD,0x48B2364B,
0xD80D2BDA,0xAF0A1B4C,0x36034AF6,0x41047A60,0xDF60EFC3,0xA867DF55,0x316E8EEF,0x4669BE79,
0xCB61B38C,0xBC66831A,0x256FD2A0,0x5268E236,0xCC0C7795,0xBB0B4703,0x220216B9,0x5505262F,
0xC5BA3BBE,0xB2BD0B28,0x2BB45A92,0x5CB36A04,0xC2D7FFA7,0xB5D0CF31,0x2CD99E8B,0x5BDEAE1D,
0x9B64C2B0,0xEC63F226,0x756AA39C,0x026D930A,0x9C0906A9,0xEB0E363F,0x72076785,0x05005713,
0x95BF4A82,0xE2B87A14,0x7BB12BAE,0x0CB61B38,0x92D28E9B,0xE5D5BE0D,0x7CDCEFB7,0x0BDBDF21,
0x86D3D2D4,0xF1D4E242,0x68DDB3F8,0x1FDA836E,0x81BE16CD,0xF6B9265B,0x6FB077E1,0x18B74777,
0x88085AE6,0xFF0F6A70,0x66063BCA,0x11010B5C,0x8F659EFF,0xF862AE69,0x616BFFD3,0x166CCF45,
0xA00AE278,0xD70DD2EE,0x4E048354,0x3903B3C2,0xA7672661,0xD06016F7,0x4969474D,0x3E6E77DB,
0xAED16A4A,0xD9D65ADC,0x40DF0B66,0x37D83BF0,0xA9BCAE53,0xDEBB9EC5,0x47B2CF7F,0x30B5FFE9,
0xBDBDF21C,0xCABAC28A,0x53B39330,0x24B4A3A6,0xBAD03605,0xCDD70693,0x54DE5729,0x23D967BF,
0xB3667A2E,0xC4614AB8,0x5D681B02,0x2A6F2B94,0xB40BBE37,0xC30C8EA1,0x5A05DF1B,0x2D02EF8D,
);
var $rtf; // rtf core stream
var $rtf_len; // length in characters of the stream (get performace due avoiding calling strlen everytime)
var $err = array(); // array of error message, no entities on no error
var $wantXML = false; // convert to XML
var $wantHTML = false; // convert to HTML
var $wantASCII = false; // convert to HTML
// the only variable which should be accessed from the outside
var $out; // output data stream (depends on which $wantXXXXX is set to true
var $outstyles; // htmlified styles (generated after parsing if wantHTML
var $styles; // if wantHTML, stylesheet definitions are put in here
// internal parser variables --------------------------------
// control word variables
var $cword; // holds the current (or last) control word, depending on $cw
var $cw; // are we currently parsing a control word ?
var $cfirst; // could this be the first character ? so watch out for control symbols
var $flags = array(); // parser flags
var $queue; // every character which is no sepcial char, not belongs to a control word/symbol; is generally considered being 'plain'
var $stack = array(); // group stack
/* keywords which don't follw the specification (used by Word '97 - 2000) */
// not yet used
var $control_exception = array(
"clFitText",
"clftsWidth(-?[0-9]+)?",
"clNoWrap(-?[0-9]+)?",
"clwWidth(-?[0-9]+)?",
"tdfrmtxtBottom(-?[0-9]+)?",
"tdfrmtxtLeft(-?[0-9]+)?",
"tdfrmtxtRight(-?[0-9]+)?",
"tdfrmtxtTop(-?[0-9]+)?",
"trftsWidthA(-?[0-9]+)?",
"trftsWidthB(-?[0-9]+)?",
"trftsWidth(-?[0-9]+)?",
"trwWithA(-?[0-9]+)?",
"trwWithB(-?[0-9]+)?",
"trwWith(-?[0-9]+)?",
"spectspecifygen(-?[0-9]+)?",
);
var $charset_table = array(
"0" => "ANSI",
"1" => "Default",
"2" => "Symbol",
"77" => "Mac",
"128" => "Shift Jis",
"129" => "Hangul",
"130" => "Johab",
"134" => "GB2312",
"136" => "Big5",
"161" => "Greek",
"162" => "Turkish",
"163" => "Vietnamese",
"177" => "Hebrew",
"178" => "Arabic",
"179" => "Arabic Traditional",
"180" => "Arabic user",
"181" => "Hebrew user",
"186" => "Baltic",
"204" => "Russian",
"222" => "Thai",
"238" => "Eastern European",
"255" => "PC 437",
"255" => "OEM",
);
/* note: the only conversion table used */
var $fontmodifier_table = array(
"bold" => "b",
"italic" => "i",
"underlined" => "u",
"strikethru" => "strike",
);
function rtf() {
$this->rtf_len = 0;
$this->rtf = '';
$this->out = '';
}
// loadrtf - load the raw rtf data to be converted by this class
// data = the raw rtf
function loadrtf($data) {
if (($this->rtf = $this->uncompress($data))) {
$this->rtf_len = strlen($this->rtf);
};
if($this->rtf_len == 0) {
debugLog("No data in stream found");
return false;
};
return true;
}
function output($typ) {
switch($typ) {
case "ascii": $this->wantASCII = true; break;
case "xml": $this->wantXML = true; break;
case "html": $this->wantHTML = true; break;
default: break;
}
}
// uncompress - uncompress compressed rtf data
// src = the compressed raw rtf in LZRTF format
function uncompress($src) {
$header = unpack("LcSize/LuSize/Lmagic/Lcrc32",substr($src,0,16));
$in = 16;
if ($header['cSize'] != strlen($src)-4) {
debugLog("Stream too short");
return false;
}
if ($header['crc32'] != $this->LZRTFCalcCRC32($src,16,(($header['cSize']+4))-16)) {
debugLog("CRC MISMATCH");
return false;
}
if ($header['magic'] == 0x414c454d) { // uncompressed RTF - return as is.
$dest = substr($src,$in,$header['uSize']);
} else if ($header['magic'] == 0x75465a4c) { // compressed RTF - uncompress.
$dst = $this->LZRTF_HDR_DATA;
$out = $this->LZRTF_HDR_LEN;
$oblen = $this->LZRTF_HDR_LEN + $header['uSize'];
$flagCount = 0;
$flags = 0;
while ($out<$oblen) {
$flags = ($flagCount++ % 8 == 0) ? ord($src{$in++}) : $flags >> 1;
if (($flags & 1) == 1) {
$offset = ord($src{$in++});
$length = ord($src{$in++});
$offset = ($offset << 4) | ($length >> 4);
$length = ($length & 0xF) + 2;
$offset = (int)($out / 4096) * 4096 + $offset;
if ($offset >= $out) $offset -= 4096;
$end = $offset + $length;
while ($offset < $end) {
$dst{$out++} = $dst{$offset++};
};
} else {
$dst{$out++} = $src{$in++};
}
}
$src = $dst;
$dest = substr($src,$this->LZRTF_HDR_LEN,$header['uSize']);
} else { // unknown magic - returfn false (please report if this ever happens)
debugLog("Unknown Magic");
return false;
}
return $dest;
}
// LZRTFCalcCRC32 - calculates the CRC32 of the LZRTF data part
// buf = the whole rtf data part
// off = start point of crc calculation
// len = length of data to calculate CRC for
// function is necessary since in RTF there is no XOR 0xffffffff being done (said to be 0x00 unsafe CRC32 calculation
function LZRTFCalcCRC32($buf, $off, $len) {
$c=0;
$end = $off + $len;
for($i=$off;$i < $end;$i++) {
$c=$this->CRC32_TABLE[($c ^ ord($buf{$i})) & 0xFF] ^ (($c >> 8) & 0x00ffffff);
}
return $c;
}
function parserInit() { /* Default values according to the specs */
$this->flags = array(
"fontsize" => 24,
"beginparagraph" => true,
);
}
function parseControl($control, $parameter) {
switch ($control) {
case "fonttbl": // font table definition start
$this->flags["fonttbl"] = true; // signal fonttable control words they are allowed to behave as expected
break;
case "f": // define or set font
if($this->flags["fonttbl"]) { // if its set, the fonttable definition is written to; else its read from
$this->flags["fonttbl_current_write"] = $parameter;
} else {
$this->flags["fonttbl_current_read"] = $parameter;
}
break;
case "fcharset": // this is for preparing flushQueue; it then moves the Queue to $this->fonttable .. instead to formatted output
$this->flags["fonttbl_want_fcharset"] = $parameter;
break;
case "fs": // sets the current fontsize; is used by stylesheets (which are therefore generated on the fly
$this->flags["fontsize"] = $parameter;
break;
case "qc": // handle center alignment
$this->flags["alignment"] = "center";
break;
case "qr": // handle right alignment
$this->flags["alignment"] = "right";
break;
case "pard": // reset paragraph settings (only alignment)
$this->flags["alignment"] = "";
break;
case "par": // define new paragraph (for now, thats a simple break in html) begin new line
$this->flags["beginparagraph"] = true;
if($this->wantHTML) {
$this->out .= "</div>";
}
if($this->wantASCII) {
$this->out .= "\n";
}
break;
case "bnone": // bold
$parameter = "0";
case "b":
// haven'y yet figured out WHY I need a (string)-cast here ... hm
if((string)$parameter == "0")
$this->flags["bold"] = false;
else
$this->flags["bold"] = true;
break;
case "ulnone": // underlined
$parameter = "0";
case "ul":
if((string)$parameter == "0")
$this->flags["underlined"] = false;
else
$this->flags["underlined"] = true;
break;
case "inone": // italic
$parameter = "0";
case "i":
if((string)$parameter == "0")
$this->flags["italic"] = false;
else
$this->flags["italic"] = true;
break;
case "strikenone": // strikethru
$parameter = "0";
case "strike":
if((string)$parameter == "0")
$this->flags["strikethru"] = false;
else
$this->flags["strikethru"] = true;
break;
case "plain": // reset all font modifiers and fontsize to 12
$this->flags["bold"] = false;
$this->flags["italic"] = false;
$this->flags["underlined"] = false;
$this->flags["strikethru"] = false;
$this->flags["fontsize"] = 12;
$this->flags["subscription"] = false;
$this->flags["superscription"] = false;
break;
case "subnone": // subscription
$parameter = "0";
case "sub":
if((string)$parameter == "0")
$this->flags["subscription"] = false;
else
$this->flags["subscription"] = true;
break;
case "supernone": // superscription
$parameter = "0";
case "super":
if((string)$parameter == "0")
$this->flags["superscription"] = false;
else
$this->flags["superscription"] = true;
break;
}
}
/*
Dispatch the control word to the output stream
*/
function flushControl() {
if(preg_match("/^([A-Za-z]+)(-?[0-9]*) ?$/", $this->cword, $match)) {
$this->parseControl($match[1], $match[2]);
if($this->wantXML) {
$this->out.="<control word=\"".$match[1]."\"";
if(strlen($match[2]) > 0)
$this->out.=" param=\"".$match[2]."\"";
$this->out.="/>";
}
}
}
/*
If output stream supports comments, dispatch it
*/
function flushComment($comment) {
if($this->wantXML || $this->wantHTML) {
$this->out.="<!-- ".$comment." -->";
}
}
/*
Dispatch start/end of logical rtf groups (not every output type needs it; merely debugging purpose)
*/
function flushGroup($state) {
if($state == "open") { /* push onto the stack */
array_push($this->stack, $this->flags);
if($this->wantXML)
$this->out.="<group>";
}
if($state == "close") { /* pop from the stack */
$this->last_flags = $this->flags;
$this->flags = array_pop($this->stack);
$this->flags["fonttbl_current_write"] = ""; // on group close, no more fontdefinition will be written to this id
// this is not really the right way to do it !
// of course a '}' not necessarily donates a fonttable end; a fonttable
// group at least *can* contain sub-groups
// therefore an stacked approach is heavily needed
$this->flags["fonttbl"] = false; // no matter what you do, if a group closes, its fonttbl definition is closed too
if($this->wantXML)
$this->out.="</group>";
}
}
function flushHead() {
if($this->wantXML)
$this->out.="<rtf>";
}
function flushBottom() {
if($this->wantXML)
$this->out.="</rtf>";
}
function checkHtmlSpanContent($command) {
reset($this->fontmodifier_table);
while(list($rtf, $html) = each($this->fontmodifier_table)) {
if($this->flags[$rtf] == true) {
if($command == "start")
$this->out .= "<".$html.">";
else
$this->out .= "</".$html.">";
}
}
}
/*
flush text in queue
*/
function flushQueue() {
if(strlen($this->queue)) {
// processing logic
if (isset($this->flags["fonttbl_want_fcharset"]) &&
preg_match("/^[0-9]+$/", $this->flags["fonttbl_want_fcharset"])) {
$this->fonttable[$this->flags["fonttbl_want_fcharset"]]["charset"] = $this->queue;
$this->flags["fonttbl_want_fcharset"] = "";
$this->queue = "";
}
// output logic
if (strlen($this->queue)) {
/*
Everything which passes this is (or, at leat, *should*) be only outputted plaintext
Thats why we can safely add the css-stylesheet when using wantHTML
*/
if($this->wantXML)
$this->out.= "<plain>".$this->queue."</plain>";
else if($this->wantHTML) {
// only output html if a valid (for now, just numeric;) fonttable is given
if (!isset($this->flags["fonttbl_current_read"])) $this->flags["fonttbl_current_read"] = "";
if(preg_match("/^[0-9]+$/", $this->flags["fonttbl_current_read"])) {
if($this->flags["beginparagraph"] == true) {
$this->flags["beginparagraph"] = false;
$this->out .= "<div align=\"";
switch($this->flags["alignment"]) {
case "right":
$this->out .= "right";
break;
case "center":
$this->out .= "center";
break;
case "left":
default:
$this->out .= "left";
}
$this->out .= "\">";
}
/* define new style for that span */
$this->styles["f".$this->flags["fonttbl_current_read"]."s".$this->flags["fontsize"]] = "font-family:".$this->fonttable[$this->flags["fonttbl_current_read"]]["charset"]." font-size:".$this->flags["fontsize"].";";
/* write span start */
$this->out .= "<span class=\"f".$this->flags["fonttbl_current_read"]."s".$this->flags["fontsize"]."\">";
/* check if the span content has a modifier */
$this->checkHtmlSpanContent("start");
/* write span content */
$this->out .= $this->queue;
/* close modifiers */
$this->checkHtmlSpanContent("stop");
/* close span */
"</span>";
}
}
$this->queue = "";
}
}
}
/*
handle special charactes like \'ef
*/
function flushSpecial($special) {
if(strlen($special) == 2) {
if($this->wantASCII)
$this->out .= chr(hexdec('0x'.$special));
else if($this->wantXML)
$this->out .= "<special value=\"".$special."\"/>";
else if($this->wantHTML){
$this->out .= "<special value=\"".$special."\"/>";
switch($special) {
case "c1": $this->out .= "&Aacute;"; break;
case "e1": $this->out .= "&aacute;"; break;
case "c0": $this->out .= "&Agrave;"; break;
case "e0": $this->out .= "&agrave;"; break;
case "c9": $this->out .= "&Eacute;"; break;
case "e9": $this->out .= "&eacute;"; break;
case "c8": $this->out .= "&Egrave;"; break;
case "e8": $this->out .= "&egrave;"; break;
case "cd": $this->out .= "&Iacute;"; break;
case "ed": $this->out .= "&iacute;"; break;
case "cc": $this->out .= "&Igrave;"; break;
case "ec": $this->out .= "&igrave;"; break;
case "d3": $this->out .= "&Oacute;"; break;
case "f3": $this->out .= "&oacute;"; break;
case "d2": $this->out .= "&Ograve;"; break;
case "f2": $this->out .= "&ograve;"; break;
case "da": $this->out .= "&Uacute;"; break;
case "fa": $this->out .= "&uacute;"; break;
case "d9": $this->out .= "&Ugrave;"; break;
case "f9": $this->out .= "&ugrave;"; break;
case "80": $this->out .= "&#8364;"; break;
case "d1": $this->out .= "&Ntilde;"; break;
case "f1": $this->out .= "&ntilde;"; break;
case "c7": $this->out .= "&Ccedil;"; break;
case "e7": $this->out .= "&ccedil;"; break;
case "dc": $this->out .= "&Uuml;"; break;
case "fc": $this->out .= "&uuml;"; break;
case "bf": $this->out .= "&#191;"; break;
case "a1": $this->out .= "&#161;"; break;
case "b7": $this->out .= "&middot;"; break;
case "a9": $this->out .= "&copy;"; break;
case "ae": $this->out .= "&reg;"; break;
case "ba": $this->out .= "&ordm;"; break;
case "aa": $this->out .= "&ordf;"; break;
case "b2": $this->out .= "&sup2;"; break;
case "b3": $this->out .= "&sup3;"; break;
}
}
}
}
/*
Output errors at end
*/
function flushErrors() {
if(count($this->err) > 0) {
if($this->wantXML) {
$this->out .= "<errors>";
while(list($num,$value) = each($this->err)) {
$this->out .= "<message>".$value."</message>";
}
$this->out .= "</errors>";
}
}
}
function makeStyles() {
$this->outstyles = "<style type=\"text/css\"><!--\n";
reset($this->styles);
while(list($stylename, $styleattrib) = each($this->styles)) {
$this->outstyles .= ".".$stylename." { ".$styleattrib." }\n";
}
$this->outstyles .= "--></style>\n";
}
function parse() {
$this->parserInit();
$i = 0;
$this->cw= false; // flag if control word is currently parsed
$this->cfirst = false; // first control character ?
$this->cword = ""; // last or current control word (depends on $this->cw
$this->queue = ""; // plain text data found during parsing
$this->flushHead();
while($i < $this->rtf_len) {
switch($this->rtf[$i]) {
case "{":
if($this->cw) {
$this->flushControl();
$this->cw = false;
$this->cfirst = false;
} else
$this->flushQueue();
$this->flushGroup("open");
break;
case "}":
if($this->cw) {
$this->flushControl();
$this->cw = false;
$this->cfirst = false;
} else
$this->flushQueue();
$this->flushGroup("close");
break;
case "\\":
if($this->cfirst) { // catches '\\'
$this->queue .= "\\"; // replaced single quotes
$this->cfirst = false;
$this->cw = false;
break;
}
if($this->cw) {
$this->flushControl();
} else
$this->flushQueue();
$this->cw = true;
$this->cfirst = true;
$this->cword = "";
break;
default:
if((ord($this->rtf[$i]) == 10) || (ord($this->rtf[$i]) == 13)) break; // eat line breaks
if($this->cw) { // active control word ?
/*
Watch the RE: there's an optional space at the end which IS part of
the control word (but actually its ignored by flushControl)
*/
if(preg_match("/^[a-zA-Z0-9-]?$/", $this->rtf[$i])) { // continue parsing
$this->cword .= $this->rtf[$i];
$this->cfirst = false;
} else {
/*
Control word could be a 'control symbol', like \~ or \* etc.
*/
$specialmatch = false;
if($this->cfirst) {
if($this->rtf[$i] == '\'') { // expect to get some special chars
$this->flushQueue();
$this->flushSpecial($this->rtf[$i+1].$this->rtf[$i+2]);
$i+=2;
$specialmatch = true;
$this->cw = false;
$this->cfirst = false;
$this->cword = "";
} else
if(preg_match("/^[{}\*]$/", $this->rtf[$i])) {
$this->flushComment("control symbols not yet handled");
$specialmatch = true;
}
$this->cfirst = false;
} else {
if($this->rtf[$i] == ' ') { // space delimtes control words, so just discard it and flush the controlword
$this->cw = false;
$this->flushControl();
break;
}
}
if(!$specialmatch) {
$this->flushControl();
$this->cw = false;
$this->cfirst = false;
/*
The current character is a delimeter, but is NOT
part of the control word so we hop one step back
in the stream and process it again
*/
$i--;
}
}
} else {
// < and > need translation before putting into queue when XML or HTML is wanted
if(($this->wantHTML) || ($this->wantXML)) {
switch($this->rtf[$i]) {
case "<":
$this->queue .= "&lt;";
break;
case ">":
$this->queue .= "&gt;";
break;
default:
$this->queue .= $this->rtf[$i];
break;
}
} else
$this->queue .= $this->rtf[$i];
}
}
$i++;
}
$this->flushQueue();
$this->flushErrors();
$this->flushBottom();
if($this->wantHTML) {
$this->makeStyles();
}
}
}

Some files were not shown because too many files have changed in this diff Show more