1
0
Fork 0
mirror of https://github.com/YunoHost-Apps/z-push_ynh.git synced 2024-09-03 18:05:58 +02:00
z-push_ynh/sources/backend/zarafa/mapi/mapi.util.php

338 lines
14 KiB
PHP

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