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 2015-08-10 13:39:56 +01:00
parent 13782dadb5
commit 34b3fcdf3d
246 changed files with 8091 additions and 5801 deletions

View file

@ -4,21 +4,19 @@ Installing Z-Push
Requirements
------------
Z-Push 2 runs only on PHP 5.1 or later
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 4.0 (etch)
Ubuntu 8.04 (hardy heron)
RHEL/CentOS 5.5
Fedora 5 (bordeaux)
OpenSuse 10.1
Slackware 12.0
Gentoo 2006.1
FreeBSD 6.1
OpenBSD 4.0
Mandriva 2007
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/.
@ -41,6 +39,10 @@ These packages vary in names between the distributions.
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
--------------
@ -274,7 +276,7 @@ synchronisation.
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
*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.

4
sources/NOTES Normal file
View file

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

View file

@ -1,6 +1,8 @@
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:
@ -31,9 +33,9 @@ You can find some configuration guidelines in the Wiki https://github.com/fmbiet
Requisites
==========
- PHP 5.5 (5.3 should also work, 5.4 it's fine, but 5.5 is better)
- 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
- PHP-FPM or MOD_PHP
Configuration
=============

View file

@ -105,7 +105,7 @@ valid for the required domain.
Software requirements
---------------------
Like Z-Push, AutoDiscover is written in PHP, where PHP 5.1 or newer is required.
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.
@ -141,7 +141,7 @@ domain part of the email used in the client):
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"
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.
@ -154,39 +154,8 @@ Please restart Apache afterwards.
Configuration
-------------
There are several parameters in the configuration file, which allow to customize
the behaviour of the AutoDiscover Service.
The configuration, generally is located in the z-push/autodiscover directory and
is called "config.php".
The parameters:
BASE_PATH This property specifies where the AutoDiscover files are
located. Normally there is no need to adjust this parameter.
SERVERURL This is the full URL where the Z-Push server is available.
You should adjust it to the domain/server where Z-Push is
installed.
USE_FULLEMAIL_FOR_LOGIN If this is set to "true", AutoDiscover will attempt to
login on the collaboration server with the full email
address sent by the client. If disabled (default), the
local part of the email address is used.
LOGFILEDIR The directory where logfiles are created.
LOGFILE The default AutoDiscover log file.
LOGERRORFILE The default AutoDiscover error log file.
LOGLEVEL The loglevel, set it to WBXML to see the data received
and sent from/to clients.
LOGAUTHFAIL Set to true, to explicitly log failed login attempts.
BACKEND_PROVIDER The backend to be used. If empty (default) the code
will auto detect which backend to use.
Please note: the desired backend also needs to be configured, in the
"backends/<backend>/config.php" file.
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

View file

@ -41,19 +41,8 @@
* Consult LICENSE file for details
************************************************/
include_once('../lib/core/zpushdefs.php');
include_once('../lib/exceptions/exceptions.php');
include_once('../lib/utils/utils.php');
include_once('../lib/core/zpush.php');
include_once('../lib/core/zlog.php');
include_once('../lib/interface/ibackend.php');
include_once('../lib/interface/ichanges.php');
include_once('../lib/interface/iexportchanges.php');
include_once('../lib/interface/iimportchanges.php');
include_once('../lib/interface/isearchprovider.php');
include_once('../lib/interface/istatemachine.php');
include_once('../version.php');
include_once('config.php');
require_once '../vendor/autoload.php';
require_once '../config.php';
class ZPushAutodiscover {
const ACCEPTABLERESPONSESCHEMA = 'http://schemas.microsoft.com/exchange/autodiscover/mobilesync/responseschema/2006';
@ -120,9 +109,8 @@ class ZPushAutodiscover {
else {
ZLog::Write(LOGLEVEL_ERROR, sprintf("Unable to complete autodiscover incorrect request: '%s'", $ex->getMessage()));
}
header('HTTP/1.1 401 Unauthorized');
header('WWW-Authenticate: Basic realm="ZPush"');
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()));
@ -220,12 +208,13 @@ class ZPushAutodiscover {
* @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 = SERVERURL;
$response->Response->Action->Settings->Server->Name = SERVERURL;
$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;
@ -259,10 +248,9 @@ class ZPushAutodiscover {
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."));
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,89 +0,0 @@
<?php
/***********************************************
* File : config.php
* Project : Z-Push
* Descr : Autodiscover configuration file
*
* Created : 30.07.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
************************************************/
/**********************************************************************************
* Default settings
*/
// Defines the base path on the server
define('BASE_PATH', dirname($_SERVER['SCRIPT_FILENAME']). '/');
// The Z-Push server location for the autodiscover response
define('SERVERURL', 'https://localhost/Microsoft-Server-ActiveSync');
/*
* Whether to use the complete email address as a login name
* (e.g. user@company.com) or the username only (user).
* Possible values:
* false - use the username only (default).
* true - use the complete email address.
*/
define('USE_FULLEMAIL_FOR_LOGIN', false);
/**********************************************************************************
* 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 . 'autodiscover.log');
define('LOGERRORFILE', LOGFILEDIR . 'autodiscover-error.log');
define('LOGLEVEL', LOGLEVEL_INFO);
define('LOGUSERLEVEL', LOGLEVEL);
/**********************************************************************************
* Backend settings
*/
// the backend data provider
define('BACKEND_PROVIDER', '');
?>

View file

@ -46,25 +46,20 @@
// config file
require_once("backend/caldav/config.php");
include_once('lib/default/diffbackend/diffbackend.php');
include_once('include/z_caldav.php');
include_once('include/z_RTF.php');
include_once('include/iCalendar.php');
class BackendCalDAV extends BackendDiff {
/**
* @var CalDAVClient
*/
private $_caldav;
private $_caldav_path;
private $_collection = array();
private $_username;
private $changessinkinit;
private $sinkdata;
private $sinkmax;
/**
* Constructor
*
*/
public function BackendCalDAV() {
if (!function_exists("curl_init")) {
@ -81,18 +76,17 @@ class BackendCalDAV extends BackendDiff {
* @see IBackend::Logon()
*/
public function Logon($username, $domain, $password) {
$this->_username = $username;
$this->_caldav_path = str_replace('%u', $username, CALDAV_PATH);
$this->_caldav = new CalDAVClient(CALDAV_SERVER . ":" . CALDAV_PORT . $this->_caldav_path, $username, $password);
$options = $this->_caldav->DoOptionsRequest();
if (isset($options["PROPFIND"])) {
ZLog::Write(LOGLEVEL_DEBUG, sprintf("BackendCalDAV->Logon(): User '%s' is authenticated on CalDAV", $username));
return true;
$url = sprintf("%s://%s:%d%s", CALDAV_PROTOCOL, CALDAV_SERVER, CALDAV_PORT, $this->_caldav_path);
$this->_caldav = new CalDAVClient($url, $username, $password);
if ($connected = $this->_caldav->CheckConnection()) {
ZLog::Write(LOGLEVEL_DEBUG, sprintf("BackendCalDAV->Logon(): User '%s' is authenticated on CalDAV '%s'", $username, $url));
}
else {
ZLog::Write(LOGLEVEL_WARN, sprintf("BackendCalDAV->Logon(): User '%s' is not authenticated on CalDAV", $username));
return false;
ZLog::Write(LOGLEVEL_WARN, sprintf("BackendCalDAV->Logon(): User '%s' is not authenticated on CalDAV '%s'", $username, $url));
}
return $connected;
}
/**
@ -100,14 +94,18 @@ class BackendCalDAV extends BackendDiff {
* @see IBackend::Logoff()
*/
public function Logoff() {
ZLog::Write(LOGLEVEL_DEBUG, sprintf("BackendCalDAV->Logoff()"));
$this->_caldav = null;
if ($this->_caldav != null) {
$this->_caldav->Disconnect();
unset($this->_caldav);
}
$this->SaveStorages();
unset($this->sinkdata);
unset($this->sinkmax);
ZLog::Write(LOGLEVEL_DEBUG, "BackendCalDAV->Logoff(): disconnected from CALDAV server");
return true;
}
@ -306,15 +304,14 @@ class BackendCalDAV extends BackendDiff {
}
else {
$etag = "*";
$date = gmdate("Ymd\THis\Z");
$random = hash("md5", microtime());
$id = $date . "-" . $random . ".ics";
$id = sprintf("%s-%s.ics", gmdate("Ymd\THis\Z"), hash("md5", microtime()));
}
$data = $this->_ParseASToVCalendar($message, $folderid, substr($id, 0, strlen($id)-4));
$url = $this->_caldav_path . substr($folderid, 1) . "/" . $id;
$etag_new = $this->_caldav->DoPUTRequest($url, $data, $etag);
$data = $this->_ParseASToVCalendar($message, $folderid, substr($id, 0, strlen($id) - 4));
$etag_new = $this->CreateUpdateCalendar($data, $url, $etag);
$item = array();
$item['href'] = $id;
@ -333,21 +330,6 @@ class BackendCalDAV extends BackendDiff {
return false;
}
/**
* Changes the 'star' flag of a message on disk
*
* @param string $folderid id of the folder
* @param string $id id of the message
* @param int $flags star flag of the message
*
* @access public
* @return boolean status of the operation
* @throws StatusException could throw specific SYNC_STATUS_* exceptions
*/
public function SetStarFlag($folderid, $id, $flags, $contentParameters) {
return false;
}
/**
* Delete a message from the CalDAV server.
* @see BackendDiff::DeleteMessage()
@ -356,10 +338,7 @@ class BackendCalDAV extends BackendDiff {
ZLog::Write(LOGLEVEL_DEBUG, sprintf("BackendCalDAV->DeleteMessage('%s','%s')", $folderid, $id));
$url = $this->_caldav_path . substr($folderid, 1) . "/" . $id;
$http_status_code = $this->_caldav->DoDELETERequest($url);
if ($http_status_code == "204") {
return true;
}
return false;
return $http_status_code == "204";
}
/**
@ -370,6 +349,51 @@ class BackendCalDAV extends BackendDiff {
return false;
}
/**
* Create or Update one event
*
* @access public
* @param $data string VCALENDAR text
* @param $url string URL for the calendar, if false a new calendar object is created
* @param $etag string ETAG for the calendar, if '*' is a new object
* @return array
*/
public function CreateUpdateCalendar($data, $url = false, $etag = "*") {
if ($url === false) {
$url = sprintf("%s%s/%s-%s.ics", $this->_caldav_path, CALDAV_PERSONAL, gmdate("Ymd\THis\Z"), hash("md5", microtime()));
$etag = "*";
}
return $this->_caldav->DoPUTRequest($url, $data, $etag);
}
/**
* Deletes one VCALENDAR
*
* @access public
* @param $id string ID of the VCALENDAR
* @return boolean
*/
public function DeleteCalendar($id) {
$http_status_code = $this->_caldav->DoDELETERequest(sprintf("%s%s/%s", $this->_caldav_path, CALDAV_PERSONAL, $id));
return $http_status_code == "204";
}
/**
* Finds one VCALENDAR
*
* @access public
* @param $uid string UID attribute
* @return array
*/
public function FindCalendar($uid) {
$filter = sprintf("<C:filter><C:comp-filter name=\"VCALENDAR\"><C:comp-filter name=\"VEVENT\"><C:prop-filter name=\"UID\"><C:text-match>%s</C:text-match></C:prop-filter></C:comp-filter></C:comp-filter></C:filter>", $uid);
$events = $this->_caldav->DoCalendarQuery($filter, sprintf("%s%s", $this->_caldav_path, CALDAV_PERSONAL));
return $events;
}
/**
* Indicates which AS version is supported by the backend.
*
@ -444,8 +468,7 @@ class BackendCalDAV extends BackendDiff {
return $notifications;
}
while($stopat > time() && empty($notifications)) {
// only check once to reduce pressure in the DAV server
foreach ($this->sinkdata as $k => $v) {
$changed = false;
@ -493,8 +516,11 @@ class BackendCalDAV extends BackendDiff {
}
}
if (empty($notifications))
sleep(5);
// Wait to timeout
if (empty($notifications)) {
while ($stopat > time()) {
sleep(1);
}
}
return $notifications;
@ -508,7 +534,8 @@ class BackendCalDAV extends BackendDiff {
* @return SyncAppointment
*/
private function _ParseVEventToAS($data, $contentparameters) {
ZLog::Write(LOGLEVEL_DEBUG, sprintf("BackendCalDAV->_ParseVEventToAS(): Parsing VEvent"));
ZLog::Write(LOGLEVEL_DEBUG, "BackendCalDAV->_ParseVEventToAS(): Parsing VEvent");
$truncsize = Utils::GetTruncSize($contentparameters->GetTruncation());
$message = new SyncAppointment();
@ -700,7 +727,6 @@ class BackendCalDAV extends BackendDiff {
$body = Utils::Utf8_truncate($body, $truncsize);
$message->bodytruncated = 1;
} else {
$body = $body;
$message->bodytruncated = 0;
}
$body = str_replace("\n","\r\n", str_replace("\r","",$body));
@ -739,6 +765,12 @@ class BackendCalDAV extends BackendDiff {
}
}
// Workaround #127 - No organizeremail defined
if (!isset($message->organizeremail)) {
ZLog::Write(LOGLEVEL_DEBUG, sprintf("BackendCalDAV->_ParseVEventToSyncObject(): No organizeremail defined, using username"));
$message->organizeremail = $this->originalUsername;
}
$valarm = current($event->GetComponents("VALARM"));
if ($valarm) {
$properties = $valarm->GetProperties();
@ -880,7 +912,7 @@ class BackendCalDAV extends BackendDiff {
$ical = new iCalComponent();
$ical->SetType("VCALENDAR");
$ical->AddProperty("VERSION", "2.0");
$ical->AddProperty("PRODID", "-//php-push//NONSGML PHP-Push Calendar//EN");
$ical->AddProperty("PRODID", "-//z-push-contrib//NONSGML Z-Push-contrib Calendar//EN");
$ical->AddProperty("CALSCALE", "GREGORIAN");
if ($folderid[0] == "C") {
@ -950,9 +982,11 @@ class BackendCalDAV extends BackendDiff {
if (isset($data->endtime)) {
if ($data->alldayevent == 1) {
$vevent->AddProperty("DTEND", $this->_GetDateFromUTC("Ymd", $data->endtime, $data->timezone), array("VALUE" => "DATE"));
$vevent->AddProperty("X-MICROSOFT-CDO-ALLDAYEVENT", "TRUE");
}
else {
$vevent->AddProperty("DTEND", gmdate("Ymd\THis\Z", $data->endtime));
$vevent->AddProperty("X-MICROSOFT-CDO-ALLDAYEVENT", "FALSE");
}
}
if (isset($data->recurrence)) {
@ -1002,13 +1036,19 @@ class BackendCalDAV extends BackendDiff {
switch ($data->meetingstatus) {
case "1":
$vevent->AddProperty("STATUS", "TENTATIVE");
$vevent->AddProperty("X-MICROSOFT-CDO-BUSYSTATUS", "TENTATIVE");
$vevent->AddProperty("X-MICROSOFT-DISALLOW-COUNTER", "FALSE");
break;
case "3":
$vevent->AddProperty("STATUS", "CONFIRMED");
$vevent->AddProperty("X-MICROSOFT-CDO-BUSYSTATUS", "CONFIRMED");
$vevent->AddProperty("X-MICROSOFT-DISALLOW-COUNTER", "FALSE");
break;
case "5":
case "7":
$vevent->AddProperty("STATUS", "CANCELLED");
$vevent->AddProperty("X-MICROSOFT-CDO-BUSYSTATUS", "CANCELLED");
$vevent->AddProperty("X-MICROSOFT-DISALLOW-COUNTER", "TRUE");
break;
}
}
@ -1017,11 +1057,15 @@ class BackendCalDAV extends BackendDiff {
//Some phones doesn't send the organizeremail, so we gotto get it somewhere else.
//Lets use the login here ($username)
if (!isset($data->organizeremail)) {
$vevent->AddProperty("ORGANIZER", sprintf("MAILTO:%s", $this->_username));
$vevent->AddProperty("ORGANIZER", sprintf("MAILTO:%s", $this->originalUsername));
}
foreach ($data->attendees as $att) {
$att_str = sprintf("MAILTO:%s", $att->email);
$vevent->AddProperty("ATTENDEE", $att_str, array("CN" => $att->name));
if (isset($att->name)) {
$vevent->AddProperty("ATTENDEE", sprintf("MAILTO:%s", $att->email), array("CN" => $att->name));
}
else {
$vevent->AddProperty("ATTENDEE", sprintf("MAILTO:%s", $att->email));
}
}
}
if (isset($data->body)) {
@ -1034,6 +1078,13 @@ class BackendCalDAV extends BackendDiff {
$vevent->AddProperty("CATEGORIES", implode(",", $data->categories));
}
// X-MICROSOFT-CDO-APPT-SEQUENCE:0
// X-MICROSOFT-CDO-OWNERAPPTID:2113393086
// X-MICROSOFT-CDO-INTENDEDSTATUS:BUSY
// X-MICROSOFT-CDO-IMPORTANCE:1
// X-MICROSOFT-CDO-INSTTYPE:0
return $vevent;
}
@ -1462,5 +1513,3 @@ class BackendCalDAV extends BackendDiff {
return base64_encode(pack('la64vvvvvvvvla64vvvvvvvvl', 0, '', 0, 0, 0, 0, 0, 0, 0, 0, 0, '', 0, 0, 0, 0, 0, 0, 0, 0, 0));
}
}
?>

View file

@ -45,23 +45,23 @@
// BackendCalDAV settings
// ************************
// Server address
define('CALDAV_SERVER', 'http://calendar.domain.com');
// Server protocol: http or https
define('CALDAV_PROTOCOL', 'https');
// Port
define('CALDAV_PORT', '80');
// 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', 'home');
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);
?>

View file

@ -44,14 +44,14 @@
// config file
require_once("backend/carddav/config.php");
include_once('lib/default/diffbackend/diffbackend.php');
include_once('include/z_carddav.php');
class BackendCardDAV extends BackendDiff implements ISearchProvider {
private $domain = '';
private $username = '';
private $url = null;
/**
* @var carddav_backend
*/
private $server = null;
private $default_url = null;
private $gal_url = null;
@ -131,8 +131,10 @@ class BackendCardDAV extends BackendDiff implements ISearchProvider {
* @return boolean
*/
public function Logoff() {
ZLog::Write(LOGLEVEL_DEBUG, sprintf("BackendCardDAV->Logoff()"));
$this->server = null;
if ($this->server != null) {
$this->server->disconnect();
unset($this->server);
}
$this->SaveStorages();
@ -140,6 +142,8 @@ class BackendCardDAV extends BackendDiff implements ISearchProvider {
unset($this->sinkdata);
unset($this->addressbooks);
ZLog::Write(LOGLEVEL_DEBUG, "BackendCardDAV->Logoff(): disconnected from CARDDAV server");
return true;
}
@ -207,8 +211,6 @@ class BackendCardDAV extends BackendDiff implements ISearchProvider {
public function ChangesSinkInitialize($folderid) {
ZLog::Write(LOGLEVEL_DEBUG, sprintf("BackendCardDAV->ChangesSinkInitialize(): folderid '%s'", $folderid));
// We don't need the actual cards, we only need to get the changes since this moment
$init_ok = true;
foreach ($this->addressbooks as $addressbook) {
@ -261,7 +263,7 @@ class BackendCardDAV extends BackendDiff implements ISearchProvider {
return $notifications;
}
while($stopat > time() && empty($notifications)) {
// only check once to reduce pressure in the DAV server
foreach ($this->addressbooks as $addressbook) {
$vcards = false;
try {
@ -311,8 +313,11 @@ class BackendCardDAV extends BackendDiff implements ISearchProvider {
}
}
if (empty($notifications))
sleep(5);
// Wait to timeout
if (empty($notifications)) {
while ($stopat > time()) {
sleep(1);
}
}
return $notifications;
@ -545,7 +550,6 @@ class BackendCardDAV extends BackendDiff implements ISearchProvider {
$message["mod"] = $this->contactsetag[$id];
$message["id"] = $id;
$message["flags"] = 1;
$message["star"] = 0;
return $message;
}
@ -641,23 +645,6 @@ class BackendCardDAV extends BackendDiff implements ISearchProvider {
return false;
}
/**
* Changes the 'star' flag of a message on disk
* Not implemented here
*
* @param string $folderid id of the folder
* @param string $id id of the message
* @param int $flags star 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 SetStarFlag($folderid, $id, $flags, $contentParameters) {
return false;
}
/**
* Called when the user has requested to delete (really delete) a message
*
@ -959,7 +946,8 @@ class BackendCardDAV extends BackendDiff implements ISearchProvider {
}
/**
* Converts the vCard into SyncContact
* Converts the vCard into SyncContact.
* See RFC 6350 for vCard format details.
*
* @param string $data string with the vcard
* @param int $truncsize truncate size requested
@ -1001,41 +989,45 @@ class BackendCardDAV extends BackendDiff implements ISearchProvider {
continue;
$field = trim(substr($line, 0, $pos));
$value = trim(substr($line, $pos+1));
$value = trim(substr($line, $pos + 1));
$fieldparts = preg_split('/(?<!\\\\)(\;)/i', $field, -1, PREG_SPLIT_NO_EMPTY);
// The base type
$type = strtolower(array_shift($fieldparts));
$fieldvalue = array();
// We do not care about visually grouping properties together, so strip groups off (see RFC 6350 § 3.3)
if (preg_match('#^[a-z0-9\\-]+\\.(.+)$#i', $type, $matches)) {
$type = $matches[1];
}
// Parse all field values
$fieldvalue = array();
foreach ($fieldparts as $fieldpart) {
if (preg_match('/([^=]+)=(.+)/', $fieldpart, $matches)) {
if (!in_array(strtolower($matches[1]), array('value', 'type', 'encoding', 'language')))
$fieldName = strtolower($matches[1]);
if (!in_array($fieldName, array('value', 'type', 'encoding', 'language')))
continue;
if (isset($fieldvalue[strtolower($matches[1])]) && is_array($fieldvalue[strtolower($matches[1])])) {
if (strtolower($matches[1]) == 'type') {
$fieldvalue[strtolower($matches[1])] = array_merge($fieldvalue[strtolower($matches[1])], array_map('strtolower', preg_split('/(?<!\\\\)(\,)/i', $matches[2], -1, PREG_SPLIT_NO_EMPTY)));
if (isset($fieldvalue[$fieldName]) && is_array($fieldvalue[$fieldName])) {
if ($fieldName == 'type') {
$fieldvalue[$fieldName] = array_merge($fieldvalue[$fieldName], array_map('strtolower', preg_split('/(?<!\\\\)(\,)/i', $matches[2], -1, PREG_SPLIT_NO_EMPTY)));
} else {
$fieldvalue[$fieldName] = array_merge($fieldvalue[$fieldName], preg_split('/(?<!\\\\)(\,)/i', $matches[2], -1, PREG_SPLIT_NO_EMPTY));
}
else {
$fieldvalue[strtolower($matches[1])] = array_merge($fieldvalue[strtolower($matches[1])], preg_split('/(?<!\\\\)(\,)/i', $matches[2], -1, PREG_SPLIT_NO_EMPTY));
} else {
if ($fieldName == 'type') {
$fieldvalue[$fieldName] = array_map('strtolower', preg_split('/(?<!\\\\)(\,)/i', $matches[2], -1, PREG_SPLIT_NO_EMPTY));
} else {
$fieldvalue[$fieldName] = preg_split('/(?<!\\\\)(\,)/i', $matches[2], -1, PREG_SPLIT_NO_EMPTY);
}
}
else {
if (strtolower($matches[1]) == 'type') {
$fieldvalue[strtolower($matches[1])] = array_map('strtolower', 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 {
} else {
if (!isset($types[strtolower($fieldpart)]))
continue;
$fieldvalue[$types[strtolower($fieldpart)]][] = $fieldpart;
}
}
//
switch ($type) {
case 'categories':
@ -1061,8 +1053,7 @@ class BackendCardDAV extends BackendDiff implements ISearchProvider {
}
break;
}
}
else {
} else {
foreach ($val as $i => $v) {
$val[$i] = $this->unescape($v);
}
@ -1125,6 +1116,7 @@ class BackendCardDAV extends BackendDiff implements ISearchProvider {
}
}
}
//;;street;city;state;postalcode;country
if (isset($vcard['adr'])) {
foreach ($vcard['adr'] as $adr) {
@ -1212,8 +1204,13 @@ class BackendCardDAV extends BackendDiff implements ISearchProvider {
$message->bodysize = strlen($message->body);
}
}
// Support both ROLE and TITLE (RFC 6350 § 6.6.1 / § 6.6.2) as mapped to JobTitle
if (!empty($vcard['role'][0]['val'][0]))
$message->jobtitle = $vcard['role'][0]['val'][0];//$vcard['title'][0]['val'][0]
$message->jobtitle = $vcard['role'][0]['val'][0];
if (!empty($vcard['title'][0]['val'][0]))
$message->jobtitle = $vcard['title'][0]['val'][0];
if (!empty($vcard['url'][0]['val'][0]))
$message->webpage = $vcard['url'][0]['val'][0];
if (!empty($vcard['categories'][0]['val']))
@ -1448,5 +1445,4 @@ class BackendCardDAV extends BackendDiff implements ISearchProvider {
return $addressbookId;
}
};
?>
}

View file

@ -49,9 +49,6 @@
* Consult LICENSE file for details
************************************************/
// default backend
include_once('lib/default/backend.php');
//include the CombinedBackend's own config file
require_once("backend/combined/config.php");
require_once("backend/combined/importer.php");
@ -59,11 +56,19 @@ 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
*
@ -100,6 +105,8 @@ class BackendCombined extends Backend implements ISearchProvider {
$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]);
@ -112,12 +119,23 @@ class BackendCombined extends Backend implements ISearchProvider {
if(isset($this->config['backends'][$i]['users'][$username]['domain']))
$d = $this->config['backends'][$i]['users'][$username]['domain'];
}
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;
// 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;
}
@ -167,6 +185,10 @@ class BackendCombined extends Backend implements ISearchProvider {
* @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();
@ -459,19 +481,14 @@ class BackendCombined extends Backend implements ISearchProvider {
$notifications = array();
if ($this->numberChangesSink == 0) {
ZLog::Write(LOGLEVEL_DEBUG, sprintf("BackendCombined doesn't include any Sinkable backends"));
ZLog::Write(LOGLEVEL_DEBUG, "BackendCombined doesn't include any Sinkable backends");
} else {
$stopat = time() + $timeout - 1;
//we will spend 2 seconds at least in each backend that support changessink
// why 2 seconds? because it's the minimum to ensure we run at least once the changessink
// I think it's fairer than run for 10 continuos seconds the same backend (run backend1, run backend2, run backend1, run backend2... vs run backend1, run backend1, run backend2, run backend2)
do {
$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), 2));
ZLog::Write(LOGLEVEL_DEBUG, sprintf("BackendCombined->ChangesSink - Calling in '%s' with %d", get_class($b), $time_each));
// 2 seconds hardcoded timeout!!!
$notifications_backend = $this->backends[$i]->ChangesSink(2);
$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];
@ -479,7 +496,6 @@ class BackendCombined extends Backend implements ISearchProvider {
$notifications = array_merge($notifications, $notifications_backend);
}
}
} while($stopat > time() && empty($notifications));
}
return $notifications;
@ -688,4 +704,3 @@ class BackendCombined extends Backend implements ISearchProvider {
return false;
}
}
?>

View file

@ -109,7 +109,8 @@ class BackendCombinedConfig {
),
//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

@ -47,8 +47,14 @@
*/
class ExportChangesCombined implements IExportChanges {
/**
* @var BackendCombined
*/
private $backend;
private $syncstates;
/**
* @var IExportChanges[]
*/
private $exporters;
private $importer;
private $importwraps;
@ -181,4 +187,3 @@ class ExportChangesCombined implements IExportChanges {
ZLog::Write(LOGLEVEL_DEBUG, "ExportChangesCombined->InitializeExporter(...) success");
}
}
?>

View file

@ -131,23 +131,6 @@ class ImportChangesCombined implements IImportChanges {
return $this->icc->ImportMessageReadFlag($id, $flags);
}
/**
* Imports a change in 'star' flag
* This can never conflict
*
* @param string $id
* @param int $flags
*
* @access public
* @return boolean
*/
public function ImportMessageStarFlag($id, $flags) {
if (!$this->icc) {
ZLog::Write(LOGLEVEL_ERROR, "ImportChangesCombined->ImportMessageReadFlag() icc not configured");
return false;
}
return $this->icc->ImportMessageStarFlag($id, $flags);
}
/**
* Imports a move of a message. This occurs when a user moves an item to another folder
@ -168,7 +151,15 @@ class ImportChangesCombined implements IImportChanges {
ZLog::Write(LOGLEVEL_WARN, "ImportChangesCombined->ImportMessageMove() cannot move message between two backends");
return false;
}
return $this->icc->ImportMessageMove($id, $this->backend->GetBackendFolder($newfolder));
$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;
}
@ -187,7 +178,7 @@ class ImportChangesCombined implements IImportChanges {
public function ImportFolderChange($folder) {
$id = $folder->serverid;
$parent = $folder->parentid;
ZLog::Write(LOGLEVEL_DEBUG, "ImportChangesCombined->ImportFolderChange() ".print_r($folder, 1));
ZLog::Write(LOGLEVEL_DEBUG, sprintf("ImportChangesCombined->ImportFolderChange() id: '%s', parent: '%s'", $id, $parent));
if($parent == '0') {
if($id) {
$backendid = $this->backend->GetBackendId($id);
@ -367,5 +358,3 @@ class ImportHierarchyChangesCombinedWrap {
return $this->ihc->ImportFolderDeletion($this->backendid.$this->backend->config['delimiter'].$id);
}
}
?>

View file

@ -0,0 +1,4 @@
*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

@ -54,6 +54,84 @@ 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
@ -99,29 +177,6 @@ define('IMAP_FROM_LDAP_FROM', '#givenname #sn <#mail>');
define('IMAP_FROM_LDAP_FULLNAME', '#givenname #sn');
// Root folder or prefix in your IMAP server (without the separator). For example, with courier it will be INBOX, and your folder will be INBOX.Sent
// You can use the real case
define('IMAP_FOLDER_ROOT', 'INBOX');
// copy outgoing mail to this folder. If not set z-push will try the default folders
// You can use the real case and the full path (INBOX.Sent)
define('IMAP_FOLDER_SENT', '');
// Draft folder
// You can use the real case and the full path (INBOX.Draft)
define('IMAP_FOLDER_DRAFT', '');
// Trash folder
// You can use the real case and the full path (INBOX.Trash)
define('IMAP_FOLDER_TRASH', '');
// 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', '');
// Method used for sending mail
// mail => mail() php function
@ -147,14 +202,26 @@ $imap_smtp_params = array();
// "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

@ -0,0 +1,344 @@
<?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();
}
if (isset($part->ctype_parameters["method"])) {
switch (strtolower($part->ctype_parameters["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;
}
}
else {
ZLog::Write(LOGLEVEL_WARN, sprintf("BackendIMAP->parse_meeting_calendar() - No method header, please report it to the developers"));
$output->messageclass = "IPM.Appointment";
}
$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

@ -70,6 +70,7 @@ function add_sub_part(&$email, $part) {
}
//FIXME: dfilename => filename
if (isset($part->d_parameters)) {
$params['headers_charset'] = 'utf-8';
foreach ($part->d_parameters as $k => $v) {
$params[$k] = $v;
}
@ -252,31 +253,9 @@ function build_mime_message($message) {
return $built_message;
}
/**
* Detect if the message-part is VCALENDAR
* Content-Type: text/calendar;
*
* @param Mail_mimeDecode $message
* @return boolean
* @access public
*/
function is_calendar($message) {
$res = false;
if (isset($message->ctype_primary) && isset($message->ctype_secondary)) {
if ($message->ctype_primary == "text" && $message->ctype_secondary == "calendar") {
$res = true;
}
}
return $res;
}
/**
* Detect if the message-part is SMIME
* Content-Type: multipart/signed;
* Content-Type: application/pkcs7-mime;
*
* @param Mail_mimeDecode $message
* @return boolean
@ -286,10 +265,43 @@ function is_smime($message) {
$res = false;
if (isset($message->ctype_primary) && isset($message->ctype_secondary)) {
if (($message->ctype_primary == "multipart" && $message->ctype_secondary == "signed") || ($message->ctype_primary == "application" && $message->ctype_secondary == "pkcs7-mime")) {
$res = true;
$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

@ -0,0 +1,232 @@
<?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

@ -56,5 +56,3 @@ 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

@ -48,8 +48,6 @@
// config file
require_once("backend/ldap/config.php");
include_once('lib/default/diffbackend/diffbackend.php');
class BackendLDAP extends BackendDiff {
private $ldap_link;
@ -521,10 +519,6 @@ class BackendLDAP extends BackendDiff {
return false;
}
public function SetStarFlag($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);
@ -581,4 +575,3 @@ class BackendLDAP extends BackendDiff {
return ZPush::ASV_14;
}
}
?>

View file

@ -47,5 +47,3 @@
define('MAILDIR_BASE', '/tmp');
define('MAILDIR_SUBDIR', 'Maildir');
?>

View file

@ -56,11 +56,6 @@
// config file
require_once("backend/maildir/config.php");
include_once('lib/default/diffbackend/diffbackend.php');
include_once('include/mimeDecode.php');
require_once('include/z_RFC822.php');
class BackendMaildir extends BackendDiff {
/**----------------------------------------------------------------------------------------------------------
* default backend methods
@ -141,7 +136,6 @@ class BackendMaildir extends BackendDiff {
$message = Mail_mimeDecode::decode(array('decode_headers' => true, 'decode_bodies' => true, 'include_bodies' => true, 'input' => $rfc822, 'crlf' => "\n", 'charset' => 'utf-8'));
include_once('include/stringstreamwrapper.php');
$attachment = new SyncItemOperationsAttachment();
$attachment->data = StringStreamWrapper::Open($message->parts[$part]->body);
if (isset($message->parts[$part]->ctype_primary) && isset($message->parts[$part]->ctype_secondary))
@ -542,22 +536,6 @@ class BackendMaildir extends BackendDiff {
return true;
}
/**
* Changes the 'star' flag of a message on disk
*
* @param string $folderid id of the folder
* @param string $id id of the message
* @param int $flags star 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 SetStarFlag($folderid, $id, $flags, $contentParameters) {
return false;
}
/**
* Called when the user has requested to delete (really delete) a message
*
@ -736,5 +714,3 @@ class BackendMaildir extends BackendDiff {
return MAILDIR_BASE . "/" . $this->store . "/" . MAILDIR_SUBDIR . "/cur";
}
}
?>

View file

@ -72,4 +72,3 @@ $ldap_field_map = array(
SYNC_GAL_MOBILEPHONE => 'mobile',
SYNC_GAL_EMAILADDRESS => 'mail',
);
?>

View file

@ -195,4 +195,3 @@ class BackendSearchLDAP implements ISearchProvider {
return true;
}
}
?>

View file

@ -46,5 +46,3 @@
// **********************
define('VCARDDIR_DIR', '/home/%u/.kde/share/apps/kabc/stdvcf');
?>

View file

@ -44,8 +44,6 @@
// config file
require_once("backend/vcarddir/config.php");
include_once('lib/default/diffbackend/diffbackend.php');
class BackendVCardDir extends BackendDiff {
/**----------------------------------------------------------------------------------------------------------
* default backend methods
@ -597,22 +595,6 @@ class BackendVCardDir extends BackendDiff {
return false;
}
/**
* Changes the 'star' flag of a message on disk
*
* @param string $folderid id of the folder
* @param string $id id of the message
* @param int $flags star 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 SetStarFlag($folderid, $id, $flags, $contentParameters) {
return false;
}
/**
* Called when the user has requested to delete (really delete) a message
*
@ -693,5 +675,4 @@ class BackendVCardDir extends BackendDiff {
$data = str_replace(array('\\\\', '\\;', '\\,', '\\n','\\N'),array('\\', ';', ',', "\n", "\n"),$data);
return $data;
}
};
?>
}

View file

@ -47,5 +47,3 @@
// Defines the server to which we want to connect
define('MAPI_SERVER', 'file:///var/run/zarafa');
?>

View file

@ -12,7 +12,7 @@
*
* Created : 14.02.2011
*
* Copyright 2007 - 2013 Zarafa Deutschland GmbH
* 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,
@ -210,7 +210,7 @@ class ExportChangesICS implements IExportChanges{
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);
$phpwrapper = new PHPWrapper($this->session, $this->store, $importer, $this->folderid);
// with a folderid we are going to get content
if($this->folderid) {
@ -295,4 +295,3 @@ class ExportChangesICS implements IExportChanges{
return false;
}
}
?>

View file

@ -197,5 +197,3 @@ class ICalParser{
return gmmktime($regs[4], $regs[5], $regs[6], $regs[2], $regs[3], $regs[1]);
}
}
?>

View file

@ -453,20 +453,6 @@ class ImportChangesICS implements IImportChanges {
return true;
}
/**
* Imports a change in 'star' flag
* This can never conflict
*
* @param string $id
* @param int $flags
*
* @access public
* @return boolean
* @throws StatusException
*/
public function ImportMessageStarFlag($id, $flags) {
return false;
}
/**
* Imports a move of a message. This occurs when a user moves an item to another folder
@ -489,10 +475,6 @@ class ImportChangesICS implements IImportChanges {
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);
// 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 the entryid of the message we're moving
$entryid = mapi_msgstore_entryidfromsourcekey($this->store, $this->folderid, hex2bin($id));
if(!$entryid)
@ -500,8 +482,18 @@ class ImportChangesICS implements IImportChanges {
//open the source message
$srcmessage = mapi_msgstore_openentry($this->store, $entryid);
if (!$srcmessage)
throw new StatusException(sprintf("ImportChangesICS->ImportMessageMove('%s','%s'): Error, unable to open source message: 0x%X", $id, $newfolder, mapi_last_hresult()), SYNC_MOVEITEMSSTATUS_INVALIDSOURCEID);
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);
@ -703,4 +695,3 @@ class ImportChangesICS implements IImportChanges {
return $ret;
}
}
?>

View file

@ -47,6 +47,8 @@
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",
@ -65,8 +67,8 @@ function main() {
function listfolders_configure() {
if (!isset($_SERVER["TERM"]) || !isset($_SERVER["LOGNAME"])) {
echo "This script should not be called in a browser.\n";
if (php_sapi_name() != "cli") {
printf("This script should not be called in a browser. Called from: %s\n", php_sapi_name());
exit(1);
}
@ -83,32 +85,40 @@ function listfolders_configure() {
}
function listfolders_handle() {
$shortoptions = "l:h:u:p:";
$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);
$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]]\n\t-h path\t\tconnect through <path>, e.g. file:///var/run/socket\n\t-u authuser\tlogin as authenticated administration user\n\t-p authpassword\tpassword of the remoteuser\n\n";
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) {
$session = @mapi_logon_zarafa($user, $pass, $mapi);
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";
@ -180,5 +190,3 @@ function listfolders_getlist ($adminStore, $session, $user) {
}
}
}
?>

View file

@ -223,4 +223,3 @@ class BaseException extends Exception
// @TODO getTrace and getTraceAsString
}
?>

View file

@ -1942,4 +1942,3 @@
return $a["start"] == $b["start"] ? 0 : ($a["start"] > $b["start"] ? 1 : -1 );
}
}
?>

View file

@ -394,4 +394,3 @@ class FreeBusyPublish {
}
}
?>

View file

@ -104,4 +104,3 @@
if (function_exists('mapi_enable_exceptions')) {
//mapi_enable_exceptions("mapiexception");
}
?>

View file

@ -3195,4 +3195,3 @@ If it is the first time this attendee has proposed a new date/time, increment th
$this->meetingTimeInfo = $meetingTimeInfo;
}
}
?>

View file

@ -1574,4 +1574,3 @@
.... ULONGx2 Constant: { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}.
*/
?>

View file

@ -461,4 +461,3 @@
}
}
}
?>

View file

@ -1033,4 +1033,3 @@
$this->doUpdate($prefix, $prefixComplete);
}
}
?>

View file

@ -336,4 +336,3 @@ function getCalendarItems($store, $calendar, $viewstart, $viewend, $propsrequest
// properties that the caller did not request (recurring, etc). This shouldn't be a problem though.
return $result;
}
?>

View file

@ -244,7 +244,3 @@ 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

@ -664,5 +664,3 @@ define('fnevTableModified' ,0x00000100);
define('fnevStatusObjectModified' ,0x00000200);
define('fnevReservedForMapi' ,0x40000000);
define('fnevExtended' ,0x80000000);
?>

View file

@ -70,5 +70,3 @@ define('PS_INTERNET_HEADERS', makeguid("{00020386-0000-0000-c0
// sk added for Z-Push
define ('PSETID_AirSync', makeguid("{71035549-0739-4DCB-9163-00F0580DBBDF}"));
?>

View file

@ -1252,5 +1252,3 @@ define('PR_ZC_CONTACT_FOLDER_NAMES' ,mapi_prop_tag(PT_MV_TSTRI
//Properties defined for Z-Push
define('PR_TODO_ITEM_FLAGS' ,mapi_prop_tag(PT_LONG, 0x0E2B));
?>

View file

@ -378,6 +378,7 @@ class MAPIMapping {
"startdate" => "PT_SYSTIME:PSETID_Task:0x8104",
"subject" => PR_SUBJECT,
"rtf" => PR_RTF_COMPRESSED,
"html" => PR_HTML,
);
}
@ -518,7 +519,8 @@ class MAPIMapping {
"attachnum" => PR_ATTACH_NUM,
"attachdatabin" => PR_ATTACH_DATA_BIN,
"internetcpid" => PR_INTERNET_CPID,
"rtf" => PR_RTF_COMPRESSED,
"rtfinsync" => PR_RTF_IN_SYNC,
);
}
}
?>

View file

@ -12,7 +12,7 @@
*
* Created : 14.02.2011
*
* Copyright 2007 - 2013 Zarafa Deutschland GmbH
* 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,
@ -59,6 +59,7 @@ class PHPWrapper {
private $mapiprovider;
private $store;
private $contentparameters;
private $folderid;
/**
@ -66,15 +67,17 @@ class PHPWrapper {
*
* @param ressource $session
* @param ressource $store
* @param IImportChanges $importer incoming changes from ICS are forwarded here
* @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) {
public function PHPWrapper($session, $store, $importer, $folderid) {
$this->importer = &$importer;
$this->store = $store;
$this->mapiprovider = new MAPIProvider($session, $this->store);
$this->folderid = $folderid;
}
/**
@ -117,6 +120,7 @@ class PHPWrapper {
$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) {
@ -156,6 +160,13 @@ class PHPWrapper {
* @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));
}
@ -189,16 +200,13 @@ class PHPWrapper {
/**
* Imports a single folder change
*
* @param mixed $props sourcekey of the changed folder
* @param array $props properties of the changed folder
*
* @access public
* @return
*/
function ImportFolderChange($props) {
$sourcekey = $props[PR_SOURCE_KEY];
$entryid = mapi_msgstore_entryidfromsourcekey($this->store, $sourcekey);
$mapifolder = mapi_msgstore_openentry($this->store, $entryid);
$folder = $this->mapiprovider->GetFolder($mapifolder);
$folder = $this->mapiprovider->GetFolder($props);
// do not import folder if there is something "wrong" with it
if ($folder === false)
@ -224,5 +232,3 @@ class PHPWrapper {
return 0;
}
}
?>

View file

@ -46,6 +46,8 @@ class MAPIProvider {
private $store;
private $zRFC822;
private $addressbook;
private $storeProps;
private $inboxProps;
/**
* Constructor of the MAPI Provider
@ -277,8 +279,9 @@ class MAPIProvider {
}
}
//set attendee's status and type if they're available
if (isset($row[PR_RECIPIENT_TRACKSTATUS]))
//set attendee's status and type if they're available and if we are the organizer
$storeprops = $this->getStoreProps();
if (isset($row[PR_RECIPIENT_TRACKSTATUS]) && $messageprops[$appointmentprops["representingentryid"]] == $storeprops[PR_MAILBOX_OWNER_ENTRYID])
$attendee->attendeestatus = $row[PR_RECIPIENT_TRACKSTATUS];
if (isset($row[PR_RECIPIENT_TYPE]))
$attendee->attendeetype = $row[PR_RECIPIENT_TYPE];
@ -319,6 +322,11 @@ class MAPIProvider {
$message->busystatus = fbFree;
}
// If the busystatus has the value of -1, we should be interpreted as tentative (1) / ZP-581
if (isset($message->busystatus) && $message->busystatus == -1) {
$message->busystatus = fbTentative;
}
return $message;
}
@ -468,6 +476,12 @@ class MAPIProvider {
if (isset($exception->busystatus) && $exception->busystatus == fbWorkingElsewhere) {
$exception->busystatus = fbFree;
}
// If the busystatus has the value of -1, we should be interpreted as tentative (1) / ZP-581
if (isset($exception->busystatus) && $exception->busystatus == -1) {
$exception->busystatus = fbTentative;
}
array_push($syncMessage->exceptions, $exception);
}
@ -622,9 +636,22 @@ class MAPIProvider {
if(!isset($message->meetingrequest->sensitivity))
$message->meetingrequest->sensitivity = 0;
// If the user is working from a location other than the office the busystatus should be interpreted as free.
if (isset($message->meetingrequest->busystatus) && $message->meetingrequest->busystatus == fbWorkingElsewhere) {
$message->meetingrequest->busystatus = fbFree;
}
// If the busystatus has the value of -1, we should be interpreted as tentative (1) / ZP-581
if (isset($message->meetingrequest->busystatus) && $message->meetingrequest->busystatus == -1) {
$message->meetingrequest->busystatus = fbTentative;
}
// if a meeting request response hasn't been processed yet,
// do it so that the attendee status is updated on the mobile
if(!isset($messageprops[$emailproperties["processed"]])) {
// check if we are not sending the MR so we can process it - ZP-581
$cuser = ZPush::GetBackend()->GetUserDetails(ZPush::GetBackend()->GetCurrentUsername());
if(isset($cuser["emailaddress"]) && $cuser["emailaddress"] != $fromaddr) {
$req = new Meetingrequest($this->store, $mapimessage, $this->session);
if ($req->isMeetingRequestResponse()) {
$req->processMeetingRequestResponse();
@ -633,6 +660,7 @@ class MAPIProvider {
$req->processMeetingCancellation();
}
}
}
$message->contentclass = DEFAULT_CALENDAR_CONTENTCLASS;
}
@ -789,18 +817,17 @@ class MAPIProvider {
}
/**
* Reads a folder object from MAPI
* Creates a SyncFolder from MAPI properties.
*
* @param mixed $mapimessage
* @param mixed $folderprops
*
* @access public
* @return SyncFolder
*/
public function GetFolder($mapifolder) {
public function GetFolder($folderprops) {
$folder = new SyncFolder();
$folderprops = mapi_getprops($mapifolder, array(PR_DISPLAY_NAME, PR_PARENT_ENTRYID, PR_SOURCE_KEY, PR_PARENT_SOURCE_KEY, PR_ENTRYID, PR_CONTAINER_CLASS, PR_ATTR_HIDDEN));
$storeprops = mapi_getprops($this->store, array(PR_IPM_SUBTREE_ENTRYID));
$storeprops = $this->getStoreProps();
if(!isset($folderprops[PR_DISPLAY_NAME]) ||
!isset($folderprops[PR_PARENT_ENTRYID]) ||
@ -818,6 +845,12 @@ class MAPIProvider {
return false;
}
// ignore certain undesired folders, like "RSS Feeds"
if (isset($folderprops[PR_CONTAINER_CLASS]) && $folderprops[PR_CONTAINER_CLASS] == "IPF.Note.OutlookHomepage") {
ZLog::Write(LOGLEVEL_DEBUG, sprintf("MAPIProvider->GetFolder(): folder '%s' should not be synchronized", $folderprops[PR_DISPLAY_NAME]));
return false;
}
$folder->serverid = bin2hex($folderprops[PR_SOURCE_KEY]);
if($folderprops[PR_PARENT_ENTRYID] == $storeprops[PR_IPM_SUBTREE_ENTRYID])
$folder->parentid = "0";
@ -840,9 +873,8 @@ class MAPIProvider {
* @return long
*/
public function GetFolderType($entryid, $class = false) {
$storeprops = mapi_getprops($this->store, array(PR_IPM_OUTBOX_ENTRYID, PR_IPM_WASTEBASKET_ENTRYID, PR_IPM_SENTMAIL_ENTRYID));
$inbox = mapi_msgstore_getreceivefolder($this->store);
$inboxprops = mapi_getprops($inbox, array(PR_ENTRYID, PR_IPM_DRAFTS_ENTRYID, PR_IPM_TASK_ENTRYID, PR_IPM_APPOINTMENT_ENTRYID, PR_IPM_CONTACT_ENTRYID, PR_IPM_NOTE_ENTRYID, PR_IPM_JOURNAL_ENTRYID));
$storeprops = $this->getStoreProps();
$inboxprops = $this->getInboxProps();
if($entryid == $inboxprops[PR_ENTRYID])
return SYNC_FOLDER_TYPE_INBOX;
@ -1255,6 +1287,7 @@ class MAPIProvider {
$representingprops = $this->getProps($mapimessage, $p);
if (!isset($representingprops[$appointmentprops["representingentryid"]])) {
// TODO use getStoreProps
$storeProps = mapi_getprops($this->store, array(PR_MAILBOX_OWNER_ENTRYID));
$props[$appointmentprops["representingentryid"]] = $storeProps[PR_MAILBOX_OWNER_ENTRYID];
$displayname = $this->getFullnameFromEntryID($storeProps[PR_MAILBOX_OWNER_ENTRYID]);
@ -1518,9 +1551,10 @@ class MAPIProvider {
// "start" and "end" are in GMT when passing to class.recurrence
// set recurrence start here because it's calculated differently for tasks and appointments
$recur["start"] = $task->recurrence->start;
$recur["regen"] = $task->regenerate;
$recur["regen"] = (isset($task->recurrence->regenerate) && $task->recurrence->regenerate) ? 1 : 0;
//Also add dates to $recur
$recur["duedate"] = $task->duedate;
$recur["complete"] = (isset($task->complete) && $task->complete) ? 1 : 0;
$recurrence->setRecurrence($recur);
}
@ -2607,6 +2641,33 @@ class MAPIProvider {
}
return $this->addressbook;
}
}
?>
/**
* Gets the required store properties.
*
* @access private
* @return array
*/
private function getStoreProps() {
if (!isset($this->storeProps) || empty($this->storeProps)) {
ZLog::Write(LOGLEVEL_DEBUG, "MAPIProvider->getStoreProps(): Getting store properties.");
$this->storeProps = mapi_getprops($this->store, array(PR_IPM_SUBTREE_ENTRYID, PR_IPM_OUTBOX_ENTRYID, PR_IPM_WASTEBASKET_ENTRYID, PR_IPM_SENTMAIL_ENTRYID, PR_ENTRYID, PR_IPM_PUBLIC_FOLDERS_ENTRYID, PR_IPM_FAVORITES_ENTRYID, PR_MAILBOX_OWNER_ENTRYID));
}
return $this->storeProps;
}
/**
* Gets the required inbox properties.
*
* @access private
* @return array
*/
private function getInboxProps() {
if (!isset($this->inboxProps) || empty($this->inboxProps)) {
ZLog::Write(LOGLEVEL_DEBUG, "MAPIProvider->getInboxProps(): Getting inbox properties.");
$inbox = mapi_msgstore_getreceivefolder($this->store);
$this->inboxProps = mapi_getprops($inbox, array(PR_ENTRYID, PR_IPM_DRAFTS_ENTRYID, PR_IPM_TASK_ENTRYID, PR_IPM_APPOINTMENT_ENTRYID, PR_IPM_CONTACT_ENTRYID, PR_IPM_NOTE_ENTRYID, PR_IPM_JOURNAL_ENTRYID));
}
return $this->inboxProps;
}
}

View file

@ -143,6 +143,4 @@ class MAPIStreamWrapper {
}
}
stream_wrapper_register(MAPIStreamWrapper::PROTOCOL, "MAPIStreamWrapper")
?>
stream_wrapper_register(MAPIStreamWrapper::PROTOCOL, "MAPIStreamWrapper");

View file

@ -482,6 +482,104 @@ class MAPIUtils {
);
}
}
/**
* 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

@ -718,4 +718,3 @@ class TNEFParser {
return NOERROR;
}
}
?>

View file

@ -164,7 +164,15 @@ class BackendZarafa implements IBackend, ISearchProvider {
try {
// check if notifications are available in php-mapi
if(function_exists('mapi_feature') && mapi_feature('LOGONFLAGS')) {
// send Z-Push version and user agent to ZCP - ZP-589
if (Utils::CheckMapiExtVersion('7.2.0')) {
$zpush_version = 'Z-Push_' . @constant('ZPUSH_VERSION');
$user_agent = $_SERVER['HTTP_USER_AGENT'];
$this->session = @mapi_logon_zarafa($user, $pass, MAPI_SERVER, null, null, 0, $zpush_version, $user_agent);
}
else {
$this->session = @mapi_logon_zarafa($user, $pass, MAPI_SERVER, null, null, 0);
}
$this->notifications = true;
}
// old fashioned session
@ -453,18 +461,14 @@ class BackendZarafa implements IBackend, ISearchProvider {
// @see http://jira.zarafa.com/browse/ZP-68
$meetingRequestProps = MAPIMapping::GetMeetingRequestProperties();
$meetingRequestProps = getPropIdsFromStrings($this->store, $meetingRequestProps);
$props = mapi_getprops($mapimessage, array(PR_MESSAGE_CLASS, $meetingRequestProps["goidtag"], $sendMailProps["internetcpid"]));
$props = mapi_getprops($mapimessage, array(PR_MESSAGE_CLASS, $meetingRequestProps["goidtag"], $sendMailProps["internetcpid"], $sendMailProps["body"], $sendMailProps["html"], $sendMailProps["rtf"], $sendMailProps["rtfinsync"]));
// Convert sent message's body to UTF-8.
// @see http://jira.zarafa.com/browse/ZP-505
if (isset($props[$sendMailProps["internetcpid"]]) && $props[$sendMailProps["internetcpid"]] != INTERNET_CPID_UTF8) {
ZLog::Write(LOGLEVEL_DEBUG, sprintf("Sent email cpid is not unicode (%d). Set it to unicode and convert email body.", $props[$sendMailProps["internetcpid"]]));
// Convert sent message's body to UTF-8 if it was a HTML message.
// @see http://jira.zarafa.com/browse/ZP-505 and http://jira.zarafa.com/browse/ZP-555
if (isset($props[$sendMailProps["internetcpid"]]) && $props[$sendMailProps["internetcpid"]] != INTERNET_CPID_UTF8 && MAPIUtils::GetNativeBodyType($props) == SYNC_BODYPREFERENCE_HTML) {
ZLog::Write(LOGLEVEL_DEBUG, sprintf("Sent email cpid is not unicode (%d). Set it to unicode and convert email html body.", $props[$sendMailProps["internetcpid"]]));
$mapiprops[$sendMailProps["internetcpid"]] = INTERNET_CPID_UTF8;
$body = MAPIUtils::readPropStream($mapimessage, PR_BODY);
$body = Utils::ConvertCodepageStringToUtf8($props[$sendMailProps["internetcpid"]], $body);
$mapiprops[$sendMailProps["body"]] = $body;
$bodyHtml = MAPIUtils::readPropStream($mapimessage, PR_HTML);
$bodyHtml = Utils::ConvertCodepageStringToUtf8($props[$sendMailProps["internetcpid"]], $bodyHtml);
$mapiprops[$sendMailProps["html"]] = $bodyHtml;
@ -968,9 +972,9 @@ class BackendZarafa implements IBackend, ISearchProvider {
* @throws StatusException
*/
public function GetGALSearchResults($searchquery, $searchrange){
// only return users from who the displayName or the username starts with $name
// only return users whose displayName or the username starts with $name
//TODO: use PR_ANR for this restriction instead of PR_DISPLAY_NAME and PR_ACCOUNT
$addrbook = mapi_openaddressbook($this->session);
$addrbook = $this->getAddressbook();
if ($addrbook)
$ab_entryid = mapi_ab_getdefaultdir($addrbook);
if ($ab_entryid)
@ -1003,12 +1007,16 @@ class BackendZarafa implements IBackend, ISearchProvider {
$querycnt = mapi_table_getrowcount($table);
//do not return more results as requested in range
$querylimit = (($rangeend + 1) < $querycnt) ? ($rangeend + 1) : $querycnt;
$items['range'] = ($querylimit > 0) ? $rangestart.'-'.($querylimit - 1) : '0-0';
$items['searchtotal'] = $querycnt;
if ($querycnt > 0)
$abentries = mapi_table_queryrows($table, array(PR_ACCOUNT, PR_DISPLAY_NAME, PR_SMTP_ADDRESS, PR_BUSINESS_TELEPHONE_NUMBER, PR_GIVEN_NAME, PR_SURNAME, PR_MOBILE_TELEPHONE_NUMBER, PR_HOME_TELEPHONE_NUMBER, PR_TITLE, PR_COMPANY_NAME, PR_OFFICE_LOCATION), $rangestart, $querylimit);
for ($i = 0; $i < $querylimit; $i++) {
if (!isset($abentries[$i][PR_SMTP_ADDRESS])) {
ZLog::Write(LOGLEVEL_WARN, sprintf("The GAL entry '%s' does not have an email address and will be ignored.", w2u($abentries[$i][PR_DISPLAY_NAME])));
continue;
}
$items[$i][SYNC_GAL_DISPLAYNAME] = w2u($abentries[$i][PR_DISPLAY_NAME]);
if (strlen(trim($items[$i][SYNC_GAL_DISPLAYNAME])) == 0)
@ -1047,6 +1055,9 @@ class BackendZarafa implements IBackend, ISearchProvider {
if (isset($abentries[$i][PR_OFFICE_LOCATION]))
$items[$i][SYNC_GAL_OFFICE] = w2u($abentries[$i][PR_OFFICE_LOCATION]);
}
$nrResults = count($items);
$items['range'] = ($nrResults > 0) ? $rangestart.'-'.($nrResults - 1) : '0-0';
$items['searchtotal'] = $nrResults;
return $items;
}
@ -1195,12 +1206,21 @@ class BackendZarafa implements IBackend, ISearchProvider {
*/
public function GetUserDetails($username) {
ZLog::Write(LOGLEVEL_WBXML, sprintf("ZarafaBackend->GetUserDetails for '%s'.", $username));
$zarafauserinfo = @mapi_zarafa_getuser_by_name($this->defaultstore, $username);
$zarafauserinfo = @mapi_zarafa_getuser_by_name($this->store, $username);
$userDetails['emailaddress'] = (isset($zarafauserinfo['emailaddress']) && $zarafauserinfo['emailaddress']) ? $zarafauserinfo['emailaddress'] : false;
$userDetails['fullname'] = (isset($zarafauserinfo['fullname']) && $zarafauserinfo['fullname']) ? $zarafauserinfo['fullname'] : false;
return $userDetails;
}
/**
* Returns the username of the currently active user
*
* @access public
* @return String
*/
public function GetCurrentUsername() {
return $this->storeName;
}
/**----------------------------------------------------------------------------------------------------------
* Private methods
@ -1880,5 +1900,3 @@ class BackendZarafa implements IBackend, ISearchProvider {
* DEPRECATED legacy class
*/
class BackendICS extends BackendZarafa {}
?>

6
sources/composer.json Normal file
View file

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

View file

@ -48,11 +48,15 @@
define('TIMEZONE', '');
// Defines the base path on the server
define('BASE_PATH', dirname($_SERVER['SCRIPT_FILENAME']). '/');
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);
@ -60,6 +64,10 @@
// 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).
@ -68,7 +76,7 @@
* false - use the username only (default).
* true - use the complete email address.
*/
define('USE_FULLEMAIL_FOR_LOGIN', false);
define('USE_FULLEMAIL_FOR_LOGIN', true);
/**********************************************************************************
* Device pre-authorization. Useful when using Z-Push as a standalone product.
@ -153,9 +161,18 @@
define('LOGUSERLEVEL', LOGLEVEL_DEVICEID);
$specialLogUsers = array();
// 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');
// 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
@ -238,8 +255,8 @@
// 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: 49152 - 48 KB default max photo size in bytes
define('SYNC_CONTACTS_MAXPICTURESIZE', 49152);
// 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
@ -261,6 +278,28 @@
// 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
*
@ -328,5 +367,3 @@
),
*/
);
?>

103
sources/docker/README.md Normal file
View file

@ -0,0 +1,103 @@
# 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

13
sources/docker/basic.yml Normal file
View file

@ -0,0 +1,13 @@
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

@ -0,0 +1,16 @@
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

@ -0,0 +1,21 @@
-----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

@ -0,0 +1,27 @@
-----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

@ -0,0 +1,91 @@
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

@ -0,0 +1,21 @@
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

@ -146,5 +146,3 @@ class Auth_SASL
return false;
}
}
?>

View file

@ -43,8 +43,6 @@
* @package Auth_SASL
*/
require_once('include/Auth/SASL/Common.php');
class Auth_SASL_Anonymous extends Auth_SASL_Common
{
/**
@ -68,4 +66,3 @@ class Auth_SASL_Anonymous extends Auth_SASL_Common
return $token;
}
}
?>

View file

@ -126,4 +126,3 @@ class Auth_SASL_Common
return false;
}
}
?>

View file

@ -43,8 +43,6 @@
* @package Auth_SASL
*/
require_once('include/Auth/SASL/Common.php');
class Auth_SASL_CramMD5 extends Auth_SASL_Common
{
/**
@ -65,4 +63,3 @@ class Auth_SASL_CramMD5 extends Auth_SASL_Common
return $user . ' ' . $this->_HMAC_MD5($pass, $challenge);
}
}
?>

View file

@ -43,8 +43,6 @@
* @package Auth_SASL
*/
require_once('include/Auth/SASL/Common.php');
class Auth_SASL_DigestMD5 extends Auth_SASL_Common
{
/**
@ -194,4 +192,3 @@ class Auth_SASL_DigestMD5 extends Auth_SASL_Common
}
}
}
?>

View file

@ -43,8 +43,6 @@
* @package Auth_SASL
*/
require_once('include/Auth/SASL/Common.php');
class Auth_SASL_External extends Auth_SASL_Common
{
/**
@ -60,4 +58,3 @@ class Auth_SASL_External extends Auth_SASL_Common
return $authzid;
}
}
?>

View file

@ -46,8 +46,6 @@
* @package Auth_SASL
*/
require_once('include/Auth/SASL/Common.php');
class Auth_SASL_Login extends Auth_SASL_Common
{
/**
@ -62,4 +60,3 @@ class Auth_SASL_Login extends Auth_SASL_Common
return sprintf('LOGIN %s %s', $user, $pass);
}
}
?>

View file

@ -43,8 +43,6 @@
* @package Auth_SASL
*/
require_once('include/Auth/SASL/Common.php');
class Auth_SASL_Plain extends Auth_SASL_Common
{
/**
@ -60,4 +58,3 @@ class Auth_SASL_Plain extends Auth_SASL_Common
return $authzid . chr(0) . $authcid . chr(0) . $pass;
}
}
?>

View file

@ -46,8 +46,6 @@
* @package Auth_SASL
*/
require_once('include/Auth/SASL/Common.php');
class Auth_SASL_SCRAM extends Auth_SASL_Common
{
/**
@ -301,5 +299,3 @@ class Auth_SASL_SCRAM extends Auth_SASL_Common
}
}
}
?>

View file

@ -86,7 +86,6 @@ class Mail
static function &factory($driver, $params = array())
{
$driver = strtolower($driver);
@include_once 'include/Mail/' . $driver . '.php';
$class = 'Mail_' . $driver;
if (class_exists($class)) {
$mailer = new $class($params);
@ -197,7 +196,6 @@ class Mail
foreach ($headers as $key => $value) {
if (strcasecmp($key, 'From') === 0) {
include_once 'include/z_RFC822.php';
$parser = new Mail_RFC822();
$addresses = $parser->parseAddressList($value, 'localhost', false);
//if (is_a($addresses, 'PEAR_Error')) {
@ -255,8 +253,6 @@ class Mail
*/
function parseRecipients($recipients)
{
include_once 'include/z_RFC822.php';
// if we're passed an array, assume addresses are valid and
// implode them before parsing.
if (is_array($recipients)) {
@ -282,6 +278,9 @@ class Mail
}
}
// Remove duplicated
$recipients = array_unique($recipients);
return $recipients;
}

View file

@ -174,6 +174,24 @@ class Mail_smtp extends Mail {
*/
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.
*
@ -211,6 +229,9 @@ class Mail_smtp extends Mail {
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'])) {
@ -309,22 +330,6 @@ class Mail_smtp extends Mail {
return $recipients;
}
// FIX: Cc and Bcc headers are sent, but we need to make sure that the recipient list contains them
foreach (array("CC", "cc", "Cc", "BCC", "Bcc", "bcc") as $key) {
if (!empty($headers[$key])) {
$extra_recipients = $this->parseRecipients($headers[$key]);
if ($extra_recipients === false) {
$this->_smtp->rset();
return $extra_recipients;
}
$recipients = array_merge($recipients, $extra_recipients);
}
}
// Remove repeated rcptTo
$recipients = array_unique($recipients);
foreach ($recipients as $recipient) {
$res = $this->_smtp->rcptTo($recipient);
//if (is_a($res, 'PEAR_Error')) {
@ -378,11 +383,15 @@ class Mail_smtp extends Mail {
return $this->_smtp;
}
include_once 'include/Net/SMTP.php';
$this->_smtp = &new Net_SMTP($this->host,
$this->port,
$this->localhost,
$this->pipelining);
$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) {
@ -471,7 +480,8 @@ class Mail_smtp extends Mail {
/* Build our standardized error string. */
return $text
. ' [SMTP: ' . $error->getMessage()
// . ' [SMTP: ' . $error->getMessage()
. ' [SMTP: '
. " (code: $code, response: $response)]";
}

View file

@ -33,7 +33,6 @@
//require_once 'PEAR.php';
//require_once 'PEAR/Exception.php';
require_once 'include/Net/Socket.php';
/**
* Provides an implementation of the SMTP protocol using PEAR's
@ -160,6 +159,27 @@ class Net_SMTP
*/
protected $_esmtp = array();
/**
* Require verification of SSL certificate used.
*
* @var bool
*/
protected $_verify_peer;
/**
* Require verification of peer name
*
* @var bool
*/
protected $_verify_peer_name;
/**
* Allow self-signed certificates. Requires verify_peer
*
* @var bool
*/
protected $_allow_self_signed;
/**
* Instantiates a new Net_SMTP object, overriding any defaults
* with parameters that are passed in.
@ -177,10 +197,14 @@ class Net_SMTP
* @param boolean $pipeling Use SMTP command pipelining
* @param integer $timeout Socket I/O timeout in seconds.
* @param array $socket_options Socket stream_context_create() options.
* @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
*/
public function __construct($host = null, $port = null, $localhost = null,
$pipelining = false, $timeout = 0,
$socket_options = null)
$socket_options = null,
$verify_peer = true, $verify_peer_name = true, $allow_self_signed = false)
{
if (isset($host)) {
$this->host = $host;
@ -195,14 +219,33 @@ class Net_SMTP
$this->_socket = new Net_Socket();
$this->_socket_options = $socket_options;
// SSL connection, we need to modify the socket_options
if (strpos($this->host, "ssl://") === 0) {
if ($this->_socket_options == null)
$this->_socket_options = array();
if (!array_key_exists('ssl', $this->_socket_options))
$this->_socket_options['ssl'] = array();
$this->_socket_options['ssl']['verify_peer'] = $verify_peer;
$this->_socket_options['ssl']['allow_self_signed'] = $allow_self_signed;
// This option was introduced in 5.6
if (version_compare(phpversion(), "5.6.0", ">="))
$this->_socket_options['ssl']['verify_peer_name'] = $verify_peer_name;
}
$this->_timeout = $timeout;
// We also need this for use in the STARTTLS command
$this->_verify_peer = $verify_peer;
$this->_verify_peer_name = $verify_peer_name;
$this->_allow_self_signed = $allow_self_signed;
/* Include the Auth_SASL package. If the package is available, we
* enable the authentication methods that depend upon it. */
if (@include_once 'include/Auth/SASL.php') {
$this->setAuthMethod('CRAM-MD5', array($this, '_authCram_MD5'));
$this->setAuthMethod('DIGEST-MD5', array($this, '_authDigest_MD5'));
}
/* These standard authentication methods are always available. */
$this->setAuthMethod('LOGIN', array($this, '_authLogin'), false);
@ -262,8 +305,9 @@ class Net_SMTP
$result = $this->_socket->write($data);
if ($result === false) {
return Net_SMTP::raiseError('Failed to write to socket: ' . $result->getMessage(),
$result);
// return Net_SMTP::raiseError('Failed to write to socket: ' . $result->getMessage(),
// $result);
return Net_SMTP::raiseError('Failed to write to socket: ');
}
return $result;
@ -330,7 +374,8 @@ class Net_SMTP
if (empty($line)) {
$this->disconnect();
return Net_SMTP::raiseError('Connection was closed',
null, PEAR_ERROR_RETURN);
// null, PEAR_ERROR_RETURN);
null, 1);
}
/* Read the code and store the rest in the arguments array. */
@ -361,7 +406,8 @@ class Net_SMTP
}
return Net_SMTP::raiseError('Invalid response code received from server',
$this->_code, PEAR_ERROR_RETURN);
// $this->_code, PEAR_ERROR_RETURN);
$this->_code, 1);
}
/**
@ -429,8 +475,9 @@ class Net_SMTP
$this->_socket_options);
//if (PEAR::isError($result)) {
if ($result === false) {
return Net_SMTP::raiseError('Failed to connect socket: ' .
$result->getMessage());
// return Net_SMTP::raiseError('Failed to connect socket: ' .
// $result->getMessage());
return Net_SMTP::raiseError('Failed to connect socket: ');
}
/*
@ -509,7 +556,8 @@ class Net_SMTP
//if (PEAR::isError($this->_parseResponse(250))) {
if (($this->_parseResponse(250)) === false) {
return Net_SMTP::raiseError('HELO was not accepted: ', $this->_code,
PEAR_ERROR_RETURN);
// PEAR_ERROR_RETURN);
1);
}
return true;
@ -548,7 +596,8 @@ class Net_SMTP
}
return Net_SMTP::raiseError('No supported authentication methods',
null, PEAR_ERROR_RETURN);
// null, PEAR_ERROR_RETURN);
null, 1);
}
/**
@ -587,7 +636,7 @@ class Net_SMTP
return $result;
}
//if (PEAR::isError($result = $this->_socket->enableCrypto(true, STREAM_CRYPTO_METHOD_TLS_CLIENT))) {
if (($result = $this->_socket->enableCrypto(true, STREAM_CRYPTO_METHOD_TLS_CLIENT)) === false) {
if (($result = $this->_socket->enableCrypto(true, STREAM_CRYPTO_METHOD_TLS_CLIENT, $this->_verify_peer, $this->_verify_peer_name, $this->_allow_self_signed)) === false) {
return $result;
} elseif ($result !== true) {
return Net_SMTP::raiseError('STARTTLS failed');

View file

@ -680,6 +680,9 @@ class Net_Socket
* 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
@ -688,12 +691,19 @@ class Net_Socket
* A PEAR_Error object is returned if the socket is not
* connected
*/
function enableCrypto($enabled, $type)
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';

File diff suppressed because it is too large Load diff

View file

@ -119,7 +119,16 @@ class iCalProp {
* @param string $propstring The string from the iCalendar which contains this property.
*/
function ParseFrom( $propstring ) {
$this->rendered = (strlen($propstring) < 72 ? $propstring : null); // Only pre-rendered if we didn't unescape it
// $this->rendered = (strlen($propstring) < 72 ? $propstring : null); // Only pre-rendered if we didn't unescape it
// FMBIETE - unset rendered content; if we alter some properties inside an object (VEVENT/ATTENDEE for example) we won't see the changes calling Render
// FIXME: if you find the bug, let me know
//$ical = new iCalComponent();
//$ical->ParseFrom(VCALENDAR DATA);
// Doing this will refresh the rendered data, but if this line is not executed, you won't see PARTSTAT changed
//$ical->SetPValue("METHOD", "REPLY");
//$ical->SetCPParameterValue("VEVENT", "ATTENDEE", "PARTSTAT", "ACCEPTED");
//printf("%s\n", $ical->Render());
unset($this->rendered);
$unescaped = preg_replace( '{\\\\[nN]}', "\n", $propstring);
@ -227,8 +236,14 @@ class iCalProp {
*/
function SetParameterValue( $name, $value ) {
if ( isset($this->rendered) ) unset($this->rendered);
// Unset parameter
if ($value === null) {
unset($this->parameters[$name]);
}
else {
$this->parameters[$name] = $value;
}
}
/**
* Render the set of parameters as key1=value1[;key2=value2[; ...]] with
@ -553,10 +568,16 @@ class iCalComponent {
for ( $i = 0; $i < count($this->properties); $i++ ) {
if ( $this->properties[$i]->Name() == $type ) {
if ( isset($this->rendered) ) unset($this->rendered);
// FMBIETE - unset property
if ($value == null) {
unset($this->properties[$i]);
}
else {
$this->properties[$i]->Value($value);
}
}
}
}
/**
@ -566,15 +587,23 @@ class iCalComponent {
* @param string $property_name Type/Name of the property
* @param string $parameter_name Type/Name of the parameter
* @param string $value New value of the parameter
* @param string $condition_value Change the parameter_value only if the property_value is equals to condition_value
*/
function SetCPParameterValue( $component_type, $property_name, $parameter_name, $value ) {
function SetCPParameterValue( $component_type, $property_name, $parameter_name, $value, $condition_value = null ) {
for ( $j = 0; $j < count($this->components); $j++ ) {
if ( $this->components[$j]->GetType() == $component_type ) {
for ( $i = 0; $i < count($this->components[$j]->properties); $i++ ) {
if ( $this->components[$j]->properties[$i]->Name() == $property_name ) {
if ( isset($this->components[$j]->rendered) ) unset($this->components[$j]->rendered);
if ($condition_value === null) {
$this->components[$j]->properties[$i]->SetParameterValue($parameter_name, $value);
}
else {
if (strcasecmp($this->components[$j]->properties[$i]->Value(), $condition_value) == 0) {
$this->components[$j]->properties[$i]->SetParameterValue($parameter_name, $value);
}
}
}
}
}
}

View file

@ -52,7 +52,7 @@
* @author Sean Coates <sean@php.net>
* @copyright 2003-2006 PEAR <pear-group@php.net>
* @license http://www.opensource.org/licenses/bsd-license.php BSD License
* @version CVS: $Id: mimeDecode.php 305875 2010-12-01 07:17:10Z alan_k $
* @version CVS: $Id: mimeDecode.php 335147 2014-10-27 08:41:39Z alan_k $
* @link http://pear.php.net/package/Mail_mime
*/
@ -68,7 +68,6 @@
*
* used "old" method of checking if called statically, as this is deprecated between php 5.0.0 and 5.3.0
* (isStatic of decode() around line 215)
*
*/
/**
@ -160,6 +159,7 @@ class Mail_mimeDecode
*/
var $_decode_headers;
/**
* Flag to determine whether to include attached messages
* as body in the returned object. Depends on $_include_bodies
@ -178,7 +178,7 @@ class Mail_mimeDecode
* @param string The input to decode
* @access public
*/
function Mail_mimeDecode($input, $deprecated_linefeed = '')
function Mail_mimeDecode($input)
{
list($header, $body) = $this->_splitBodyHeader($input);
@ -202,6 +202,7 @@ class Mail_mimeDecode
* decode_bodies - Whether to decode the bodies
* of the parts. (Transfer encoding)
* decode_headers - Whether to decode headers
*
* input - If called statically, this will be treated
* as the input
* charset - convert all data to this charset
@ -211,7 +212,7 @@ class Mail_mimeDecode
function decode($params = null)
{
// determine if this method has been called statically
$isStatic = !(isset($this) && get_class($this) == __CLASS__);
$isStatic = empty($this) || !is_a($this, __CLASS__);
// Have we been called statically?
// If so, create an object and pass details to that.
@ -237,6 +238,11 @@ class Mail_mimeDecode
$this->_charset = isset($params['charset']) ?
strtolower($params['charset']) : 'utf-8';
if (is_string($this->_decode_headers) && !function_exists('iconv')) {
$this->raiseError('header decode conversion requested, however iconv is missing');
}
$structure = $this->_decode($this->_header, $this->_body);
if ($structure === false) {
$structure = $this->raiseError($this->_error);
@ -263,7 +269,7 @@ class Mail_mimeDecode
$headers = $this->_parseHeaders($headers);
foreach ($headers as $value) {
$value['value'] = $this->_decode_headers ? $this->_decodeHeader($value['value']) : $value['value'];
$value['value'] = $this->_decodeHeader($value['value']);
if (isset($return->headers[strtolower($value['name'])]) AND !is_array($return->headers[strtolower($value['name'])])) {
$return->headers[strtolower($value['name'])] = array($return->headers[strtolower($value['name'])]);
$return->headers[strtolower($value['name'])][] = $value['value'];
@ -284,7 +290,12 @@ class Mail_mimeDecode
case 'content-type':
$content_type = $this->_parseHeaderValue($headers[$key]['value']);
if (preg_match('/([0-9a-z+.-]+)\/([0-9a-z+.-]+)/i', $content_type['value'], $regs)) {
if (preg_match('/([0-9a-z+.-]+)\/([0-9a-z+.-]+)\; name=\"([0-9a-z+.-]+)/i', $headers[$key]['value'], $regs)) {
$return->ctype_primary = $regs[1];
$return->ctype_secondary = $regs[2];
$return->filename = $regs[3];
}
elseif (preg_match('/([0-9a-z+.-]+)\/([0-9a-z+.-]+)/i', $content_type['value'], $regs)) {
$return->ctype_primary = $regs[1];
$return->ctype_secondary = $regs[2];
}
@ -317,22 +328,24 @@ class Mail_mimeDecode
case 'text/plain':
$encoding = isset($content_transfer_encoding) ? $content_transfer_encoding['value'] : '7bit';
$charset = isset($return->ctype_parameters['charset']) ? $return->ctype_parameters['charset'] : $this->_charset;
$this->_include_bodies ? $return->body = ($this->_decode_bodies ? $this->_decodeBody($body, $encoding, $charset) : $body) : null;
$this->_include_bodies ? $return->body = ($this->_decode_bodies ? $this->_decodeBody($body, $encoding, $charset, true) : $body) : null;
break;
case 'text/html':
$encoding = isset($content_transfer_encoding) ? $content_transfer_encoding['value'] : '7bit';
$charset = isset($return->ctype_parameters['charset']) ? $return->ctype_parameters['charset'] : $this->_charset;
$this->_include_bodies ? $return->body = ($this->_decode_bodies ? $this->_decodeBody($body, $encoding, $charset) : $body) : null;
$this->_include_bodies ? $return->body = ($this->_decode_bodies ? $this->_decodeBody($body, $encoding, $charset, true) : $body) : null;
break;
case 'multipart/signed': // PGP
case 'multipart/encrypted': // #190 encrypted parts will be treated as normal ones
case 'multipart/parallel':
case 'multipart/appledouble': // Appledouble mail
case 'multipart/report': // RFC1892
case 'multipart/signed': // PGP
case 'multipart/digest':
case 'multipart/alternative':
case 'multipart/related':
case 'multipart/relative': //#20431 - android
case 'multipart/mixed':
case 'application/vnd.wap.multipart.related':
if(!isset($content_type['other']['boundary'])){
@ -353,6 +366,7 @@ class Mail_mimeDecode
break;
case 'message/rfc822':
case 'message/delivery-status': // #bug #18693
if ($this->_rfc822_bodies) {
$encoding = isset($content_transfer_encoding) ? $content_transfer_encoding['value'] : '7bit';
$charset = isset($return->ctype_parameters['charset']) ? $return->ctype_parameters['charset'] : $this->_charset;
@ -364,8 +378,29 @@ class Mail_mimeDecode
'decode_bodies' => $this->_decode_bodies,
'decode_headers' => $this->_decode_headers));
unset($obj);
// #213, KD 2015-06-29 - Always inline them because there is no "type" to them (they're text)
$return->disposition = 'inline';
break;
// #190, KD 2015-06-09 - Add type for S/MIME Encrypted messages; these must have the filename set explicitly (it won't work otherwise)
//and then falls through for the rest on purpose.
case 'application/x-pkcs7-mime':
case 'application/pkcs7-mime':
if (!isset($content_transfer_encoding['value'])) {
$content_transfer_encoding['value'] = 'base64';
}
// if there is no explicit charset, then don't try to convert to default charset, and make sure that only text mimetypes are converted
$charset = (isset($return->ctype_parameters['charset']) && ((isset($return->ctype_primary) && $return->ctype_primary == 'text') || !isset($return->ctype_primary)) ) ? $return->ctype_parameters['charset'] : '';
$part->body = ($this->_decode_bodies ? $this->_decodeBody($body, $content_transfer_encoding['value'], $charset, false) : $body);
$ctype = explode('/', strtolower($content_type['value']));
$part->ctype_parameters['name'] = 'smime.p7m';
$part->ctype_primary = $ctype[0];
$part->ctype_secondary = $ctype[1];
$part->d_parameters['size'] = strlen($part->body);
$return->parts[] = $part;
// Fall through intentionally
default:
if(!isset($content_transfer_encoding['value']))
$content_transfer_encoding['value'] = '7bit';
@ -460,7 +495,6 @@ class Mail_mimeDecode
*/
function _parseHeaders($input)
{
if ($input !== '') {
// Unfold the input
$input = preg_replace("/\r?\n/", "\r\n", $input);
@ -471,12 +505,25 @@ class Mail_mimeDecode
$input = preg_replace("/\r\n(\t| )+/", ' ', $input);
$headers = explode("\r\n", trim($input));
$got_start = false;
foreach ($headers as $value) {
if (!$got_start) {
// munge headers for mbox style from
if ($value[0] == '>') {
$value = substring($value, 1); // remove mbox >
}
if (substr($value,0,5) == 'From ') {
$value = 'Return-Path: ' . substr($value, 5);
} else {
$got_start = true;
}
}
$hdr_name = substr($value, 0, $pos = strpos($value, ':'));
$hdr_value = substr($value, $pos+1);
if($hdr_value[0] == ' ')
if($hdr_value[0] == ' ') {
$hdr_value = substr($hdr_value, 1);
}
$return[] = array(
'name' => $hdr_name,
@ -497,15 +544,25 @@ class Mail_mimeDecode
* robust as it could be. Eg. header comments
* in the wrong place will probably break it.
*
* Extra things this can handle
* filename*0=......
* filename*1=......
*
* This is where lines are broken in, and need merging.
*
* filename*0*=ENC'lang'urlencoded data.
* filename*1*=ENC'lang'urlencoded data.
*
*
*
* @param string Header value to parse
* @return array Contains parsed result
* @access private
*/
function _parseHeaderValue($input)
{
if (($pos = strpos($input, ';')) === false) {
$input = $this->_decode_headers ? $this->_decodeHeader($input) : $input;
$input = $this->_decodeHeader($input);
$return['value'] = trim($input);
return $return;
}
@ -513,7 +570,7 @@ class Mail_mimeDecode
$value = substr($input, 0, $pos);
$value = $this->_decode_headers ? $this->_decodeHeader($value) : $value;
$value = $this->_decodeHeader($value);
$return['value'] = trim($value);
$input = trim(substr($input, $pos+1));
@ -557,7 +614,6 @@ class Mail_mimeDecode
if ($key) { // a key without a value..
$key= trim($key);
$return['other'][$key] = '';
$return['other'][strtolower($key)] = '';
}
$key = '';
}
@ -574,7 +630,11 @@ class Mail_mimeDecode
$i++;
continue; // skip leading spaces after '=' or after '"'
}
if (!$escaped && ($c == '"' || $c == "'")) {
// do not de-quote 'xxx*= itesm..
$key_is_trans = $key[strlen($key)-1] == '*';
if (!$key_is_trans && !$escaped && ($c == '"' || $c == "'")) {
// start quoted area..
$q = $c;
// in theory should not happen raw text in value part..
@ -586,25 +646,7 @@ class Mail_mimeDecode
// got end....
if (!$escaped && $c == ';') {
$val = trim($val);
$added = false;
if (preg_match('/\*[0-9]+$/', $key)) {
// this is the extended aaa*0=...;aaa*1=.... code
// it assumes the pieces arrive in order, and are valid...
$key = preg_replace('/\*[0-9]+$/', '', $key);
if (isset($return['other'][$key])) {
$return['other'][$key] .= $val;
if (strtolower($key) != $key) {
$return['other'][strtolower($key)] .= $val;
}
$added = true;
}
// continue and use standard setters..
}
if (!$added) {
$return['other'][$key] = $val;
$return['other'][strtolower($key)] = $val;
}
$return['other'][$key] = trim($val);
$val = false;
$key = '';
$lq = false;
@ -636,29 +678,58 @@ class Mail_mimeDecode
if (strlen(trim($key)) || $val !== false) {
$val = trim($val);
$added = false;
if ($val !== false && preg_match('/\*[0-9]+$/', $key)) {
// no dupes due to our crazy regexp.
$key = preg_replace('/\*[0-9]+$/', '', $key);
if (isset($return['other'][$key])) {
$return['other'][$key] .= $val;
if (strtolower($key) != $key) {
$return['other'][strtolower($key)] .= $val;
}
$added = true;
}
// continue and use standard setters..
}
if (!$added) {
$return['other'][$key] = $val;
$return['other'][strtolower($key)] = $val;
}
$clean_others = array();
// merge added values. eg. *1[*]
foreach($return['other'] as $key =>$val) {
if (preg_match('/\*[0-9]+\**$/', $key)) {
$key = preg_replace('/(.*)\*[0-9]+(\**)$/', '\1\2', $key);
if (isset($clean_others[$key])) {
$clean_others[$key] .= $val;
continue;
}
}
$clean_others[$key] = $val;
}
// handle language translation of '*' ending others.
foreach( $clean_others as $key =>$val) {
if ( $key[strlen($key)-1] != '*') {
$clean_others[strtolower($key)] = $val;
continue;
}
unset($clean_others[$key]);
$key = substr($key,0,-1);
//extended-initial-value := [charset] "'" [language] "'"
// extended-other-values
$match = array();
$info = preg_match("/^([^']+)'([^']*)'(.*)$/", $val, $match);
$clean_others[$key] = urldecode($match[3]);
$clean_others[strtolower($key)] = $clean_others[$key];
$clean_others[strtolower($key).'-charset'] = $match[1];
$clean_others[strtolower($key).'-language'] = $match[2];
}
$return['other'] = $clean_others;
// decode values.
foreach($return['other'] as $key =>$val) {
$return['other'][$key] = $this->_decode_headers ? $this->_decodeHeader($val) : $val;
$charset = isset($return['other'][$key . '-charset']) ?
$return['other'][$key . '-charset'] : false;
$return['other'][$key] = $this->_decodeHeader($val, $charset);
}
//print_r($return);
return $return;
}
@ -670,7 +741,7 @@ class Mail_mimeDecode
* @return array Contains array of resulting mime parts
* @access private
*/
function _boundarySplit($input, $boundary)
function _boundarySplit($input, $boundary, $eatline = false)
{
$parts = array();
@ -680,7 +751,10 @@ class Mail_mimeDecode
if ($boundary == $bs_check) {
$boundary = $bs_possible;
}
$tmp = preg_split("/--".preg_quote($boundary, '/')."((?=\s)|--)/", $input);
// eatline is used by multipart/signed.
$tmp = $eatline ?
preg_split("/\r?\n--".preg_quote($boundary, '/')."(|--)\n/", $input) :
preg_split("/--".preg_quote($boundary, '/')."((?=\s)|--)/", $input);
$len = count($tmp) -1;
for ($i = 1; $i < $len; $i++) {
@ -708,11 +782,15 @@ class Mail_mimeDecode
*/
function _decodeHeader($input)
{
if (!$this->_decode_headers) {
return $input;
}
// Remove white space between encoded-words
$input = preg_replace('/(=\?[^?]+\?(q|b)\?[^?]*\?=)(\s)+=\?/i', '\1=?', $input);
$encodedwords = false;
$charset = '';
// For each encoded-word...
while (preg_match('/(=\?([^?]+)\?(q|b)\?([^?]*)\?=)/i', $input, $matches)) {
$encodedwords = true;
@ -730,13 +808,13 @@ class Mail_mimeDecode
case 'q':
$text = str_replace('_', ' ', $text);
preg_match_all('/=([a-f0-9]{2})/i', $text, $matches);
foreach($matches[1] as $value) {
$text = str_replace('='.$value, chr(hexdec($value)), $text);
}
foreach ($matches[1] as $value)
$text = str_replace('=' . $value, chr(hexdec($value)), $text);
break;
}
$input = str_replace($encoded, $this->_autoconvert_encoding($text, $charset), $input);
$text = $this->_autoconvert_encoding($text, $charset);
$input = str_replace($encoded, $text, $input);
}
if (!$encodedwords) {
@ -761,21 +839,20 @@ class Mail_mimeDecode
{
switch (strtolower($encoding)) {
case 'quoted-printable':
$input_decoded = $this->_quotedPrintableDecode($input);
return $detectCharset ? $this->_autoconvert_encoding($input_decoded, $charset) : $input_decoded;
$input = $this->_quotedPrintableDecode($input);
break;
case 'base64':
$input_decoded = base64_decode($input);
return $detectCharset ? $this->_autoconvert_encoding($input_decoded, $charset) : $input_decoded;
$input = base64_decode($input);
break;
case '7bit':
case '8bit':
default:
return $detectCharset ? $this->_autoconvert_encoding($input, $charset) : $input;
break;
}
return $detectCharset ? $this->_autoconvert_encoding($input, $charset) : $input;
}
/**
@ -805,17 +882,26 @@ class Mail_mimeDecode
if (function_exists("mb_detect_order")) {
$mb_order = array_merge(array($supposed_encoding), mb_detect_order());
set_error_handler('Mail_mimeDecode::_iconv_notice_handler');
// Default value in case of error
$detected_encoding = $supposed_encoding;
try {
$input_converted = iconv(mb_detect_encoding($input, $mb_order, true), $this->_charset, $input);
$detected_encoding = mb_detect_encoding($input, $mb_order, true);
// In some cases mb_detect_encoding returns an empty string
if ($detected_encoding === false || strlen($detected_encoding) == 0) {
$detected_encoding = $supposed_encoding;
}
$input_converted = iconv($detected_encoding, $this->_charset, $input);
}
catch(Exception $ex) {
$this->raiseError($ex->getMessage());
}
restore_error_handler();
if (strlen($input_converted) == 0 && strlen($input) != 0) {
ZLog::Write(LOGLEVEL_INFO, "Mail_mimeDecode() - Text cannot be correctly decoded, using original text. Expect encoding errors");
$input_converted = mb_convert_encoding($input, 'UTF-8', 'UTF-8');
if ($input_converted === false || mb_strlen($input_converted, $this->_charset) !== mb_strlen($input, $detected_encoding)) {
ZLog::Write(LOGLEVEL_DEBUG, "Mail_mimeDecode()::_autoconvert_encoding(): Text cannot be correctly decoded, using original text. This will be ok if the part is not text, otherwise expect encoding errors");
$input_converted = $input;
}
}
@ -836,7 +922,10 @@ class Mail_mimeDecode
$input = preg_replace("/=\r?\n/", '', $input);
// Replace encoded characters
$input = preg_replace('/=([a-f0-9]{2})/ie', "chr(hexdec('\\1'))", $input);
$cb = create_function('$matches', ' return chr(hexdec($matches[0]));');
$input = preg_replace_callback( '/=([a-f0-9]{2})/i', $cb, $input);
return $input;
}
@ -923,14 +1012,21 @@ class Mail_mimeDecode
* @param string $message mimedecode message(part)
* @param string $message message subtype
* @param string &$body body reference
* @param boolean $replace_nr replace \n\r with \n
*
* @return void
* @access public
*/
static function getBodyRecursive($message, $subtype, &$body) {
static function getBodyRecursive($message, $subtype, &$body, $replace_nr = false) {
if(!isset($message->ctype_primary)) return;
if(strcasecmp($message->ctype_primary,"text")==0 && strcasecmp($message->ctype_secondary,$subtype)==0 && isset($message->body))
if(strcasecmp($message->ctype_primary, "text") == 0 && strcasecmp($message->ctype_secondary, $subtype) == 0 && isset($message->body)) {
if ($replace_nr) {
$body .= str_replace("\n", "\r\n", str_replace("\r", "", $message->body));
}
else {
$body .= $message->body;
}
}
if(strcasecmp($message->ctype_primary,"multipart")==0 && isset($message->parts) && is_array($message->parts)) {
foreach($message->parts as $part) {
@ -938,7 +1034,7 @@ class Mail_mimeDecode
// Content-Type: text/plain; charset=us-ascii; name="hareandtoroise.txt" Content-Transfer-Encoding: 7bit Content-Disposition: inline; filename="hareandtoroise.txt"
// We don't want to show that file text (outlook doesn't show it), so if we have content-disposition we don't apply recursivity
if(!isset($part->disposition)) {
Mail_mimeDecode::getBodyRecursive($part, $subtype, $body);
Mail_mimeDecode::getBodyRecursive($part, $subtype, $body, $replace_nr);
}
}
}
@ -1117,4 +1213,5 @@ class Mail_mimeDecode
ZLog::Write(LOGLEVEL_ERROR, "mimeDecode error: ". $message);
return false;
}
} // End of class

View file

@ -1,144 +0,0 @@
<?php
/***********************************************
* File : stringstreamwrapper.php
* Project : Z-Push
* Descr : Wraps a string 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 StringStreamWrapper {
const PROTOCOL = "stringstream";
private $stringstream;
private $position;
private $stringlength;
/**
* Opens the stream
* The string to be streamed 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]['string']))
return false;
$this->position = 0;
// this is our stream!
$this->stringstream = $contextOptions[self::PROTOCOL]['string'];
$this->stringlength = strlen($this->stringstream);
ZLog::Write(LOGLEVEL_DEBUG, sprintf("StringStreamWrapper::stream_open(): initialized stream length: %d", $this->stringlength));
return true;
}
/**
* Reads from stream
*
* @param int $len amount of bytes to be read
*
* @access public
* @return string
*/
public function stream_read($len) {
$data = substr($this->stringstream, $this->position, $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->stringlength);
}
/**
* Retrieves information about a stream
*
* @access public
* @return array
*/
public function stream_stat() {
return array(
7 => $this->stringlength,
'size' => $this->stringlength,
);
}
/**
* Instantiates a StringStreamWrapper
*
* @param string $string The string to be wrapped
*
* @access public
* @return StringStreamWrapper
*/
static public function Open($string) {
$context = stream_context_create(array(self::PROTOCOL => array('string' => &$string)));
return fopen(self::PROTOCOL . "://",'r', false, $context);
}
}
stream_wrapper_register(StringStreamWrapper::PROTOCOL, "StringStreamWrapper")
?>

View file

@ -2,7 +2,7 @@
/**
* RFC 822 Email address list validation Utility
*
* PHP versions 4 and 5
* PHP version 5
*
* LICENSE:
*
@ -63,10 +63,11 @@
*
* @author Richard Heyes <richard@phpguru.org>
* @author Chuck Hagenbuch <chuck@horde.org>
* @version $Revision: 294749 $
* @version $Revision$
* @license BSD
* @package Mail
*/
class Mail_RFC822 {
/**
@ -120,7 +121,6 @@ class Mail_RFC822 {
/**
* The number of groups that have been found in the address list.
* @var integer $num_groups
* @access public
*/
var $num_groups = 0;
@ -141,7 +141,6 @@ class Mail_RFC822 {
* Sets up the object. The address must either be set here or when
* calling parseAddressList(). One or the other.
*
* @access public
* @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.
@ -149,7 +148,7 @@ class Mail_RFC822 {
*
* @return object Mail_RFC822 A new Mail_RFC822 object.
*/
function Mail_RFC822($address = null, $default_domain = null, $nest_groups = null, $validate = null, $limit = null)
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;
@ -162,7 +161,6 @@ class Mail_RFC822 {
* Starts the whole process. The address must either be set here
* or when creating the object. One or the other.
*
* @access public
* @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.
@ -170,7 +168,7 @@ class Mail_RFC822 {
*
* @return array A structured array of addresses.
*/
function parseAddressList($address = null, $default_domain = null, $nest_groups = null, $validate = null, $limit = null)
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);
@ -178,6 +176,7 @@ class Mail_RFC822 {
}
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;
@ -223,11 +222,10 @@ class Mail_RFC822 {
/**
* Splits an address into separate addresses.
*
* @access private
* @param string $address The addresses to split.
* @return boolean Success or failure.
*/
function _splitAddresses($address)
protected function _splitAddresses($address)
{
if (!empty($this->limit) && count($this->addresses) == $this->limit) {
return '';
@ -299,11 +297,10 @@ class Mail_RFC822 {
/**
* Checks for a group at the start of the string.
*
* @access private
* @param string $address The address to check.
* @return boolean Whether or not there is a group at the start of the string.
*/
function _isGroup($address)
protected function _isGroup($address)
{
// First comma not in quotes, angles or escaped:
$parts = explode(',', $address);
@ -323,12 +320,11 @@ class Mail_RFC822 {
/**
* A common function that will check an exploded string.
*
* @access private
* @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.
*/
function _splitCheck($parts, $char)
protected function _splitCheck($parts, $char)
{
$string = $parts[0];
@ -356,12 +352,11 @@ class Mail_RFC822 {
/**
* Checks if a string has unclosed quotes or not.
*
* @access private
* @param string $string The string to check.
* @return boolean True if there are unclosed quotes inside the string,
* false otherwise.
*/
function _hasUnclosedQuotes($string)
protected function _hasUnclosedQuotes($string)
{
$string = trim($string);
$iMax = strlen($string);
@ -393,12 +388,11 @@ class Mail_RFC822 {
* Checks if a string has an unclosed brackets or not. IMPORTANT:
* This function handles both angle brackets and square brackets;
*
* @access private
* @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.
*/
function _hasUnclosedBrackets($string, $chars)
protected function _hasUnclosedBrackets($string, $chars)
{
$num_angle_start = substr_count($string, $chars[0]);
$num_angle_end = substr_count($string, $chars[1]);
@ -417,13 +411,12 @@ class Mail_RFC822 {
/**
* Sub function that is used only by hasUnclosedBrackets().
*
* @access private
* @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.
*/
function _hasUnclosedBracketsSub($string, &$num, $char)
protected function _hasUnclosedBracketsSub($string, &$num, $char)
{
$parts = explode($char, $string);
for ($i = 0; $i < count($parts); $i++){
@ -439,11 +432,10 @@ class Mail_RFC822 {
/**
* Function to begin checking the address.
*
* @access private
* @param string $address The address to validate.
* @return mixed False on failure, or a structured array of address information on success.
*/
function _validateAddress($address)
protected function _validateAddress($address)
{
$is_group = false;
$addresses = array();
@ -484,14 +476,6 @@ class Mail_RFC822 {
$addresses[] = $address['address'];
}
// Check that $addresses is set, if address like this:
// Groupname:;
// Then errors were appearing.
if (!count($addresses)){
$this->error = 'Empty group.';
return false;
}
// Trim the whitespace from all of the address strings.
array_map('trim', $addresses);
@ -532,11 +516,10 @@ class Mail_RFC822 {
/**
* Function to validate a phrase.
*
* @access private
* @param string $phrase The phrase to check.
* @return boolean Success or failure.
*/
function _validatePhrase($phrase)
protected function _validatePhrase($phrase)
{
// Splits on one or more Tab or space.
$parts = preg_split('/[ \\x09]+/', $phrase, -1, PREG_SPLIT_NO_EMPTY);
@ -573,11 +556,10 @@ class Mail_RFC822 {
* can split a list of addresses up before encoding personal names
* (umlauts, etc.), for example.
*
* @access private
* @param string $atom The string to check.
* @return boolean Success or failure.
*/
function _validateAtom($atom)
protected function _validateAtom($atom)
{
if (!$this->validate) {
// Validation has been turned off; assume the atom is okay.
@ -606,11 +588,10 @@ class Mail_RFC822 {
* Function to validate quoted string, which is:
* quoted-string = <"> *(qtext/quoted-pair) <">
*
* @access private
* @param string $qstring The string to check
* @return boolean Success or failure.
*/
function _validateQuotedString($qstring)
protected function _validateQuotedString($qstring)
{
// Leading and trailing "
$qstring = substr($qstring, 1, -1);
@ -624,11 +605,10 @@ class Mail_RFC822 {
* mailbox = addr-spec ; simple address
* / phrase route-addr ; name and route-addr
*
* @access public
* @param string &$mailbox The string to check.
* @return boolean Success or failure.
*/
function validateMailbox(&$mailbox)
public function validateMailbox(&$mailbox)
{
// A couple of defaults.
$phrase = '';
@ -718,11 +698,10 @@ class Mail_RFC822 {
* Angle brackets have already been removed at the point of
* getting to this function.
*
* @access private
* @param string $route_addr The string to check.
* @return mixed False on failure, or an array containing validated address/route information on success.
*/
function _validateRouteAddr($route_addr)
protected function _validateRouteAddr($route_addr)
{
// Check for colon.
if (strpos($route_addr, ':') !== false) {
@ -768,11 +747,10 @@ class Mail_RFC822 {
* Function to validate a route, which is:
* route = 1#("@" domain) ":"
*
* @access private
* @param string $route The string to check.
* @return mixed False on failure, or the validated $route on success.
*/
function _validateRoute($route)
protected function _validateRoute($route)
{
// Split on comma.
$domains = explode(',', trim($route));
@ -791,11 +769,10 @@ class Mail_RFC822 {
*
* domain = sub-domain *("." sub-domain)
*
* @access private
* @param string $domain The string to check.
* @return mixed False on failure, or the validated domain on success.
*/
function _validateDomain($domain)
protected function _validateDomain($domain)
{
// Note the different use of $subdomains and $sub_domains
$subdomains = explode('.', $domain);
@ -819,11 +796,10 @@ class Mail_RFC822 {
* Function to validate a subdomain:
* subdomain = domain-ref / domain-literal
*
* @access private
* @param string $subdomain The string to check.
* @return boolean Success or failure.
*/
function _validateSubdomain($subdomain)
protected function _validateSubdomain($subdomain)
{
if (preg_match('|^\[(.*)]$|', $subdomain, $arr)){
if (!$this->_validateDliteral($arr[1])) return false;
@ -839,11 +815,10 @@ class Mail_RFC822 {
* Function to validate a domain literal:
* domain-literal = "[" *(dtext / quoted-pair) "]"
*
* @access private
* @param string $dliteral The string to check.
* @return boolean Success or failure.
*/
function _validateDliteral($dliteral)
protected function _validateDliteral($dliteral)
{
return !preg_match('/(.)[][\x0D\\\\]/', $dliteral, $matches) && $matches[1] != '\\';
}
@ -853,11 +828,10 @@ class Mail_RFC822 {
*
* addr-spec = local-part "@" domain
*
* @access private
* @param string $addr_spec The string to check.
* @return mixed False on failure, or the validated addr-spec on success.
*/
function _validateAddrSpec($addr_spec)
protected function _validateAddrSpec($addr_spec)
{
$addr_spec = trim($addr_spec);
@ -884,17 +858,16 @@ class Mail_RFC822 {
* Function to validate the local part of an address:
* local-part = word *("." word)
*
* @access private
* @param string $local_part
* @return mixed False on failure, or the validated local part on success.
*/
function _validateLocalPart($local_part)
protected function _validateLocalPart($local_part)
{
$parts = explode('.', $local_part);
$words = array();
// Split the local_part into words.
while (count($parts) > 0){
while (count($parts) > 0) {
$words[] = $this->_splitCheck($parts, '.');
for ($i = 0; $i < $this->index + 1; $i++) {
array_shift($parts);
@ -903,6 +876,10 @@ class Mail_RFC822 {
// 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] !== '"')
{

View file

@ -702,6 +702,3 @@ class rtf {
}
}
}
?>

View file

@ -8,7 +8,7 @@
* caldav-client-v2.php by xbgmsharp <xbgmsharp@gmail.com>.
*
* Copyright Andrew McMillan (original caldav-client-v2.php), Jean-Louis Dupond (cURL code), xbgmsharp (bugfixes)
* Copyright Thorsten Köster
* Copyright Thorsten Köster
* License GNU LGPL version 3 or later (http://www.gnu.org/licenses/lgpl-3.0.txt)
*/
@ -69,6 +69,11 @@ class CalDAVClient {
*/
protected $calendar_urls;
/**
* Construct URL
*/
protected $url;
/**
* The useragent which is send to the caldav server
*
@ -89,8 +94,7 @@ class CalDAVClient {
*
* @var resource
*/
private $curl;
private $curl = false;
private $synctoken = array();
@ -102,25 +106,61 @@ class CalDAVClient {
* @param string $pass The password for that user
*/
function __construct( $caldav_url, $user, $pass ) {
$this->url = $caldav_url;
$this->user = $user;
$this->pass = $pass;
$this->auth = $user . ':' . $pass;
$this->headers = array();
$parsed_url = parse_url($caldav_url);
if ($parsed_url == FALSE) {
ZLog::Write(LOGLEVEL_ERROR, sprintf('Couldn\'t parse URL: %s', $caldav_url));
} else
if ($parsed_url === false) {
ZLog::Write(LOGLEVEL_ERROR, sprintf("BackendCalDAV->caldav_backend(): Couldn't parse URL: %s", $caldav_url));
return;
}
$this->server = $parsed_url['scheme'] . '://' . $parsed_url['host'] . ':' . $parsed_url['port'];
$this->base_url = $parsed_url['path'];
// $this->base_url .= !empty($parsed_url['query']) ? '?' . $parsed_url['query'] : '';
// $this->base_url .= !empty($parsed_url['fragment']) ? '#' . $parsed_url['fragment'] : '';
//ZLog::Write(LOGLEVEL_DEBUG, sprintf("BackendCalDAV->caldav_backend(): base_url '%s'", $this->base_url));
//$this->base_url .= !empty($parsed_url['query']) ? '?' . $parsed_url['query'] : '';
//$this->base_url .= !empty($parsed_url['fragment']) ? '#' . $parsed_url['fragment'] : '';
if (substr($this->base_url, -1, 1) !== '/') {
if (substr($this->base_url, -1) !== '/') {
$this->base_url = $this->base_url . '/';
}
}
/**
* Checks if the CalDAV server is reachable
*
* @return boolean
*/
public function CheckConnection() {
$result = $this->DoRequest($this->url, 'OPTIONS');
switch ($this->httpResponseCode) {
case 200:
case 207:
case 401:
$status = true;
break;
default:
$status = false;
}
return $status;
}
/**
* Disconnect curl connection
*
*/
public function Disconnect() {
if ($this->curl !== false) {
curl_close($this->curl);
$this->curl = false;
}
}
/**
* Adds an If-Match or If-None-Match header
@ -178,7 +218,7 @@ class CalDAVClient {
public function curl_init() {
if (empty($this->curl)) {
if ($this->curl === false) {
$this->curl = curl_init();
curl_setopt($this->curl, CURLOPT_HEADER, true);
curl_setopt($this->curl, CURLOPT_SSL_VERIFYHOST, false);
@ -210,6 +250,7 @@ class CalDAVClient {
curl_setopt($this->curl, CURLOPT_URL, $url);
curl_setopt($this->curl, CURLOPT_CUSTOMREQUEST, $method);
curl_setopt($this->curl, CURLOPT_CONNECTTIMEOUT, 30); // 30 seconds it's already too big
if ($content !== null)
{
@ -231,9 +272,9 @@ class CalDAVClient {
$this->xmlResponse = '';
//ZLog::Write(LOGLEVEL_DEBUG, sprintf("Request:\n%s\n", $content));
// ZLog::Write(LOGLEVEL_DEBUG, sprintf("Request:\n%s\n", $content));
$response = curl_exec($this->curl);
//ZLog::Write(LOGLEVEL_DEBUG, sprintf("Reponse:\n%s\n", $response));
// ZLog::Write(LOGLEVEL_DEBUG, sprintf("Reponse:\n%s\n", $response));
$header_size = curl_getinfo($this->curl, CURLINFO_HEADER_SIZE);
$this->httpResponseCode = curl_getinfo($this->curl, CURLINFO_HTTP_CODE);
$this->httpResponseHeaders = trim(substr($response, 0, $header_size));
@ -253,7 +294,7 @@ class CalDAVClient {
* @return array The allowed options
*/
function DoOptionsRequest( $url = null ) {
$headers = $this->DoRequest($url, "OPTIONS");
$headers = $this->DoRequest($url === null ? $this->url : $url, "OPTIONS");
$options_header = preg_replace( '/^.*Allow: ([a-z, ]+)\r?\n.*/is', '$1', $headers );
$options = array_flip( preg_split( '/[, ]+/', $options_header ));
return $options;
@ -315,7 +356,6 @@ class CalDAVClient {
}
if ( !isset($etag) || $etag == '' ) {
ZLog::Write(LOGLEVEL_DEBUG, sprintf("No etag in:\n%s\n", $this->httpResponseHeaders));
$save_request = $this->httpRequest;
$save_response_headers = $this->httpResponseHeaders;
$this->DoHEADRequest( $url );
if ( preg_match( '{^Etag:\s+"([^"]*)"\s*$}im', $this->httpResponseHeaders, $matches ) ) {
@ -324,7 +364,6 @@ class CalDAVClient {
if ( !isset($etag) || $etag == '' ) {
ZLog::Write(LOGLEVEL_DEBUG, sprintf("Still No etag in:\n%s\n", $this->httpResponseHeaders));
}
$this->httpRequest = $save_request;
$this->httpResponseHeaders = $save_response_headers;
}
return $etag;
@ -904,8 +943,9 @@ EOFILTER;
$this->SetCalendar($relative_url);
}
$hasToken = !$initial && isset($this->synctoken[$this->calendar_url]);
if ($support_dav_sync) {
$token = ($initial ? "" : $this->synctoken[$this->calendar_url]);
$token = ($hasToken ? $this->synctoken[$this->calendar_url] : "");
$body = <<<EOXML
<?xml version="1.0" encoding="utf-8"?>
@ -938,12 +978,13 @@ EOXML;
$this->DoRequest($this->calendar_url, "REPORT", $body, "text/xml");
$report = array();
foreach( $this->xmlnodes as $k => $v ) {
switch( $v['tag'] ) {
foreach ($this->xmlnodes as $k => $v) {
switch ($v['tag']) {
case 'DAV::response':
if ( $v['type'] == 'open' ) {
if ($v['type'] == 'open') {
$response = array();
} elseif ( $v['type'] == 'close' ) {
}
elseif ($v['type'] == 'close') {
$report[] = $response;
}
break;
@ -966,6 +1007,12 @@ EOXML;
break;
}
}
// Report sync-token support on initial sync
if ($initial && $support_dav_sync && !isset($this->synctoken[$this->calendar_url])) {
ZLog::Write(LOGLEVEL_WARN, 'CalDAVClient->GetSync(): no DAV::sync-token received; did you set CALDAV_SUPPORTS_SYNC correctly?');
}
return $report;
}

View file

@ -160,7 +160,7 @@ class carddav_backend
*
* @var resource
*/
private $curl;
private $curl = false;
/**
* Debug on or off
@ -546,7 +546,6 @@ EOFXMLGETXMLVCARD;
switch($result['http_code']) {
case 200:
case 207:
case 401:
$status = true;
break;
}
@ -771,7 +770,7 @@ EOFXSL;
* @return void
*/
public function curl_init() {
if (empty($this->curl)) {
if ($this->curl === false) {
$this->curl = curl_init();
curl_setopt($this->curl, CURLOPT_HEADER, true);
curl_setopt($this->curl, CURLOPT_SSL_VERIFYHOST, false);
@ -885,8 +884,17 @@ EOFXSL;
* @return void
*/
public function __destruct() {
if (!empty($this->curl)) {
$this->disconnect();
}
/**
* Disconnect curl connection
*
*/
public function disconnect() {
if ($this->curl !== false) {
curl_close($this->curl);
$this->curl = false;
}
}
}

View file

@ -43,85 +43,29 @@
* Consult LICENSE file for details
************************************************/
ob_start(null, 1048576);
// #190, KD 2015-06-08 - We are missing the flags to truncate the buffer in PHP >= 5.4
if (version_compare(phpversion(), '5.4.0') < 0) {
ob_start(null, 1048576);
}
else {
ob_start(null, 1048576, PHP_OUTPUT_HANDLER_STDFLAGS);
}
// ignore user abortions because this can lead to weird errors - see ZP-239
ignore_user_abort(true);
include_once('lib/exceptions/exceptions.php');
include_once('lib/utils/utils.php');
include_once('lib/utils/compat.php');
include_once('lib/utils/timezoneutil.php');
include_once('lib/core/zpushdefs.php');
include_once('lib/core/stateobject.php');
include_once('lib/core/interprocessdata.php');
include_once('lib/core/pingtracking.php');
include_once('lib/core/topcollector.php');
include_once('lib/core/loopdetection.php');
include_once('lib/core/asdevice.php');
include_once('lib/core/statemanager.php');
include_once('lib/core/devicemanager.php');
include_once('lib/core/zpush.php');
include_once('lib/core/zlog.php');
include_once('lib/core/paddingfilter.php');
include_once('lib/interface/ibackend.php');
include_once('lib/interface/ichanges.php');
include_once('lib/interface/iexportchanges.php');
include_once('lib/interface/iimportchanges.php');
include_once('lib/interface/isearchprovider.php');
include_once('lib/interface/istatemachine.php');
include_once('lib/core/streamer.php');
include_once('lib/core/streamimporter.php');
include_once('lib/core/synccollections.php');
include_once('lib/core/hierarchycache.php');
include_once('lib/core/changesmemorywrapper.php');
include_once('lib/core/syncparameters.php');
include_once('lib/core/bodypreference.php');
include_once('lib/core/contentparameters.php');
include_once('lib/wbxml/wbxmldefs.php');
include_once('lib/wbxml/wbxmldecoder.php');
include_once('lib/wbxml/wbxmlencoder.php');
include_once('lib/syncobjects/syncobject.php');
include_once('lib/syncobjects/syncbasebody.php');
include_once('lib/syncobjects/syncbaseattachment.php');
include_once('lib/syncobjects/syncmailflags.php');
include_once('lib/syncobjects/syncrecurrence.php');
include_once('lib/syncobjects/syncappointment.php');
include_once('lib/syncobjects/syncappointmentexception.php');
include_once('lib/syncobjects/syncattachment.php');
include_once('lib/syncobjects/syncattendee.php');
include_once('lib/syncobjects/syncmeetingrequestrecurrence.php');
include_once('lib/syncobjects/syncmeetingrequest.php');
include_once('lib/syncobjects/syncmail.php');
include_once('lib/syncobjects/syncnote.php');
include_once('lib/syncobjects/synccontact.php');
include_once('lib/syncobjects/syncfolder.php');
include_once('lib/syncobjects/syncprovisioning.php');
include_once('lib/syncobjects/synctaskrecurrence.php');
include_once('lib/syncobjects/synctask.php');
include_once('lib/syncobjects/syncoofmessage.php');
include_once('lib/syncobjects/syncoof.php');
include_once('lib/syncobjects/syncuserinformation.php');
include_once('lib/syncobjects/syncdeviceinformation.php');
include_once('lib/syncobjects/syncdevicepassword.php');
include_once('lib/syncobjects/syncitemoperationsattachment.php');
include_once('lib/syncobjects/syncsendmail.php');
include_once('lib/syncobjects/syncsendmailsource.php');
include_once('lib/syncobjects/syncvalidatecert.php');
include_once('lib/syncobjects/syncresolverecipients.php');
include_once('lib/syncobjects/syncresolverecipient.php');
include_once('lib/syncobjects/syncresolverecipientsoptions.php');
include_once('lib/syncobjects/syncresolverecipientsavailability.php');
include_once('lib/syncobjects/syncresolverecipientscertificates.php');
include_once('lib/syncobjects/syncresolverecipientspicture.php');
include_once('lib/default/backend.php');
include_once('lib/default/searchprovider.php');
include_once('lib/request/request.php');
include_once('lib/request/requestprocessor.php');
include_once('config.php');
include_once('version.php');
require_once 'vendor/autoload.php';
require_once 'config.php';
if (defined('LOG_MEMORY_PROFILER') && LOG_MEMORY_PROFILER) {
if (function_exists('memprof_enable')) {
memprof_enable();
}
else {
ZLog::Write(LOGLEVEL_WARN, "Memory profiler is enabled but the php-pecl-memprof extension was not found. Install and enable it");
}
}
// Attempt to set maximum execution time
ini_set('max_execution_time', SCRIPT_TIMEOUT);
@ -206,43 +150,24 @@ include_once('version.php');
}
RequestProcessor::Initialize();
if(!RequestProcessor::HandleRequest())
throw new WBXMLException(ZLog::GetWBXMLDebugInfo());
RequestProcessor::HandleRequest();
// eventually the RequestProcessor wants to send other headers to the mobile
foreach (RequestProcessor::GetSpecialHeaders() as $header)
header($header);
// stream the data
$len = ob_get_length();
$data = ob_get_contents();
ob_end_clean();
// log amount of data transferred
// TODO check $len when streaming more data (e.g. Attachments), as the data will be send chunked
ZPush::GetDeviceManager()->SentData($len);
// Unfortunately, even though Z-Push can stream the data to the client
// with a chunked encoding, using chunked encoding breaks the progress bar
// on the PDA. So the data is de-chunk here, written a content-length header and
// data send as a 'normal' packet. If the output packet exceeds 1MB (see ob_start)
// then it will be sent as a chunked packet anyway because PHP will have to flush
// the buffer.
if(!headers_sent())
header("Content-Length: $len");
// send vnd.ms-sync.wbxml content type header if there is no content
// otherwise text/html content type is added which might break some devices
if ($len == 0)
header("Content-Type: application/vnd.ms-sync.wbxml");
print $data;
// destruct backend after all data is on the stream
$backend->Logoff();
ZPush::GetDeviceManager()->SentData(ob_get_length());
}
catch (NoPostRequestException $nopostex) {
$len = ob_get_length();
if ($len) {
ZLog::Write(LOGLEVEL_WARN, sprintf("Cleaning %d octets of data", $len));
ob_clean();
}
if ($nopostex->getCode() == NoPostRequestException::OPTIONS_REQUEST) {
header(ZPush::GetServerHeader());
header(ZPush::GetSupportedProtocolVersions());
@ -258,6 +183,12 @@ include_once('version.php');
}
catch (Exception $ex) {
$len = ob_get_length();
if ($len) {
ZLog::Write(LOGLEVEL_WARN, sprintf("Cleaning %d octets of data", $len));
ob_clean();
}
if (Request::GetUserAgent())
ZLog::Write(LOGLEVEL_INFO, sprintf("User-agent: '%s'", Request::GetUserAgent()));
$exclass = get_class($ex);
@ -285,7 +216,7 @@ include_once('version.php');
// This could be a WBXML problem.. try to get the complete request
else if ($ex instanceof WBXMLException) {
ZLog::Write(LOGLEVEL_FATAL, "Request could not be processed correctly due to a WBXMLException. Please report this.");
ZLog::Write(LOGLEVEL_FATAL, "Request could not be processed correctly due to a WBXMLException. Please report this including WBXML debug data logged. Be aware that the debug data could contain confidential information.");
}
// Try to output some kind of error information. This is only possible if
@ -306,10 +237,29 @@ include_once('version.php');
ZPush::GetTopCollector()->AnnounceInformation(get_class($ex), true);
}
// FinishResponse
ZPush::FinishResponse();
// destruct backend after all data is on the stream
ZPush::GetBackend()->Logoff();
// save device data if the DeviceManager is available
if (ZPush::GetDeviceManager(false))
ZPush::GetDeviceManager()->Save();
// end gracefully
ZLog::Write(LOGLEVEL_DEBUG, '-------- End');
?>
if (version_compare(phpversion(), '5.4.0') < 0) {
$time_used = number_format(time() - $_SERVER["REQUEST_TIME"], 4);
}
else {
$time_used = number_format(microtime(true) - $_SERVER["REQUEST_TIME_FLOAT"], 4);
}
ZLog::Write(LOGLEVEL_DEBUG, sprintf("-------- End - max mem: %s/%s - time: %s - code: %s", memory_get_peak_usage(false), memory_get_peak_usage(true), $time_used, http_response_code()));
if (defined('LOG_MEMORY_PROFILER') && LOG_MEMORY_PROFILER) {
if (function_exists('memprof_enable')) {
// Be aware that the pid is not unique, so we will overwrite the output in some cases. But using the pid will be easier to relate the dump with the log lines
memprof_dump_callgrind(fopen(LOG_MEMORY_PROFILER_FILE . "_" . getmypid(), "w"));
}
}

View file

@ -687,5 +687,3 @@ class ASDevice extends StateObject {
}
}
?>

View file

@ -65,4 +65,3 @@ class BodyPreference extends StateObject {
return (count($this->data) > 0);
}
}
?>

View file

@ -115,7 +115,6 @@ class ChangesMemoryWrapper extends HierarchyCache implements IImportChanges, IEx
public function LoadConflicts($contentparameters, $state) { return true; }
public function ConfigContentParameters($contentparameters) { return true; }
public function ImportMessageReadFlag($id, $flags) { return true; }
public function ImportMessageStarFlag($id, $flags) { return true; }
public function ImportMessageMove($id, $newfolder) { return true; }
/**----------------------------------------------------------------------------------------------------------
@ -346,5 +345,3 @@ class ChangesMemoryWrapper extends HierarchyCache implements IImportChanges, IEx
$this->step = 0;
}
}
?>

View file

@ -137,5 +137,3 @@ class ContentParameters extends StateObject {
return true;
}
}
?>

View file

@ -91,7 +91,7 @@ class DeviceManager {
else
throw new FatalNotImplementedException("Can not proceed without a device id.");
$this->loopdetection = new LoopDetection();
$this->loopdetection = ZPush::GetLoopDetection();
$this->loopdetection->ProcessLoopDetectionInit();
$this->loopdetection->ProcessLoopDetectionPreviousConnectionFailed();
@ -427,6 +427,8 @@ class DeviceManager {
return true;
}
if (!is_object($message))
throw new Exception("DeviceManager->DoNotStreamMessage(): message isn't an object");
// message is semantically incorrect
if (!$message->Check(true)) {
$this->AnnounceIgnoredMessage($folderid, $id, $message, self::MSG_BROKEN_SEMANTICERR);
@ -478,7 +480,7 @@ class DeviceManager {
if (defined("SYNC_MAX_ITEMS") && SYNC_MAX_ITEMS < $items) {
if ($queuedmessages > SYNC_MAX_ITEMS)
ZLog::Write(LOGLEVEL_DEBUG, sprintf("DeviceManager->GetWindowSize() overwriting max itmes requested of %d by %d forced in configuration.", $items, SYNC_MAX_ITEMS));
ZLog::Write(LOGLEVEL_DEBUG, sprintf("DeviceManager->GetWindowSize() overwriting max items requested of %d by %d forced in configuration.", $items, SYNC_MAX_ITEMS));
$items = SYNC_MAX_ITEMS;
}
@ -899,5 +901,3 @@ class DeviceManager {
return $this->latestFolder;
}
}
?>

View file

@ -212,5 +212,3 @@ class HierarchyCache {
}
}
?>

View file

@ -1,101 +0,0 @@
<?php
/***********************************************
* File : paddingfilter.php
* Project : Z-Push
* Descr : Our own filter for stream padding with zero strings.
*
* Created : 18.07.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
************************************************/
/* Define our filter class
*
* Usage: stream_filter_append($stream, 'padding.X');
* where X is a number a stream will be padded to be
* multiple of (e.g. padding.3 will pad the stream
* to be multiple of 3 which is useful in base64
* encoding).
*
* */
class padding_filter extends php_user_filter {
private $padding = 4; // default padding
/**
* This method is called whenever data is read from or written to the attached stream
*
* @see php_user_filter::filter()
*
* @param resource $in
* @param resource $out
* @param int $consumed
* @param boolean $closing
*
* @access public
* @return int
*
*/
function filter($in, $out, &$consumed, $closing) {
while ($bucket = stream_bucket_make_writeable($in)) {
if ($this->padding != 0 && $bucket->datalen < 8192) {
$bucket->data .= str_pad($bucket->data, $this->padding, 0x0);
}
$consumed += ($this->padding != 0 && $bucket->datalen < 8192) ? ($bucket->datalen + $this->padding) : $bucket->datalen;
stream_bucket_append($out, $bucket);
}
return PSFS_PASS_ON;
}
/**
* Called when creating the filter
*
* @see php_user_filter::onCreate()
*
* @access public
* @return boolean
*/
function onCreate() {
$delim = strrpos($this->filtername, '.');
if ($delim !== false) {
$padding = substr($this->filtername, $delim + 1);
if (is_numeric($padding))
$this->padding = $padding;
}
return true;
}
}
stream_filter_register("padding.*", "padding_filter");
?>

View file

@ -537,4 +537,3 @@ class StateManager {
mt_rand( 0, 0xffff ), mt_rand( 0, 0xffff ), mt_rand( 0, 0xffff ) );
}
}
?>

View file

@ -197,7 +197,7 @@ class StateObject implements Serializable {
return true;
}
throw new FatalNotImplementedException(sprintf("StateObject->__call('%s'): not implemented. op: {$operator} args:". count($arguments), $name));
throw new FatalNotImplementedException(sprintf("StateObject->__call('%s'): not implemented. op: {%s} args: %d", $name, $operator, count($arguments)));
}
/**
@ -264,5 +264,3 @@ class StateObject implements Serializable {
throw new StateInvalidException("Unserialization failed as class was not found or not compatible");
}
}
?>

View file

@ -291,7 +291,7 @@ class Streamer implements Serializable {
if ($encoder->getMultipart() && isset($map[self::STREAMER_PROP]) && $map[self::STREAMER_PROP] == self::STREAMER_TYPE_MULTIPART) {
$encoder->addBodypartStream($this->$map[self::STREAMER_VAR]);
$encoder->startTag(SYNC_ITEMOPERATIONS_PART);
$encoder->content(count($encoder->getBodypartsCount()));
$encoder->content($encoder->getBodypartsCount());
$encoder->endTag();
continue;
}
@ -315,22 +315,10 @@ class Streamer implements Serializable {
$encoder->content(strtoupper(bin2hex($this->$map[self::STREAMER_VAR])));
}
else if(isset($map[self::STREAMER_TYPE]) && $map[self::STREAMER_TYPE] == self::STREAMER_TYPE_STREAM) {
//encode stream with base64
$stream = $this->$map[self::STREAMER_VAR];
$stat = fstat($stream);
// the padding size muss be calculated for the entire stream,
// the base64 filter seems to process 8192 byte chunks correctly itself
$padding = (isset($stat['size']) && $stat['size'] > 8192) ? ($stat['size'] % 3) : 0;
$paddingfilter = stream_filter_append($stream, 'padding.'.$padding);
$base64filter = stream_filter_append($stream, 'convert.base64-encode');
$d = "";
while (!feof($stream)) {
$d .= fgets($stream, 4096);
}
$encoder->content($d);
stream_filter_remove($base64filter);
stream_filter_remove($paddingfilter);
//we need to encode in base64
$encoder->content(Utils::EncodeBase64($this->$map[self::STREAMER_VAR]));
//memory ...
$this->$map[self::STREAMER_VAR] = null;
}
// implode comma or semicolon arrays into a string
else if(isset($map[self::STREAMER_TYPE]) && is_array($this->$map[self::STREAMER_VAR]) &&
@ -461,5 +449,3 @@ class Streamer implements Serializable {
return 0;
}
}
?>

View file

@ -44,6 +44,7 @@
class ImportChangesStream implements IImportChanges {
private $encoder;
private $objclass;
private $classAsString;
private $seenObjects;
private $importedMsgs;
private $checkForIgnoredMessages;
@ -189,41 +190,6 @@ class ImportChangesStream implements IImportChanges {
return true;
}
/**
* Imports a change in 'star' flag
* Can only be applied to SyncMail (Email) requests
*
* @param string $id
* @param int $flags - flagged/unflagged
*
* @access public
* @return boolean
*/
public function ImportMessageStarFlag($id, $flags) {
if(!($this->objclass instanceof SyncMail))
return false;
$this->importedMsgs++;
$this->encoder->startTag(SYNC_MODIFY);
$this->encoder->startTag(SYNC_SERVERENTRYID);
$this->encoder->content($id);
$this->encoder->endTag();
$this->encoder->startTag(SYNC_DATA);
$this->encoder->startTag(SYNC_POOMMAIL_FLAG);
$this->encoder->startTag(SYNC_POOMMAIL_FLAGSTATUS);
$this->encoder->content($flags == 1? "2" : "0");
$this->encoder->endTag();
$this->encoder->startTag(SYNC_POOMMAIL_FLAGTYPE);
$this->encoder->content("FollowUp");
$this->encoder->endTag();
$this->encoder->endTag();
$this->encoder->endTag();
$this->encoder->endTag();
return true;
}
/**
* ImportMessageMove is not implemented, as this operation can not be streamed to a WBXMLEncoder
*
@ -293,4 +259,3 @@ class ImportChangesStream implements IImportChanges {
return $this->importedMsgs;
}
}
?>

View file

@ -54,6 +54,7 @@ class SyncCollections implements Iterator {
const ERROR_NO_COLLECTIONS = 1;
const ERROR_WRONG_HIERARCHY = 2;
const OBSOLETE_CONNECTION = 3;
const HIERARCHY_CHANGED = 4;
private $stateManager;
@ -419,7 +420,7 @@ class SyncCollections implements Iterator {
else
$checkClasses = implode(" ", array_keys($classes));
$pingTracking = new PingTracking();
$pingTracking = ZPush::GetPingTracking();
$this->changes = array();
$changesAvailable = false;
@ -473,7 +474,7 @@ class SyncCollections implements Iterator {
// Check if a hierarchy sync is necessary
if (ZPush::GetDeviceManager()->IsHierarchySyncRequired())
throw new StatusException("SyncCollections->CheckForChanges(): HierarchySync required.", self::ERROR_WRONG_HIERARCHY);
throw new StatusException("SyncCollections->CheckForChanges(): HierarchySync required.", self::HIERARCHY_CHANGED);
// Check if there are newer requests
// If so, this process should be terminated if more than 60 secs to go
@ -717,5 +718,3 @@ class SyncCollections implements Iterator {
$this->stateManager = ZPush::GetDeviceManager()->GetStateManager();
}
}
?>

View file

@ -269,7 +269,7 @@ class SyncParameters extends StateObject {
ZLog::Write(LOGLEVEL_DEBUG, "SyncParameters->UseCPO(): removed existing DEFAULT CPO as it is obsolete");
}
ZLOG::Write(LOGLEVEL_DEBUG, sprintf("SyncParameters->UseCPO('%s')", $options));
ZLog::Write(LOGLEVEL_DEBUG, sprintf("SyncParameters->UseCPO('%s')", $options));
$this->currentCPO = $options;
$this->checkCPO($this->currentCPO);
}
@ -347,8 +347,6 @@ class SyncParameters extends StateObject {
elseif (isset($this->contentParameters[self::TASKOPTIONS]))
$returnCPO = self::TASKOPTIONS;
if ($returnCPO != $options)
ZLog::Write(LOGLEVEL_DEBUG, sprintf("SyncParameters->normalizeType(): using %s for requested %s", $returnCPO, $options));
return $returnCPO;
}
// something unexpected happened, just return default, empty in the worst case
@ -410,10 +408,10 @@ class SyncParameters extends StateObject {
* @return boolean
*/
protected function postUnserialize() {
// init with default options
$this->UseCPO();
// init with the available CPO or default
$availableCPO = $this->normalizeType(self::DEFAULTOPTIONS);
$this->UseCPO($availableCPO);
return true;
}
}
?>

View file

@ -50,6 +50,7 @@ class ZLog {
static private $lastLogs = array();
static private $userLog = false;
static private $unAuthCache = array();
static private $syslogEnabled = false;
/**
* Initializes the logging
@ -60,6 +61,11 @@ class ZLog {
static public function Initialize() {
global $specialLogUsers;
if (defined('LOG_SYSLOG_ENABLED') && LOG_SYSLOG_ENABLED) {
self::$syslogEnabled = true;
ZSyslog::Initialize();
}
// define some constants for the logging
if (!defined('LOGUSERLEVEL'))
define('LOGUSERLEVEL', LOGLEVEL_OFF);
@ -111,7 +117,7 @@ class ZLog {
$data = self::buildLogString($loglevel) . $message . "\n";
if ($loglevel <= LOGLEVEL) {
@file_put_contents(LOGFILE, $data, FILE_APPEND);
self::writeToLog($loglevel, $data, LOGFILE);
}
// should we write this into the user log?
@ -123,11 +129,11 @@ class ZLog {
if (self::logToUserFile()) {
// something was logged before the user was authenticated, write this to the log
if (!empty(self::$unAuthCache)) {
@file_put_contents(LOGFILEDIR . self::logToUserFile() . ".log", implode('', self::$unAuthCache), FILE_APPEND);
self::writeToLog($loglevel, implode('', self::$unAuthCache), LOGFILEDIR . self::logToUserFile() . ".log");
self::$unAuthCache = array();
}
// only use plain old a-z characters for the generic log file
@file_put_contents(LOGFILEDIR . self::logToUserFile() . ".log", $data, FILE_APPEND);
self::writeToLog($loglevel, $data, LOGFILEDIR . self::logToUserFile() . ".log");
}
// the user is not authenticated yet, we save the log into memory for now
else {
@ -136,7 +142,7 @@ class ZLog {
}
if (($loglevel & LOGLEVEL_FATAL) || ($loglevel & LOGLEVEL_ERROR)) {
@file_put_contents(LOGERRORFILE, $data, FILE_APPEND);
self::writeToLog($loglevel, $data, LOGERRORFILE);
}
if ($loglevel & LOGLEVEL_WBXMLSTACK) {
@ -205,7 +211,10 @@ class ZLog {
if (!isset(self::$devid))
self::$devid = '';
return Utils::GetFormattedTime() ." ". self::$pidstr . self::getLogLevelString($loglevel, (LOGLEVEL > LOGLEVEL_INFO)) ." ". self::$user . self::$devid;
if (self::$syslogEnabled)
return self::$pidstr . self::getLogLevelString($loglevel, (LOGLEVEL > LOGLEVEL_INFO)) . " " . self::$user . self::$devid;
else
return Utils::GetFormattedTime() . " " . self::$pidstr . self::getLogLevelString($loglevel, (LOGLEVEL > LOGLEVEL_INFO)) . " " . self::$user . self::$devid;
}
/**
@ -233,48 +242,29 @@ class ZLog {
case LOGLEVEL_WBXMLSTACK: return "[WBXMLSTACK]"; break;
}
}
}
/**----------------------------------------------------------------------------------------------------------
* Legacy debug stuff
/**
* Write the message to the log facility.
*
* @param int $loglevel
* @param string $data
* @param string $logfile
*
* @access private
* @return void
*/
// deprecated
// backwards compatible
function debugLog($message) {
ZLog::Write(LOGLEVEL_DEBUG, $message);
}
// TODO review error handler
function zarafa_error_handler($errno, $errstr, $errfile, $errline, $errcontext) {
$bt = debug_backtrace();
switch ($errno) {
case 8192: // E_DEPRECATED since PHP 5.3.0
// do not handle this message
break;
case E_NOTICE:
case E_WARNING:
// TODO check if there is a better way to avoid these messages
if (stripos($errfile,'interprocessdata') !== false && stripos($errstr,'shm_get_var()') !== false)
break;
ZLog::Write(LOGLEVEL_WARN, "$errfile:$errline $errstr ($errno)");
break;
default:
ZLog::Write(LOGLEVEL_ERROR, "trace error: $errfile:$errline $errstr ($errno) - backtrace: ". (count($bt)-1) . " steps");
for($i = 1, $bt_length = count($bt); $i < $bt_length; $i++) {
$file = $line = "unknown";
if (isset($bt[$i]['file'])) $file = $bt[$i]['file'];
if (isset($bt[$i]['line'])) $line = $bt[$i]['line'];
ZLog::Write(LOGLEVEL_ERROR, "trace: $i:". $file . ":" . $line. " - " . ((isset($bt[$i]['class']))? $bt[$i]['class'] . $bt[$i]['type']:""). $bt[$i]['function']. "()");
static private function writeToLog($loglevel, $data, $logfile = null) {
if (self::$syslogEnabled) {
if (ZSyslog::send($loglevel, $data) === false) {
error_log("Unable to send to syslog");
error_log($data);
}
}
else {
if (@file_put_contents($logfile, $data, FILE_APPEND) === false) {
error_log(sprintf("Unable to write in %s", $logfile));
error_log($data);
}
}
//throw new Exception("An error occured.");
break;
}
}
error_reporting(E_ALL);
set_error_handler("zarafa_error_handler");
?>

View file

@ -0,0 +1,73 @@
<?php
// TODO Win1252/UTF8 functions are deprecated and will be removed sometime
//if the ICS backend is loaded in CombinedBackend and Zarafa > 7
//STORE_SUPPORTS_UNICODE is true and the convertion will not be done
//for other backends.
function utf8_to_windows1252($string, $option = "", $force_convert = false) {
//if the store supports unicode return the string without converting it
if (!$force_convert && defined('STORE_SUPPORTS_UNICODE') && STORE_SUPPORTS_UNICODE == true) return $string;
if (function_exists("iconv")){
return @iconv("UTF-8", "Windows-1252" . $option, $string);
}else{
return utf8_decode($string); // no euro support here
}
}
function windows1252_to_utf8($string, $option = "", $force_convert = false) {
//if the store supports unicode return the string without converting it
if (!$force_convert && defined('STORE_SUPPORTS_UNICODE') && STORE_SUPPORTS_UNICODE == true) return $string;
if (function_exists("iconv")){
return @iconv("Windows-1252", "UTF-8" . $option, $string);
}else{
return utf8_encode($string); // no euro support here
}
}
function w2u($string) { return windows1252_to_utf8($string); }
function u2w($string) { return utf8_to_windows1252($string); }
function w2ui($string) { return windows1252_to_utf8($string, "//TRANSLIT"); }
function u2wi($string) { return utf8_to_windows1252($string, "//TRANSLIT"); }
/**
* @param string $message
* @deprecated
*/
function debugLog($message) {
ZLog::Write(LOGLEVEL_DEBUG, $message);
}
// TODO review error handler
function zarafa_error_handler($errno, $errstr, $errfile, $errline, $errcontext) {
$bt = debug_backtrace();
switch ($errno) {
case 8192: // E_DEPRECATED since PHP 5.3.0
// do not handle this message
break;
case E_NOTICE:
case E_WARNING:
// TODO check if there is a better way to avoid these messages
if (stripos($errfile,'interprocessdata') !== false && stripos($errstr,'shm_get_var()') !== false)
break;
ZLog::Write(LOGLEVEL_WARN, "$errfile:$errline $errstr ($errno)");
break;
default:
ZLog::Write(LOGLEVEL_ERROR, "trace error: $errfile:$errline $errstr ($errno) - backtrace: ". (count($bt)-1) . " steps");
for($i = 1, $bt_length = count($bt); $i < $bt_length; $i++) {
$file = $line = "unknown";
if (isset($bt[$i]['file'])) $file = $bt[$i]['file'];
if (isset($bt[$i]['line'])) $line = $bt[$i]['line'];
ZLog::Write(LOGLEVEL_ERROR, "trace: $i:". $file . ":" . $line. " - " . ((isset($bt[$i]['class']))? $bt[$i]['class'] . $bt[$i]['type']:""). $bt[$i]['function']. "()");
}
//throw new Exception("An error occured.");
break;
}
}
error_reporting(E_ALL);
set_error_handler("zarafa_error_handler");

View file

@ -56,13 +56,11 @@ class ZPush {
const CLASS_OTHERTYPES = 4;
// AS versions
const ASV_1 = "1.0";
const ASV_2 = "2.0";
const ASV_21 = "2.1";
const ASV_25 = "2.5";
const ASV_12 = "12.0";
const ASV_121 = "12.1";
const ASV_14 = "14.0";
const ASV_141 = "14.1";
/**
* Command codes for base64 encoded requests (AS >= 12.1)
@ -87,12 +85,8 @@ class ZPush {
const COMMAND_RESOLVERECIPIENTS = 21;
const COMMAND_VALIDATECERT = 22;
// Deprecated commands
// Deprecated commands (AS >= 14)
const COMMAND_GETHIERARCHY = -1;
const COMMAND_CREATECOLLECTION = -2;
const COMMAND_DELETECOLLECTION = -3;
const COMMAND_MOVECOLLECTION = -4;
const COMMAND_NOTIFY = -5;
// Webservice commands
const COMMAND_WEBSERVICE_DEVICE = -100;
@ -109,48 +103,68 @@ class ZPush {
"BackendMaildir"
);
// Versions 1.0, 2.0, 2.1 and 2.5 are deprecated (ZP-604)
static private $supportedASVersions = array(
self::ASV_1,
self::ASV_2,
self::ASV_21,
self::ASV_25,
self::ASV_12,
self::ASV_121,
self::ASV_14
self::ASV_14,
self::ASV_141
);
static private $supportedCommands = array(
// COMMAND // AS VERSION // REQUESTHANDLER // OTHER SETTINGS
self::COMMAND_SYNC => array(self::ASV_1, self::REQUESTHANDLER => "Sync"),
self::COMMAND_SENDMAIL => array(self::ASV_1, self::REQUESTHANDLER => "SendMail"),
self::COMMAND_SMARTFORWARD => array(self::ASV_1, self::REQUESTHANDLER => "SendMail"),
self::COMMAND_SMARTREPLY => array(self::ASV_1, self::REQUESTHANDLER => "SendMail"),
self::COMMAND_GETATTACHMENT => array(self::ASV_1, self::REQUESTHANDLER => "GetAttachment"),
self::COMMAND_GETHIERARCHY => array(self::ASV_1, self::REQUESTHANDLER => "GetHierarchy", self::HIERARCHYCOMMAND), // deprecated but implemented
self::COMMAND_CREATECOLLECTION => array(self::ASV_1), // deprecated & not implemented
self::COMMAND_DELETECOLLECTION => array(self::ASV_1), // deprecated & not implemented
self::COMMAND_MOVECOLLECTION => array(self::ASV_1), // deprecated & not implemented
self::COMMAND_FOLDERSYNC => array(self::ASV_2, self::REQUESTHANDLER => "FolderSync", self::HIERARCHYCOMMAND),
self::COMMAND_FOLDERCREATE => array(self::ASV_2, self::REQUESTHANDLER => "FolderChange", self::HIERARCHYCOMMAND),
self::COMMAND_FOLDERDELETE => array(self::ASV_2, self::REQUESTHANDLER => "FolderChange", self::HIERARCHYCOMMAND),
self::COMMAND_FOLDERUPDATE => array(self::ASV_2, self::REQUESTHANDLER => "FolderChange", self::HIERARCHYCOMMAND),
self::COMMAND_MOVEITEMS => array(self::ASV_1, self::REQUESTHANDLER => "MoveItems"),
self::COMMAND_GETITEMESTIMATE => array(self::ASV_1, self::REQUESTHANDLER => "GetItemEstimate"),
self::COMMAND_MEETINGRESPONSE => array(self::ASV_1, self::REQUESTHANDLER => "MeetingResponse"),
self::COMMAND_RESOLVERECIPIENTS => array(self::ASV_1, self::REQUESTHANDLER => "ResolveRecipients"),
self::COMMAND_VALIDATECERT => array(self::ASV_1, self::REQUESTHANDLER => "ValidateCert"),
self::COMMAND_PROVISION => array(self::ASV_25, self::REQUESTHANDLER => "Provisioning", self::UNAUTHENTICATED, self::UNPROVISIONED),
self::COMMAND_SEARCH => array(self::ASV_1, self::REQUESTHANDLER => "Search"),
self::COMMAND_PING => array(self::ASV_2, self::REQUESTHANDLER => "Ping", self::UNPROVISIONED),
self::COMMAND_NOTIFY => array(self::ASV_1, self::REQUESTHANDLER => "Notify"), // deprecated & not implemented
self::COMMAND_ITEMOPERATIONS => array(self::ASV_12, self::REQUESTHANDLER => "ItemOperations"),
self::COMMAND_SETTINGS => array(self::ASV_12, self::REQUESTHANDLER => "Settings"),
// COMMAND // AS VERSION // OTHER SETTINGS
self::COMMAND_SYNC => array(self::ASV_25),
self::COMMAND_SENDMAIL => array(self::ASV_25),
self::COMMAND_SMARTFORWARD => array(self::ASV_25),
self::COMMAND_SMARTREPLY => array(self::ASV_25),
self::COMMAND_GETATTACHMENT => array(self::ASV_25),
self::COMMAND_GETHIERARCHY => array(self::ASV_25, self::HIERARCHYCOMMAND), // deprecated (AS >= 14)
self::COMMAND_FOLDERSYNC => array(self::ASV_25, self::HIERARCHYCOMMAND),
self::COMMAND_FOLDERCREATE => array(self::ASV_25, self::HIERARCHYCOMMAND),
self::COMMAND_FOLDERDELETE => array(self::ASV_25, self::HIERARCHYCOMMAND),
self::COMMAND_FOLDERUPDATE => array(self::ASV_25, self::HIERARCHYCOMMAND),
self::COMMAND_MOVEITEMS => array(self::ASV_25),
self::COMMAND_GETITEMESTIMATE => array(self::ASV_25),
self::COMMAND_MEETINGRESPONSE => array(self::ASV_25),
self::COMMAND_RESOLVERECIPIENTS => array(self::ASV_25),
self::COMMAND_VALIDATECERT => array(self::ASV_25),
self::COMMAND_PROVISION => array(self::ASV_25, self::UNAUTHENTICATED, self::UNPROVISIONED),
self::COMMAND_SEARCH => array(self::ASV_25),
self::COMMAND_PING => array(self::ASV_25, self::UNPROVISIONED),
self::COMMAND_ITEMOPERATIONS => array(self::ASV_12),
self::COMMAND_SETTINGS => array(self::ASV_12),
self::COMMAND_WEBSERVICE_DEVICE => array(self::REQUESTHANDLER => "Webservice", self::PLAININPUT, self::NOACTIVESYNCCOMMAND, self::WEBSERVICECOMMAND),
self::COMMAND_WEBSERVICE_USERS => array(self::REQUESTHANDLER => "Webservice", self::PLAININPUT, self::NOACTIVESYNCCOMMAND, self::WEBSERVICECOMMAND),
self::COMMAND_WEBSERVICE_DEVICE => array(self::PLAININPUT, self::NOACTIVESYNCCOMMAND, self::WEBSERVICECOMMAND),
self::COMMAND_WEBSERVICE_USERS => array(self::PLAININPUT, self::NOACTIVESYNCCOMMAND, self::WEBSERVICECOMMAND),
);
static private $requestHandler = array(
// COMMAND // REQUESTHANDLER
self::COMMAND_SYNC => "Sync",
self::COMMAND_SENDMAIL => "SendMail",
self::COMMAND_SMARTFORWARD => "SendMail",
self::COMMAND_SMARTREPLY => "SendMail",
self::COMMAND_GETATTACHMENT => "GetAttachment",
self::COMMAND_GETHIERARCHY => "GetHierarchy", // deprecated (AS >= 14)
self::COMMAND_FOLDERSYNC => "FolderSync",
self::COMMAND_FOLDERCREATE => "FolderChange",
self::COMMAND_FOLDERDELETE => "FolderChange",
self::COMMAND_FOLDERUPDATE => "FolderChange",
self::COMMAND_MOVEITEMS => "MoveItems",
self::COMMAND_GETITEMESTIMATE => "GetItemEstimate",
self::COMMAND_MEETINGRESPONSE => "MeetingResponse",
self::COMMAND_RESOLVERECIPIENTS => "ResolveRecipients",
self::COMMAND_VALIDATECERT => "ValidateCert",
self::COMMAND_PROVISION => "Provisioning",
self::COMMAND_SEARCH => "Search",
self::COMMAND_PING => "Ping",
self::COMMAND_ITEMOPERATIONS => "ItemOperations",
self::COMMAND_SETTINGS => "Settings",
self::COMMAND_WEBSERVICE_DEVICE => "Webservice",
self::COMMAND_WEBSERVICE_USERS => "Webservice",
);
static private $classes = array(
"Email" => array(
@ -205,8 +219,8 @@ class ZPush {
*/
static public function CheckConfig() {
// check the php version
if (version_compare(phpversion(),'5.1.0') < 0)
throw new FatalException("The configured PHP version is too old. Please make sure at least PHP 5.1 is used.");
if (version_compare(phpversion(), '5.3.0') < 0)
throw new FatalException("The configured PHP version is too old. Please make sure at least PHP 5.3 is used.");
// some basic checks
if (!defined('BASE_PATH'))
@ -354,18 +368,16 @@ class ZPush {
else {
// Initialize the default StateMachine
if (defined('STATE_MACHINE') && STATE_MACHINE == 'SQL') {
include_once('lib/default/sqlstatemachine.php');
ZPush::$stateMachine = new SqlStateMachine();
}
else {
include_once('lib/default/filestatemachine.php');
ZPush::$stateMachine = new FileStateMachine();
}
}
if (ZPush::$stateMachine->GetStateVersion() !== ZPush::GetLatestStateVersion()) {
if (class_exists("TopCollector")) self::GetTopCollector()->AnnounceInformation("Run migration script!", true);
throw new HTTPReturnCodeException(sprintf("The state version available to the %s is not the latest version - please run the state upgrade script. See release notes for more information.", get_class(ZPush::$stateMachine), 503));
throw new HTTPReturnCodeException(sprintf("The state version available to the %s is not the latest version - please run the state upgrade script. See release notes for more information.", get_class(ZPush::$stateMachine)), HTTP_CODE_500);
}
}
return ZPush::$stateMachine;
@ -403,10 +415,33 @@ class ZPush {
* @return object TopCollector
*/
static public function GetTopCollector() {
if (!isset(ZPush::$topCollector))
ZPush::$topCollector = new TopCollector();
if (!isset(self::$topCollector)) {
$class = defined('TOP_COLLECTOR_BACKEND') ? TOP_COLLECTOR_BACKEND : 'TopCollector';
self::$topCollector = new $class();
}
return self::$topCollector;
}
return ZPush::$topCollector;
/**
* Returns an instance of PingTracking
*
* @access public
* @return object IPingTracking
*/
static public function GetPingTracking() {
$class = defined('PING_TRACKING_BACKEND') ? PING_TRACKING_BACKEND : 'PingTracking';
return new $class();
}
/**
* Returns an instance of LoopDetection
*
* @access public
* @return object ILoopDetection
*/
static public function GetLoopDetection() {
$class = defined('LOOP_DETECTION_BACKEND') ? LOOP_DETECTION_BACKEND : 'LoopDetection';
return new $class();
}
/**
@ -686,9 +721,7 @@ END;
* @return string
*/
static public function GetSupportedProtocolVersions($valueOnly = false) {
//$versions = implode(',', array_slice(self::$supportedASVersions, 0, (array_search(self::GetSupportedASVersion(), self::$supportedASVersions)+1)));
// Removing support for AS 1.0, 2.0, 2.1 - That will make Outlook 2013 works
$versions = implode(',', array_slice(self::$supportedASVersions, 3, (array_search(self::GetSupportedASVersion(), self::$supportedASVersions)+1)));
$versions = implode(',', array_slice(self::$supportedASVersions, 0, (array_search(self::GetSupportedASVersion(), self::$supportedASVersions)+1)));
ZLog::Write(LOGLEVEL_DEBUG, "ZPush::GetSupportedProtocolVersions(): " . $versions);
if ($valueOnly === true)
@ -725,18 +758,10 @@ END;
* @return RequestProcessor sub-class
*/
static public function GetRequestHandlerForCommand($commandCode) {
if (!array_key_exists($commandCode, self::$supportedCommands) ||
!array_key_exists(self::REQUESTHANDLER, self::$supportedCommands[$commandCode]) )
if (!array_key_exists($commandCode, self::$requestHandler))
throw new FatalNotImplementedException(sprintf("Command '%s' has no request handler or class", Utils::GetCommandFromCode($commandCode)));
$class = self::$supportedCommands[$commandCode][self::REQUESTHANDLER];
if ($class == "Webservice")
$handlerclass = REAL_BASE_PATH . "lib/webservice/webservice.php";
else
$handlerclass = REAL_BASE_PATH . "lib/request/" . strtolower($class) . ".php";
if (is_file($handlerclass))
include($handlerclass);
$class = self::$requestHandler[$commandCode];
if (class_exists($class))
return new $class();
@ -830,5 +855,22 @@ END;
return $defcapa;
}
/**
* End Response
*
* @access public
*/
public static function FinishResponse() {
$len = ob_get_length();
if ($len !== false) {
if (!headers_sent()) {
header(sprintf("Content-Length: %s", $len));
if ($len == 0)
header("Content-Type:");
}
ZLog::Write(LOGLEVEL_DEBUG, sprintf("Flushing %d, headers already sent? %s", $len , headers_sent() ? "yes" : "no"));
if (!ob_end_flush())
ZLog::Write(LOGLEVEL_ERROR, "Unable to flush buffer!?");
}
}
}
?>

File diff suppressed because it is too large Load diff

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