⚝
One Hat Cyber Team
⚝
Your IP:
216.73.216.50
Server IP:
41.128.143.86
Server:
Linux host.raqmix.cloud 6.8.0-1025-azure #30~22.04.1-Ubuntu SMP Wed Mar 12 15:28:20 UTC 2025 x86_64
Server Software:
Apache
PHP Version:
8.3.23
Buat File
|
Buat Folder
Eksekusi
Dir :
~
/
usr
/
share
/
psa-horde
/
turba
/
lib
/
Edit File: Driver.php
<?php /** * Copyright 2000-2017 Horde LLC (http://www.horde.org/) * * See the enclosed file LICENSE for license information (ASL). If you did * did not receive this file, see http://www.horde.org/licenses/apache. * * @category Horde * @copyright 2000-2017 Horde LLC * @license http://www.horde.org/licenses/apache ASL * @package Turba */ /** * Provides a common abstracted interface to the various directory search * drivers. It includes functions for searching, adding, removing, and * modifying directory entries. * * @author Chuck Hagenbuch <chuck@horde.org> * @author Jon Parise <jon@csh.rit.edu> * @category Horde * @copyright 2000-2017 Horde LLC * @license http://www.horde.org/licenses/apache ASL * @package Turba */ class Turba_Driver implements Countable { /** * The symbolic title of this source. * * @var string */ public $title; /** * Hash describing the mapping between Turba attributes and * driver-specific fields. * * @var array */ public $map = array(); /** * Hash with all tabs and their fields. * * @var array */ public $tabs = array(); /** * List of all fields that can be accessed in the backend (excludes * composite attributes, etc.). * * @var array */ public $fields = array(); /** * Array of fields that must match exactly. * * @var array */ public $strict = array(); /** * Array of fields to search "approximately" (@see * config/backends.php). * * @var array */ public $approximate = array(); /** * The name of a field to store contact list names in if not the default. * * @var string */ public $listNameField = null; /** * The name of a field to use as an alternative to the name field if that * one is empty. * * @var string */ public $alternativeName = null; /** * The internal name of this source. * * @var string */ protected $_name; /** * Hash holding the driver's additional parameters. * * @var array */ protected $_params = array(); /** * What can this backend do? * * @var array */ protected $_capabilities = array(); /** * Any additional options passed to Turba_Object constructors. * * @var array */ protected $_objectOptions = array(); /** * Number of contacts in this source. * * @var integer */ protected $_count = null; /** * Hold the value for the owner of this address book. * * @var string */ protected $_contact_owner = ''; /** * Mapping of Turba attributes to ActiveSync fields. * * @var array */ static protected $_asMap = array( 'name' => 'fileas', 'lastname' => 'lastname', 'firstname' => 'firstname', 'middlenames' => 'middlename', 'alias' => 'nickname', 'nickname' => 'nickname', 'namePrefix' => 'title', 'nameSuffix' => 'suffix', 'homeStreet' => 'homestreet', 'homeCity' => 'homecity', 'homeProvince' => 'homestate', 'homePostalCode' => 'homepostalcode', 'homeCountryFree' => 'homecountry', 'otherStreet' => 'otherstreet', 'otherCity' => 'othercity', 'otherProvince' => 'otherstate', 'otherPostalCode' => 'otherpostalcode', 'otherCountryFree' => 'othercountry', 'workStreet' => 'businessstreet', 'workCity' => 'businesscity', 'workProvince' => 'businessstate', 'workPostalCode' => 'businesspostalcode', 'workCountryFree' => 'businesscountry', 'title' => 'jobtitle', 'company' => 'companyname', 'department' => 'department', 'office' => 'officelocation', 'spouse' => 'spouse', 'website' => 'webpage', 'assistant' => 'assistantname', 'manager' => 'managername', 'yomifirstname' => 'yomifirstname', 'yomilastname' => 'yomilastname', 'imaddress' => 'imaddress', 'imaddress2' => 'imaddress2', 'imaddress3' => 'imaddress3', 'homePhone' => 'homephonenumber', 'homePhone2' => 'home2phonenumber', 'workPhone' => 'businessphonenumber', 'workPhone2' => 'business2phonenumber', 'fax' => 'businessfaxnumber', 'homeFax' => 'homefaxnumber', 'pager' => 'pagernumber', 'cellPhone' => 'mobilephonenumber', 'carPhone' => 'carphonenumber', 'assistPhone' => 'assistnamephonenumber', 'companyPhone' => 'companymainphone', 'radioPhone' => 'radiophonenumber' ); /** * Constructs a new Turba_Driver object. * * @param string $name Source name * @param array $params Hash containing additional configuration * parameters. */ public function __construct($name = '', array $params = array()) { $this->_name = $name; $this->_params = $params; } /** * Returns the current driver's additional parameters. * * @return array Hash containing the driver's additional parameters. */ public function getParams() { return $this->_params; } /** * Checks if this backend has a certain capability. * * @param string $capability The capability to check for. * * @return boolean Supported or not. */ public function hasCapability($capability) { return !empty($this->_capabilities[$capability]); } /** * Returns the attributes that are blob types. * * @return array List of blob attributes in the array keys. */ public function getBlobs() { global $attributes; $blobs = array(); foreach (array_keys($this->fields) as $attribute) { if (isset($attributes[$attribute]) && $attributes[$attribute]['type'] == 'image') { $blobs[$attribute] = true; } } return $blobs; } /** * Returns the attributes that represent dates. * * @return array List of date attributes in the array keys. * @since 4.2.0 */ public function getDateFields() { global $attributes; $dates = array(); foreach (array_keys($this->fields) as $attribute) { if (isset($attributes[$attribute]) && $attributes[$attribute]['type'] == 'monthdayyear') { $dates[$attribute] = '0000-00-00'; } } return $dates; } /** * Translates the keys of the first hash from the generalized Turba * attributes to the driver-specific fields. The translation is based on * the contents of $this->map. * * @param array $hash Hash using Turba keys. * * @return array Translated version of $hash. */ public function toDriverKeys(array $hash) { if (!empty($hash['name']) && !empty($this->listNameField) && !empty($hash['__type']) && is_array($this->map['name']) && ($hash['__type'] == 'Group')) { $hash[$this->listNameField] = $hash['name']; unset($hash['name']); } // Add composite fields to $hash if at least one field part exists // and the composite field will be saved to storage. // Otherwise composite fields won't be computed during an import. foreach ($this->map as $key => $val) { if (!is_array($val) || empty($this->map[$key]['attribute']) || array_key_exists($key, $hash)) { continue; } foreach ($this->map[$key]['fields'] as $mapfields) { if (isset($hash[$mapfields])) { // Add composite field $hash[$key] = null; break; } } } $fields = array(); foreach ($hash as $key => $val) { if (!isset($this->map[$key])) { continue; } if (!is_array($this->map[$key])) { $fields[$this->map[$key]] = $val; } elseif (!empty($this->map[$key]['attribute'])) { $fieldarray = array(); foreach ($this->map[$key]['fields'] as $mapfields) { $fieldarray[] = isset($hash[$mapfields]) ? $hash[$mapfields] : ''; } $fields[$this->map[$key]['attribute']] = Turba::formatCompositeField($this->map[$key]['format'], $fieldarray); } else { // If 'parse' is not specified, use 'format' and 'fields'. if (!isset($this->map[$key]['parse'])) { $this->map[$key]['parse'] = array( array( 'format' => $this->map[$key]['format'], 'fields' => $this->map[$key]['fields'] ) ); } foreach ($this->map[$key]['parse'] as $parse) { $splitval = sscanf($val, $parse['format']); $count = 0; $tmp_fields = array(); foreach ($parse['fields'] as $mapfield) { if (isset($hash[$mapfield])) { // If the compositing fields are set // individually, then don't set them at all. break 2; } $tmp_fields[$this->map[$mapfield]] = $splitval[$count++]; } // Exit if we found the best match. if ($splitval[$count - 1] !== null) { break; } } $fields = array_merge($fields, $tmp_fields); } } return $fields; } /** * Takes a hash of Turba key => search value and return a (possibly * nested) array, using backend attribute names, that can be turned into a * search by the driver. The translation is based on the contents of * $this->map, and includes nested OR searches for composite fields. * * @param array $criteria Hash of criteria using Turba keys. * @param string $search_type OR search or AND search? * @param array $strict Fields that must be matched exactly. * @param boolean $match_begin Whether to match only at beginning of * words. * @param array $custom_strict Custom set of fields that are to matched * exactly, but are glued using $search_type * and 'AND' together with $strict fields. * Allows an 'OR' search pm a custom set of * $strict fields. * * @return array An array of search criteria. */ public function makeSearch($criteria, $search_type, array $strict, $match_begin = false, array $custom_strict = array()) { $search = $search_terms = $subsearch = $strict_search = array(); $glue = $temp = ''; $lastChar = '\"'; $blobs = $this->getBlobs(); foreach ($criteria as $key => $val) { if (!isset($this->map[$key])) { continue; } if (is_array($this->map[$key])) { /* Composite field, break out the search terms. */ $parts = explode(' ', $val); if (count($parts) > 1) { /* Only parse if there was more than 1 search term and * 'AND' the cumulative subsearches. */ for ($i = 0; $i < count($parts); ++$i) { $term = $parts[$i]; $firstChar = substr($term, 0, 1); if ($firstChar == '"') { $temp = substr($term, 1, strlen($term) - 1); $done = false; while (!$done && $i < count($parts) - 1) { $lastChar = substr($parts[$i + 1], -1); if ($lastChar == '"') { $temp .= ' ' . substr($parts[$i + 1], 0, -1); $done = true; } else { $temp .= ' ' . $parts[$i + 1]; } ++$i; } $search_terms[] = $temp; } else { $search_terms[] = $term; } } $glue = 'AND'; } else { /* If only one search term, use original input and 'OR' the searces since we're only looking for 1 term in any of the composite fields. */ $search_terms[0] = $val; $glue = 'OR'; } foreach ($this->map[$key]['fields'] as $field) { if (!empty($blobs[$field])) { continue; } $field = $this->toDriver($field); if (!empty($strict[$field])) { /* For strict matches, use the original search * vals. */ $strict_search[] = array( 'field' => $field, 'op' => '=', 'test' => $val, ); } elseif (!empty($custom_strict[$field])) { $search[] = array( 'field' => $field, 'op' => '=', 'test' => $val, ); } else { /* Create a subsearch for each individual search * term. */ if (count($search_terms) > 1) { /* Build the 'OR' search for each search term * on this field. */ $atomsearch = array(); for ($i = 0; $i < count($search_terms); ++$i) { $atomsearch[] = array( 'field' => $field, 'op' => 'LIKE', 'test' => $search_terms[$i], 'begin' => $match_begin, 'approximate' => !empty($this->approximate[$field]), ); } $atomsearch[] = array( 'field' => $field, 'op' => '=', 'test' => '', 'begin' => $match_begin, 'approximate' => !empty($this->approximate[$field]) ); $subsearch[] = array('OR' => $atomsearch); unset($atomsearch); $glue = 'AND'; } else { /* $parts may have more than one element, but * if they are all quoted we will only have 1 * $subsearch. */ $subsearch[] = array( 'field' => $field, 'op' => 'LIKE', 'test' => $search_terms[0], 'begin' => $match_begin, 'approximate' => !empty($this->approximate[$field]), ); $glue = 'OR'; } } } if (count($subsearch)) { $search[] = array($glue => $subsearch); } } else { /* Not a composite field. */ if (!empty($blobs[$key])) { continue; } if (!empty($strict[$this->map[$key]])) { $strict_search[] = array( 'field' => $this->map[$key], 'op' => '=', 'test' => $val, ); } elseif (!empty($custom_strict[$this->map[$key]])) { $search[] = array( 'field' => $this->map[$key], 'op' => '=', 'test' => $val, ); } else { $search[] = array( 'field' => $this->map[$key], 'op' => 'LIKE', 'test' => $val, 'begin' => $match_begin, 'approximate' => !empty($this->approximate[$this->map[$key]]), ); } } } if (count($strict_search) && count($search)) { return array( 'AND' => array( $search_type => $strict_search, array( $search_type => $search ) ) ); } elseif (count($strict_search)) { return array( $search_type => $strict_search ); } elseif (count($search)) { return array( $search_type => $search ); } return array(); } /** * Translates a single Turba attribute to the driver-specific * counterpart. The translation is based on the contents of * $this->map. This ignores composite fields. * * @param string $attribute The Turba attribute to translate. * * @return string The driver name for this attribute. */ public function toDriver($attribute) { if (!isset($this->map[$attribute])) { return null; } return is_array($this->map[$attribute]) ? $this->map[$attribute]['fields'] : $this->map[$attribute]; } /** * Translates a hash from being keyed on driver-specific fields to being * keyed on the generalized Turba attributes. The translation is based on * the contents of $this->map. * * @param array $entry A hash using driver-specific keys. * * @return array Translated version of $entry. */ public function toTurbaKeys(array $entry) { $new_entry = array(); foreach ($this->map as $key => $val) { if (!is_array($val)) { $new_entry[$key] = (isset($entry[$val]) && (!empty($entry[$val]) || (is_string($entry[$val]) && strlen($entry[$val])))) ? trim($entry[$val]) : null; } } return $new_entry; } /** * Searches the source based on the provided criteria. * * @todo Allow $criteria to contain the comparison operator (<, =, >, * 'like') and modify the drivers accordingly. * * @param array $search_criteria Hash containing the search criteria. * @param string $sort_order The requested sort order which is passed * to Turba_List::sort(). * @param string $search_type Do an AND or an OR search (defaults to * AND). * @param array $return_fields A list of fields to return; defaults to * all fields. * @param array $custom_strict A list of fields that must match exactly. * @param boolean $match_begin Whether to match only at beginning of * words. * @param boolean $count_only Only return the count of matching entries, * not the entries themselves. * * @return mixed Turba_List|integer The sorted, filtered list of search * results or the number of matching * entries (if $count_only is true). * @throws Turba_Exception */ public function search(array $search_criteria, $sort_order = null, $search_type = 'AND', array $return_fields = array(), array $custom_strict = array(), $match_begin = false, $count_only = false) { /* Add any fields that must match exactly for this source to the * $strict_fields array. */ $strict_fields = $custom_strict_fields = array(); foreach ($this->strict as $strict_field) { $strict_fields[$strict_field] = true; } /* Differentiate between provided $custom_strict fields - which honor * the $search_type and $strict fields which are not * explicitly requested as part of this search, and as such, are not * constrained by the requested $search_type. */ foreach ($custom_strict as $strict_field) { if (isset($this->map[$strict_field])) { $custom_strict_fields[$this->map[$strict_field]] = true; } } /* Translate the Turba attributes to driver-specific attributes. */ $fields = $this->makeSearch($search_criteria, $search_type, $strict_fields, $match_begin, $custom_strict_fields); /* If we are not using Horde_Share, enforce the requirement that the * current user must be the owner of the addressbook. */ if (isset($this->map['__owner'])) { $fields = array( 'AND' => array( $fields, array( 'field' => $this->toDriver('__owner'), 'op' => '=', 'test' => $this->getContactOwner() ) ) ); } if (in_array('email', $return_fields) && !in_array('emails', $return_fields)) { $return_fields[] = 'emails'; } if (count($return_fields)) { $default_fields = array('__key', '__type', '__owner', '__members', 'name'); if ($this->alternativeName) { $default_fields[] = $this->alternativeName; } $return_fields_pre = array_unique(array_merge($default_fields, $return_fields)); $return_fields = array(); foreach ($return_fields_pre as $field) { $result = $this->toDriver($field); if (is_array($result)) { foreach ($result as $composite_field) { $composite_result = $this->toDriver($composite_field); if ($composite_result) { $return_fields[] = $composite_result; } } } elseif ($result) { $return_fields[] = $result; } } } else { /* Need to force the array to be re-keyed for the (fringe) case * where we might have 1 DB field mapped to 2 or more Turba * fields */ $return_fields = array_values( array_unique(array_values($this->fields))); } /* Retrieve the search results from the driver. */ $objects = $this->_search($fields, $return_fields, $this->toDriverKeys($this->getBlobs()), $count_only); if ($count_only) { return $objects; } return $this->_toTurbaObjects($objects, $sort_order); } /** * Searches the current address book for duplicate entries. * * Duplicates are determined by comparing email and name or last name and * first name values. * * @return array A hash with the following format: * <code> * array('name' => array('John Doe' => Turba_List, ...), ...) * </code> * @throws Turba_Exception */ public function searchDuplicates() { return array(); } /** * Takes an array of object hashes and returns a Turba_List * containing the correct Turba_Objects * * @param array $objects An array of object hashes (keyed to backend). * @param array $sort_order Array of hashes describing sort fields. Each * hash has the following fields: * <pre> * ascending - (boolean) Indicating sort direction. * field - (string) Sort field. * </pre> * * @return Turba_List A list object. */ protected function _toTurbaObjects(array $objects, array $sort_order = null) { $list = new Turba_List(); foreach ($objects as $object) { /* Translate the driver-specific fields in the result back to the * more generalized common Turba attributes using the map. */ $object = $this->toTurbaKeys($object); $done = false; if (!empty($object['__type']) && ucwords($object['__type']) != 'Object') { $class = 'Turba_Object_' . ucwords($object['__type']); if (class_exists($class)) { $list->insert(new $class($this, $object, $this->_objectOptions)); $done = true; } } if (!$done) { $list->insert(new Turba_Object($this, $object, $this->_objectOptions)); } } $list->sort($sort_order); /* Return the filtered (sorted) results. */ return $list; } /** * Returns a list of birthday or anniversary hashes from this source for a * certain period. * * @param Horde_Date $start The start date of the valid period. * @param Horde_Date $end The end date of the valid period. * @param string $category The timeObjects category to return. * * @return array A list of timeObject hashes. * @throws Turba Exception */ public function listTimeObjects(Horde_Date $start, Horde_Date $end, $category) { try { $res = $this->getTimeObjectTurbaList($start, $end, $category); } catch (Turba_Exception $e) { /* Try the default implementation before returning an error */ $res = $this->_getTimeObjectTurbaListFallback($start, $end, $category); } $t_objects = array(); while ($ob = $res->next()) { $t_object = $ob->getValue($category); if (empty($t_object)) { continue; } try { $t_object = new Horde_Date($t_object); } catch (Horde_Date_Exception $e) { continue; } if ($t_object->compareDate($end) > 0) { continue; } $t_object_end = new Horde_Date($t_object); ++$t_object_end->mday; $key = $ob->getValue('__key'); // Calculate the age of the time object if ($start->year == $end->year || $end->year == 9999) { $age = $start->year - $t_object->year; } elseif ($t_object->month <= $end->month) { // t_object must be in later year $age = $end->year - $t_object->year; } else { // t_object must be in earlier year $age = $start->year - $t_object->year; } $title = sprintf(_("%d. %s of %s"), $age, $GLOBALS['attributes'][$category]['label'], $ob->getValue('name')); $t_objects[] = array( 'id' => $key, 'title' => $title, 'start' => sprintf('%d-%02d-%02dT00:00:00', $t_object->year, $t_object->month, $t_object->mday), 'end' => sprintf('%d-%02d-%02dT00:00:00', $t_object_end->year, $t_object_end->month, $t_object_end->mday), 'recurrence' => array('type' => Horde_Date_Recurrence::RECUR_YEARLY_DATE, 'interval' => 1), 'params' => array('source' => $this->_name, 'key' => $key), 'link' => Horde::url('contact.php', true)->add(array('source' => $this->_name, 'key' => $key))->setRaw(true) ); } return $t_objects; } /** * Default implementation for obtaining a Turba_List to get TimeObjects * out of. * * @param Horde_Date $start The starting date. * @param Horde_Date $end The ending date. * @param string $field The address book field containing the * timeObject information (birthday, * anniversary). * * @return Turba_List A list of objects. * @throws Turba_Exception */ public function getTimeObjectTurbaList(Horde_Date $start, Horde_Date $end, $field) { return $this->_getTimeObjectTurbaListFallback($start, $end, $field); } /** * Default implementation for obtaining a Turba_List to get TimeObjects * out of. * * @param Horde_Date $start The starting date. * @param Horde_Date $end The ending date. * @param string $field The address book field containing the * timeObject information (birthday, * anniversary). * * @return Turba_List A list of objects. * @throws Turba_Exception */ protected function _getTimeObjectTurbaListFallback(Horde_Date $start, Horde_Date $end, $field) { return $this->search(array(), null, 'AND', array('name', $field)); } /** * Retrieves a set of objects from the source. * * @param array $objectIds The unique ids of the objects to retrieve. * * @return array The array of retrieved objects (Turba_Objects). * @throws Turba_Exception * @throws Horde_Exception_NotFound */ public function getObjects(array $objectIds) { $objects = $this->_read($this->map['__key'], $objectIds, $this->getContactOwner(), array_values($this->fields), $this->toDriverKeys($this->getBlobs()), $this->toDriverKeys($this->getDateFields())); if (!is_array($objects)) { throw new Horde_Exception_NotFound(); } $results = array(); foreach ($objects as $object) { $object = $this->toTurbaKeys($object); $done = false; if (!empty($object['__type']) && ucwords($object['__type']) != 'Object') { $class = 'Turba_Object_' . ucwords($object['__type']); if (class_exists($class)) { $results[] = new $class($this, $object, $this->_objectOptions); $done = true; } } if (!$done) { $results[] = new Turba_Object($this, $object, $this->_objectOptions); } } return $results; } /** * Retrieves one object from the source. * * @param string $objectId The unique id of the object to retrieve. * * @return Turba_Object The retrieved object. * @throws Turba_Exception * @throws Horde_Exception_NotFound */ public function getObject($objectId) { $result = $this->getObjects(array($objectId)); if (empty($result[0])) { throw new Horde_Exception_NotFound(); } $result = $result[0]; if (!isset($this->map['__owner'])) { $result->attributes['__owner'] = $this->getContactOwner(); } return $result; } /** * Adds a new entry to the contact source. * * @param array $attributes The attributes of the new object to add. * * @return string The new __key value on success. * @throws Turba_Exception */ public function add(array $attributes) { /* Only set __type and __owner if they are not already set. */ if (!isset($attributes['__type'])) { $attributes['__type'] = 'Object'; } if (isset($this->map['__owner']) && !isset($attributes['__owner'])) { $attributes['__owner'] = $this->getContactOwner(); } if (!isset($attributes['__uid'])) { $attributes['__uid'] = $this->_makeUid(); } $key = $attributes['__key'] = $this->_makeKey($this->toDriverKeys($attributes)); $uid = $attributes['__uid']; /* Remember any tags, since toDriverKeys will remove them. (They are not stored in the Turba backend so have no mapping). */ $tags = isset($attributes['__tags']) ? $attributes['__tags'] : false; $attributes = $this->toDriverKeys($attributes); $this->_add($attributes, $this->toDriverKeys($this->getBlobs()), $this->toDriverKeys($this->getDateFields())); /* Add tags. */ if ($tags !== false) { $GLOBALS['injector']->getInstance('Turba_Tagger')->tag( $uid, $tags, $GLOBALS['registry']->getAuth(), 'contact' ); } /* Log the creation of this item in the history log. */ try { $GLOBALS['injector']->getInstance('Horde_History') ->log('turba:' . $this->getName() . ':' . $uid, array('action' => 'add'), true); } catch (Exception $e) { Horde::log($e, 'ERR'); } return $key; } /** * Returns ability of the backend to add new contacts. * * @return boolean Can backend add? */ public function canAdd() { return $this->_canAdd(); } /** * Returns ability of the backend to add new contacts. * * @return boolean Can backend add? */ protected function _canAdd() { return false; } /** * Deletes the specified entry from the contact source. * * @param string $object_id The ID of the object to delete. * @param boolean $remove_tags Remove tags if true. * * @throws Turba_Exception * @throws Horde_Exception_NotFound */ public function delete($object_id, $remove_tags = true) { $object = $this->getObject($object_id); if (!$object->hasPermission(Horde_Perms::DELETE)) { throw new Turba_Exception(_("Permission denied")); } $this->_delete($this->toDriver('__key'), $object_id); $own_contact = $GLOBALS['prefs']->getValue('own_contact'); if (!empty($own_contact)) { @list(,$id) = explode(';', $own_contact); if ($id == $object_id) { $GLOBALS['prefs']->setValue('own_contact', ''); } } /* Log the deletion of this item in the history log. */ if ($object->getValue('__uid')) { try { $GLOBALS['injector']->getInstance('Horde_History')->log($object->getGuid(), array('action' => 'delete'), true); } catch (Exception $e) { Horde::log($e, 'ERR'); } } /* Remove any CalDAV mappings. */ try { $davStorage = $GLOBALS['injector'] ->getInstance('Horde_Dav_Storage'); try { $davStorage ->deleteInternalObjectId($object_id, $this->_name); } catch (Horde_Exception $e) { Horde::log($e); } } catch (Horde_Exception $e) { } /* Remove tags */ if ($remove_tags) { $GLOBALS['injector']->getInstance('Turba_Tagger') ->replaceTags($object->getValue('__uid'), array(), $this->getContactOwner(), 'contact'); /* Might have tags disabled, hence no Content_* objects autoloadable. */ try { /* Tell content we removed the object */ $GLOBALS['injector']->getInstance('Content_Objects_Manager') ->delete(array($object->getValue('__uid')), 'contact'); } catch (Horde_Exception $e) {} } } /** * Deletes all contacts from an address book. * * @param string $sourceName The identifier of the address book to * delete. If omitted, will clear the current * user's 'default' address book for this * source type. * * @throws Turba_Exception */ public function deleteAll($sourceName = null) { if (!$this->hasCapability('delete_all')) { throw new Turba_Exception('Not supported'); } $ids = $this->_deleteAll($sourceName); // Update Horde_History and Tagger $history = $GLOBALS['injector']->getInstance('Horde_History'); try { foreach ($ids as $uid) { // This is slightly hackish, but it saves us from having to // create and save an array of Turba_Objects before we delete // them, just to be able to calculate this using // Turba_Object#getGuid $guid = 'turba:' . $this->getName() . ':' . $uid; $history->log($guid, array('action' => 'delete'), true); // Remove tags. $GLOBALS['injector']->getInstance('Turba_Tagger') ->replaceTags($uid, array(), $this->getContactOwner(), 'contact'); /* Tell content we removed the object */ $GLOBALS['injector']->getInstance('Content_Objects_Manager') ->delete(array($uid), 'contact'); } } catch (Exception $e) { Horde::log($e, 'ERR'); } } /** * Deletes all contacts from an address book. * * @param string $sourceName The source to remove all contacts from. * * @return array An array of UIDs that have been deleted. * @throws Turba_Exception */ protected function _deleteAll() { return array(); } /** * Modifies an existing entry in the contact source. * * @param Turba_Object $object The object to update. * * @return string The object id, possibly updated. * @throws Turba_Exception */ public function save(Turba_Object $object) { $object_id = $this->_save($object); if ($uid = $object->getValue('__uid')) { /* Update tags. */ if (!is_null($tags = $object->getValue('__tags'))) { $GLOBALS['injector']->getInstance('Turba_Tagger')->replaceTags( $uid, $tags, $this->getContactOwner(), 'contact' ); } /* Log the modification of this item in the history log. */ try { $GLOBALS['injector']->getInstance('Horde_History')->log($object->getGuid(), array('action' => 'modify'), true); } catch (Exception $e) { Horde::log($e, 'ERR'); } } return $object_id; } /** * Returns the criteria available for this source except '__key'. * * @return array An array containing the criteria. */ public function getCriteria() { return array_diff_key($this->map, array('__key' => true)); } /** * Returns all non-composite fields for this source. Useful for importing * and exporting data, etc. * * @return array The field list. */ public function getFields() { return array_flip($this->fields); } /** * Exports a given Turba_Object as an iCalendar vCard. * * @param Turba_Object $object Turba_Object. * @param string $version The vcard version to produce. * @param array $fields Hash of field names and * Horde_SyncMl_Property properties with the * requested fields. * @param boolean $skipEmpty Whether to skip empty fields. * * @return Horde_Icalendar_Vcard A vcard object. */ public function tovCard(Turba_Object $object, $version = '2.1', array $fields = null, $skipEmpty = false) { global $injector; $hash = $object->getAttributes(); $attributes = array_keys($this->map); $vcard = new Horde_Icalendar_Vcard($version); $formattedname = false; $charset = ($version == '2.1') ? array('CHARSET' => 'UTF-8') : array(); $hooks = $injector->getInstance('Horde_Core_Hooks'); $decode_hook = $hooks->hookExists('decode_attribute', 'turba'); // Tags are stored externally to Turba, so they don't appear in the // source map. $attributes[] = '__tags'; foreach ($attributes as $key) { $val = $object->getValue($key); if ($skipEmpty && !is_array($val) && !strlen($val)) { continue; } if ($decode_hook) { try { $val = $hooks->callHook( 'decode_attribute', 'turba', array($key, $val, $object) ); } catch (Turba_Exception $e) {} } switch ($key) { case '__uid': $vcard->setAttribute('UID', $val); break; case 'name': if ($fields && !isset($fields['FN'])) { break; } $vcard->setAttribute('FN', $val, Horde_Mime::is8bit($val) ? $charset : array()); $formattedname = true; break; case 'nickname': case 'alias': $params = Horde_Mime::is8bit($val) ? $charset : array(); if (!$fields || isset($fields['NICKNAME'])) { $vcard->setAttribute('NICKNAME', $val, $params); } if (!$fields || isset($fields['X-EPOCSECONDNAME'])) { $vcard->setAttribute('X-EPOCSECONDNAME', $val, $params); } break; case 'homeAddress': if ($fields && (!isset($fields['LABEL']) || (isset($fields['LABEL']->Params['TYPE']) && !$this->_hasValEnum($fields['LABEL']->Params['TYPE']->ValEnum, 'HOME')))) { break; } if ($version == '2.1') { $vcard->setAttribute('LABEL', $val, array('HOME' => null)); } else { $vcard->setAttribute('LABEL', $val, array('TYPE' => 'HOME')); } break; case 'workAddress': if ($fields && (!isset($fields['LABEL']) || (isset($fields['LABEL']->Params['TYPE']) && !$this->_hasValEnum($fields['LABEL']->Params['TYPE']->ValEnum, 'WORK')))) { break; } if ($version == '2.1') { $vcard->setAttribute('LABEL', $val, array('WORK' => null)); } else { $vcard->setAttribute('LABEL', $val, array('TYPE' => 'WORK')); } break; case 'otherAddress': if ($fields && !isset($fields['LABEL'])) { break; } $vcard->setAttribute('LABEL', $val); break; case 'phone': if ($fields && !isset($fields['TEL'])) { break; } $vcard->setAttribute('TEL', $val); break; case 'homePhone': if ($fields && (!isset($fields['TEL']) || (isset($fields['TEL']->Params['TYPE']) && !$this->_hasValEnum($fields['TEL']->Params['TYPE']->ValEnum, 'HOME')))) { break; } if ($version == '2.1') { $vcard->setAttribute('TEL', $val, array('HOME' => null, 'VOICE' => null)); } else { $vcard->setAttribute('TEL', $val, array('TYPE' => array('HOME', 'VOICE'))); } break; case 'workPhone': if ($fields && (!isset($fields['TEL']) || (isset($fields['TEL']->Params['TYPE']) && !$this->_hasValEnum($fields['TEL']->Params['TYPE']->ValEnum, 'WORK')))) { break; } if ($version == '2.1') { $vcard->setAttribute('TEL', $val, array('WORK' => null, 'VOICE' => null)); } else { $vcard->setAttribute('TEL', $val, array('TYPE' => array('WORK', 'VOICE'))); } break; case 'cellPhone': if ($fields && (!isset($fields['TEL']) || (isset($fields['TEL']->Params['TYPE']) && !$this->_hasValEnum($fields['TEL']->Params['TYPE']->ValEnum, 'CELL')))) { break; } if ($version == '2.1') { $vcard->setAttribute('TEL', $val, array('CELL' => null, 'VOICE' => null)); } else { $vcard->setAttribute('TEL', $val, array('TYPE' => array('CELL', 'VOICE'))); } break; case 'homeCellPhone': $parameters = array(); if ($fields) { if (!isset($fields['TEL'])) { break; } if (!isset($fields['TEL']->Params['TYPE']) || $this->_hasValEnum($fields['TEL']->Params['TYPE']->ValEnum, 'CELL')) { if ($version == '2.1') { $parameters['CELL'] = null; $parameters['VOICE'] = null; } else { $parameters['TYPE'] = array('CELL', 'VOICE'); } } if (!isset($fields['TEL']->Params['TYPE']) || $this->_hasValEnum($fields['TEL']->Params['TYPE']->ValEnum, 'HOME')) { if ($version == '2.1') { $parameters['HOME'] = null; $parameters['VOICE'] = null; } else { $parameters['TYPE'] = array('HOME', 'VOICE'); } } if (empty($parameters)) { break; } } else { if ($version == '2.1') { $parameters = array('CELL' => null, 'HOME' => null, 'VOICE' => null); } else { $parameters = array('TYPE' => array('CELL', 'HOME', 'VOICE')); } } $vcard->setAttribute('TEL', $val, $parameters); break; case 'workCellPhone': $parameters = array(); if ($fields) { if (!isset($fields['TEL'])) { break; } if (!isset($fields['TEL']->Params['TYPE']) || $this->_hasValEnum($fields['TEL']->Params['TYPE']->ValEnum, 'CELL')) { if ($version == '2.1') { $parameters['CELL'] = null; $parameters['VOICE'] = null; } else { $parameters['TYPE'] = array('CELL', 'VOICE'); } } if (!isset($fields['TEL']->Params['TYPE']) || $this->_hasValEnum($fields['TEL']->Params['TYPE']->ValEnum, 'WORK')) { if ($version == '2.1') { $parameters['WORK'] = null; $parameters['VOICE'] = null; } else { $parameters['TYPE'] = array('WORK', 'VOICE'); } } if (empty($parameters)) { break; } } else { if ($version == '2.1') { $parameters = array('CELL' => null, 'WORK' => null, 'VOICE' => null); } else { $parameters = array('TYPE' => array('CELL', 'WORK', 'VOICE')); } } $vcard->setAttribute('TEL', $val, $parameters); break; case 'videoCall': if ($fields && (!isset($fields['TEL']) || (isset($fields['TEL']->Params['TYPE']) && !$this->_hasValEnum($fields['TEL']->Params['TYPE']->ValEnum, 'VIDEO')))) { break; } if ($version == '2.1') { $vcard->setAttribute('TEL', $val, array('VIDEO' => null)); } else { $vcard->setAttribute('TEL', $val, array('TYPE' => 'VIDEO')); } break; case 'homeVideoCall': $parameters = array(); if ($fields) { if (!isset($fields['TEL'])) { break; } if (!isset($fields['TEL']->Params['TYPE']) || $this->_hasValEnum($fields['TEL']->Params['TYPE']->ValEnum, 'VIDEO')) { if ($version == '2.1') { $parameters['VIDEO'] = null; } else { $parameters['TYPE'] = 'VIDEO'; } } if (!isset($fields['TEL']->Params['TYPE']) || $this->_hasValEnum($fields['TEL']->Params['TYPE']->ValEnum, 'HOME')) { if ($version == '2.1') { $parameters['HOME'] = null; } else { $parameters['TYPE'] = 'HOME'; } } if (empty($parameters)) { break; } } else { if ($version == '2.1') { $parameters = array('VIDEO' => null, 'HOME' => null); } else { $parameters = array('TYPE' => array('VIDEO', 'HOME')); } } $vcard->setAttribute('TEL', $val, $parameters); break; case 'workVideoCall': $parameters = array(); if ($fields) { if (!isset($fields['TEL'])) { break; } if (!isset($fields['TEL']->Params['TYPE']) || $this->_hasValEnum($fields['TEL']->Params['TYPE']->ValEnum, 'VIDEO')) { if ($version == '2.1') { $parameters['VIDEO'] = null; } else { $parameters['TYPE'] = 'VIDEO'; } } if (!isset($fields['TEL']->Params['TYPE']) || $this->_hasValEnum($fields['TEL']->Params['TYPE']->ValEnum, 'WORK')) { if ($version == '2.1') { $parameters['WORK'] = null; } else { $parameters['TYPE'] = 'WORK'; } } if (empty($parameters)) { break; } } else { if ($version == '2.1') { $parameters = array('VIDEO' => null, 'WORK' => null); } else { $parameters = array('TYPE' => array('VIDEO', 'WORK')); } } $vcard->setAttribute('TEL', $val, $parameters); break; case 'sip': if ($fields && !isset($fields['X-SIP'])) { break; } $vcard->setAttribute('X-SIP', $val); break; case 'ptt': if ($fields && (!isset($fields['X-SIP']) || (isset($fields['X-SIP']->Params['TYPE']) && !$this->_hasValEnum($fields['X-SIP']->Params['TYPE']->ValEnum, 'POC')))) { break; } if ($version == '2.1') { $vcard->setAttribute('X-SIP', $val, array('POC' => null)); } else { $vcard->setAttribute('X-SIP', $val, array('TYPE' => 'POC')); } break; case 'voip': if ($fields && (!isset($fields['X-SIP']) || (isset($fields['X-SIP']->Params['TYPE']) && !$this->_hasValEnum($fields['X-SIP']->Params['TYPE']->ValEnum, 'VOIP')))) { break; } if ($version == '2.1') { $vcard->setAttribute('X-SIP', $val, array('VOIP' => null)); } else { $vcard->setAttribute('X-SIP', $val, array('TYPE' => 'VOIP')); } break; case 'shareView': if ($fields && (!isset($fields['X-SIP']) || (isset($fields['X-SIP']->Params['TYPE']) && !$this->_hasValEnum($fields['X-SIP']->Params['TYPE']->ValEnum, 'SWIS')))) { break; } if ($version == '2.1') { $vcard->setAttribute('X-SIP', $val, array('SWIS' => null)); } else { $vcard->setAttribute('X-SIP', $val, array('TYPE' => 'SWIS')); } break; case 'imaddress': if ($fields && !isset($fields['X-WV-ID'])) { break; } $vcard->setAttribute('X-WV-ID', $val); break; case 'fax': if ($fields && (!isset($fields['TEL']) || (isset($fields['TEL']->Params['TYPE']) && !$this->_hasValEnum($fields['TEL']->Params['TYPE']->ValEnum, 'FAX')))) { break; } if ($version == '2.1') { $vcard->setAttribute('TEL', $val, array('FAX' => null)); } else { $vcard->setAttribute('TEL', $val, array('TYPE' => 'FAX')); } break; case 'homeFax': $parameters = array(); if ($fields) { if (!isset($fields['TEL'])) { break; } if (!isset($fields['TEL']->Params['TYPE']) || $this->_hasValEnum($fields['TEL']->Params['TYPE']->ValEnum, 'FAX')) { if ($version == '2.1') { $parameters['FAX'] = null; } else { $parameters['TYPE'] = 'FAX'; } } if (!isset($fields['TEL']->Params['TYPE']) || $this->_hasValEnum($fields['TEL']->Params['TYPE']->ValEnum, 'HOME')) { if ($version == '2.1') { $parameters['HOME'] = null; } else { $parameters['TYPE'] = 'HOME'; } } if (empty($parameters)) { break; } } else { if ($version == '2.1') { $parameters = array('FAX' => null, 'HOME' => null); } else { $parameters = array('TYPE' => array('FAX', 'HOME')); } } $vcard->setAttribute('TEL', $val, $parameters); break; case 'workFax': $parameters = array(); if ($fields) { if (!isset($fields['TEL'])) { break; } if (!isset($fields['TEL']->Params['TYPE']) || $this->_hasValEnum($fields['TEL']->Params['TYPE']->ValEnum, 'FAX')) { if ($version == '2.1') { $parameters['FAX'] = null; } else { $parameters['TYPE'] = 'FAX'; } } if (!isset($fields['TEL']->Params['TYPE']) || $this->_hasValEnum($fields['TEL']->Params['TYPE']->ValEnum, 'WORK')) { if ($version == '2.1') { $parameters['WORK'] = null; } else { $parameters['TYPE'] = 'WORK'; } } if (empty($parameters)) { break; } } else { if ($version == '2.1') { $parameters = array('FAX' => null, 'WORK' => null); } else { $parameters = array('TYPE' => array('FAX', 'WORK')); } } $vcard->setAttribute('TEL', $val, $parameters); if ($version == '2.1') { $vcard->setAttribute('TEL', $val, array('FAX' => null, 'WORK' => null)); } else { $vcard->setAttribute('TEL', $val, array('TYPE' => array('FAX', 'WORK'))); } break; case 'pager': if ($fields && (!isset($fields['TEL']) || (isset($fields['TEL']->Params['TYPE']) && !$this->_hasValEnum($fields['TEL']->Params['TYPE']->ValEnum, 'PAGER')))) { break; } if ($version == '2.1') { $vcard->setAttribute('TEL', $val, array('PAGER' => null)); } else { $vcard->setAttribute('TEL', $val, array('TYPE' => 'PAGER')); } break; case 'email': if ($fields && !isset($fields['EMAIL'])) { break; } if ($version == '2.1') { $vcard->setAttribute( 'EMAIL', Horde_Icalendar_Vcard::getBareEmail($val), array('INTERNET' => null)); } else { $vcard->setAttribute( 'EMAIL', Horde_Icalendar_Vcard::getBareEmail($val), array('TYPE' => 'INTERNET')); } break; case 'homeEmail': if ($fields && (!isset($fields['EMAIL']) || (isset($fields['EMAIL']->Params['TYPE']) && !$this->_hasValEnum($fields['EMAIL']->Params['TYPE']->ValEnum, 'HOME')))) { break; } if ($version == '2.1') { $vcard->setAttribute('EMAIL', Horde_Icalendar_Vcard::getBareEmail($val), array('HOME' => null)); } else { $vcard->setAttribute('EMAIL', Horde_Icalendar_Vcard::getBareEmail($val), array('TYPE' => 'HOME')); } break; case 'workEmail': if ($fields && (!isset($fields['EMAIL']) || (isset($fields['EMAIL']->Params['TYPE']) && !$this->_hasValEnum($fields['EMAIL']->Params['TYPE']->ValEnum, 'WORK')))) { break; } if ($version == '2.1') { $vcard->setAttribute('EMAIL', Horde_Icalendar_Vcard::getBareEmail($val), array('WORK' => null)); } else { $vcard->setAttribute('EMAIL', Horde_Icalendar_Vcard::getBareEmail($val), array('TYPE' => 'WORK')); } break; case 'emails': if ($fields && !isset($fields['EMAIL'])) { break; } $emails = explode(',', $val); foreach ($emails as $email) { $vcard->setAttribute('EMAIL', Horde_Icalendar_Vcard::getBareEmail($email)); } break; case 'title': if ($fields && !isset($fields['TITLE'])) { break; } $vcard->setAttribute('TITLE', $val, Horde_Mime::is8bit($val) ? $charset : array()); break; case 'role': if ($fields && !isset($fields['ROLE'])) { break; } $vcard->setAttribute('ROLE', $val, Horde_Mime::is8bit($val) ? $charset : array()); break; case 'notes': if ($fields && !isset($fields['NOTE'])) { break; } $vcard->setAttribute('NOTE', $val, Horde_Mime::is8bit($val) ? $charset : array()); break; case '__tags': $val = $injector->getInstance('Turba_Tagger')->split($val); case 'businessCategory': // No CATEGORIES in vCard 2.1 if ($version == '2.1' || ($fields && !isset($fields['CATEGORIES']))) { break; } $vcard->setAttribute('CATEGORIES', null, array(), true, $val); break; case 'anniversary': if (!$fields || isset($fields['X-ANNIVERSARY'])) { $vcard->setAttribute('X-ANNIVERSARY', $val); } break; case 'spouse': if (!$fields || isset($fields['X-SPOUSE'])) { $vcard->setAttribute('X-SPOUSE', $val); } break; case 'children': if (!$fields || isset($fields['X-CHILDREN'])) { $vcard->setAttribute('X-CHILDREN', $val); } break; case 'website': if ($fields && !isset($fields['URL'])) { break; } $vcard->setAttribute('URL', $val); break; case 'homeWebsite': if ($fields && (!isset($fields['URL']) || (isset($fields['URL']->Params['TYPE']) && !$this->_hasValEnum($fields['URL']->Params['TYPE']->ValEnum, 'HOME')))) { break; } if ($version == '2.1') { $vcard->setAttribute('URL', $val, array('HOME' => null)); } else { $vcard->setAttribute('URL', $val, array('TYPE' => 'HOME')); } break; case 'workWebsite': if ($fields && (!isset($fields['URL']) || (isset($fields['URL']->Params['TYPE']) && !$this->_hasValEnum($fields['URL']->Params['TYPE']->ValEnum, 'WORK')))) { break; } if ($version == '2.1') { $vcard->setAttribute('URL', $val, array('WORK' => null)); } else { $vcard->setAttribute('URL', $val, array('TYPE' => 'WORK')); } break; case 'freebusyUrl': if ($version == '2.1' || ($fields && !isset($fields['FBURL']))) { break; } $vcard->setAttribute('FBURL', $val); break; case 'birthday': if ($fields && !isset($fields['BDAY'])) { break; } $vcard->setAttribute('BDAY', $val); break; case 'timezone': if ($fields && !isset($fields['TZ'])) { break; } $vcard->setAttribute('TZ', $val, array('VALUE' => 'text')); break; case 'latitude': if ($fields && !isset($fields['GEO'])) { break; } if (isset($hash['longitude'])) { $vcard->setAttribute('GEO', array('latitude' => $val, 'longitude' => $hash['longitude'])); } break; case 'homeLatitude': if ($fields && (!isset($fields['GEO']) || (isset($fields['GEO']->Params['TYPE']) && !$this->_hasValEnum($fields['GEO']->Params['TYPE']->ValEnum, 'HOME')))) { break; } if (isset($hash['homeLongitude'])) { if ($version == '2.1') { $vcard->setAttribute('GEO', array('latitude' => $val, 'longitude' => $hash['homeLongitude']), array('HOME' => null)); } else { $vcard->setAttribute('GEO', array('latitude' => $val, 'longitude' => $hash['homeLongitude']), array('TYPE' => 'HOME')); } } break; case 'workLatitude': if ($fields && (!isset($fields['GEO']) || (isset($fields['GEO']->Params['TYPE']) && !$this->_hasValEnum($fields['GEO']->Params['TYPE']->ValEnum, 'HOME')))) { break; } if (isset($hash['workLongitude'])) { if ($version == '2.1') { $vcard->setAttribute('GEO', array('latitude' => $val, 'longitude' => $hash['workLongitude']), array('WORK' => null)); } else { $vcard->setAttribute('GEO', array('latitude' => $val, 'longitude' => $hash['workLongitude']), array('TYPE' => 'WORK')); } } break; case 'photo': case 'logo': $name = Horde_String::upper($key); $params = array(); if (strlen($hash[$key])) { $params['ENCODING'] = 'b'; } if (isset($hash[$key . 'type'])) { $params['TYPE'] = $hash[$key . 'type']; } if ($fields && (!isset($fields[$name]) || (isset($params['TYPE']) && isset($fields[$name]->Params['TYPE']) && !$this->_hasValEnum($fields[$name]->Params['TYPE']->ValEnum, $params['TYPE'])))) { break; } $vcard->setAttribute($name, base64_encode($hash[$key]), $params); break; } } // No explicit firstname/lastname in data source: we have to guess. if (!isset($hash['lastname']) && isset($hash['name'])) { $this->_guessName($hash); } $a = array( Horde_Icalendar_Vcard::N_FAMILY => isset($hash['lastname']) ? $hash['lastname'] : '', Horde_Icalendar_Vcard::N_GIVEN => isset($hash['firstname']) ? $hash['firstname'] : '', Horde_Icalendar_Vcard::N_ADDL => isset($hash['middlenames']) ? $hash['middlenames'] : '', Horde_Icalendar_Vcard::N_PREFIX => isset($hash['namePrefix']) ? $hash['namePrefix'] : '', Horde_Icalendar_Vcard::N_SUFFIX => isset($hash['nameSuffix']) ? $hash['nameSuffix'] : '', ); $val = implode(';', $a); if (!$fields || isset($fields['N'])) { $vcard->setAttribute('N', $val, Horde_Mime::is8bit($val) ? $charset : array(), false, $a); } if (!$formattedname && (!$fields || isset($fields['FN']))) { if ($object->getValue('name')) { $val = $object->getValue('name'); } elseif (!empty($this->alternativeName) && isset($hash[$this->alternativeName])) { $val = $hash[$this->alternativeName]; } else { $val = ''; } $vcard->setAttribute('FN', $val, Horde_Mime::is8bit($val) ? $charset : array()); } $org = array(); if (!empty($hash['company']) || (!$skipEmpty && array_key_exists('company', $hash))) { $org[] = $hash['company']; } if (!empty($hash['department']) || (!$skipEmpty && array_key_exists('department', $hash))) { $org[] = $hash['department']; } if (count($org) && (!$fields || isset($fields['ORG']))) { $val = implode(';', $org); $vcard->setAttribute('ORG', $val, Horde_Mime::is8bit($val) ? $charset : array(), false, $org); } if ((!$fields || isset($fields['ADR'])) && (!empty($hash['commonAddress']) || !empty($hash['commonStreet']) || !empty($hash['commonPOBox']) || !empty($hash['commonExtended']) || !empty($hash['commonCity']) || !empty($hash['commonProvince']) || !empty($hash['commonPostalCode']) || !empty($hash['commonCountry']) || (!$skipEmpty && (array_key_exists('commonAddress', $hash) || array_key_exists('commonStreet', $hash) || array_key_exists('commonPOBox', $hash) || array_key_exists('commonExtended', $hash) || array_key_exists('commonCity', $hash) || array_key_exists('commonProvince', $hash) || array_key_exists('commonPostalCode', $hash) || array_key_exists('commonCountry', $hash))))) { /* We can't know if this particular Turba source uses a single * address field or multiple for * street/city/province/postcode/country. Try to deal with * both. */ if (isset($hash['commonAddress']) && !isset($hash['commonStreet'])) { $hash['commonStreet'] = $hash['commonAddress']; } $a = array( Horde_Icalendar_Vcard::ADR_POB => isset($hash['commonPOBox']) ? $hash['commonPOBox'] : '', Horde_Icalendar_Vcard::ADR_EXTEND => isset($hash['commonExtended']) ? $hash['commonExtended'] : '', Horde_Icalendar_Vcard::ADR_STREET => isset($hash['commonStreet']) ? $hash['commonStreet'] : '', Horde_Icalendar_Vcard::ADR_LOCALITY => isset($hash['commonCity']) ? $hash['commonCity'] : '', Horde_Icalendar_Vcard::ADR_REGION => isset($hash['commonProvince']) ? $hash['commonProvince'] : '', Horde_Icalendar_Vcard::ADR_POSTCODE => isset($hash['commonPostalCode']) ? $hash['commonPostalCode'] : '', Horde_Icalendar_Vcard::ADR_COUNTRY => isset($hash['commonCountry']) ? Horde_Nls::getCountryISO($hash['commonCountry']) : '', ); $val = implode(';', $a); if ($version == '2.1') { $params = array(); if (Horde_Mime::is8bit($val)) { $params['CHARSET'] = 'UTF-8'; } } else { $params = array('TYPE' => ''); } $vcard->setAttribute('ADR', $val, $params, true, $a); } if ((!$fields || (isset($fields['ADR']) && (!isset($fields['ADR']->Params['TYPE']) || $this->_hasValEnum($fields['ADR']->Params['TYPE']->ValEnum, 'HOME')))) && (!empty($hash['homeAddress']) || !empty($hash['homeStreet']) || !empty($hash['homePOBox']) || !empty($hash['homeExtended']) || !empty($hash['homeCity']) || !empty($hash['homeProvince']) || !empty($hash['homePostalCode']) || !empty($hash['homeCountry']) || (!$skipEmpty && (array_key_exists('homeAddress', $hash) || array_key_exists('homeStreet', $hash) || array_key_exists('homePOBox', $hash) || array_key_exists('homeExtended', $hash) || array_key_exists('homeCity', $hash) || array_key_exists('homeProvince', $hash) || array_key_exists('homePostalCode', $hash) || array_key_exists('homeCountry', $hash))))) { if (isset($hash['homeAddress']) && !isset($hash['homeStreet'])) { $hash['homeStreet'] = $hash['homeAddress']; } $a = array( Horde_Icalendar_Vcard::ADR_POB => isset($hash['homePOBox']) ? $hash['homePOBox'] : '', Horde_Icalendar_Vcard::ADR_EXTEND => isset($hash['homeExtended']) ? $hash['homeExtended'] : '', Horde_Icalendar_Vcard::ADR_STREET => isset($hash['homeStreet']) ? $hash['homeStreet'] : '', Horde_Icalendar_Vcard::ADR_LOCALITY => isset($hash['homeCity']) ? $hash['homeCity'] : '', Horde_Icalendar_Vcard::ADR_REGION => isset($hash['homeProvince']) ? $hash['homeProvince'] : '', Horde_Icalendar_Vcard::ADR_POSTCODE => isset($hash['homePostalCode']) ? $hash['homePostalCode'] : '', Horde_Icalendar_Vcard::ADR_COUNTRY => isset($hash['homeCountry']) ? Horde_Nls::getCountryISO($hash['homeCountry']) : '', ); $val = implode(';', $a); if ($version == '2.1') { $params = array('HOME' => null); if (Horde_Mime::is8bit($val)) { $params['CHARSET'] = 'UTF-8'; } } else { $params = array('TYPE' => 'HOME'); } $vcard->setAttribute('ADR', $val, $params, true, $a); } if ((!$fields || (isset($fields['ADR']) && (!isset($fields['ADR']->Params['TYPE']) || $this->_hasValEnum($fields['ADR']->Params['TYPE']->ValEnum, 'WORK')))) && (!empty($hash['workAddress']) || !empty($hash['workStreet']) || !empty($hash['workPOBox']) || !empty($hash['workExtended']) || !empty($hash['workCity']) || !empty($hash['workProvince']) || !empty($hash['workPostalCode']) || !empty($hash['workCountry']) || (!$skipEmpty && (array_key_exists('workAddress', $hash) || array_key_exists('workStreet', $hash) || array_key_exists('workPOBox', $hash) || array_key_exists('workExtended', $hash) || array_key_exists('workCity', $hash) || array_key_exists('workProvince', $hash) || array_key_exists('workPostalCode', $hash) || array_key_exists('workCountry', $hash))))) { if (isset($hash['workAddress']) && !isset($hash['workStreet'])) { $hash['workStreet'] = $hash['workAddress']; } $a = array( Horde_Icalendar_Vcard::ADR_POB => isset($hash['workPOBox']) ? $hash['workPOBox'] : '', Horde_Icalendar_Vcard::ADR_EXTEND => isset($hash['workExtended']) ? $hash['workExtended'] : '', Horde_Icalendar_Vcard::ADR_STREET => isset($hash['workStreet']) ? $hash['workStreet'] : '', Horde_Icalendar_Vcard::ADR_LOCALITY => isset($hash['workCity']) ? $hash['workCity'] : '', Horde_Icalendar_Vcard::ADR_REGION => isset($hash['workProvince']) ? $hash['workProvince'] : '', Horde_Icalendar_Vcard::ADR_POSTCODE => isset($hash['workPostalCode']) ? $hash['workPostalCode'] : '', Horde_Icalendar_Vcard::ADR_COUNTRY => isset($hash['workCountry']) ? Horde_Nls::getCountryISO($hash['workCountry']) : '', ); $val = implode(';', $a); if ($version == '2.1') { $params = array('WORK' => null); if (Horde_Mime::is8bit($val)) { $params['CHARSET'] = 'UTF-8'; } } else { $params = array('TYPE' => 'WORK'); } $vcard->setAttribute('ADR', $val, $params, true, $a); } return $vcard; } /** * Returns whether a ValEnum entry from a DevInf object contains a certain * type. * * @param array $valEnum A ValEnum hash. * @param string $type A requested attribute type. * * @return boolean True if $type exists in $valEnum. */ protected function _hasValEnum($valEnum, $type) { foreach (array_keys($valEnum) as $key) { if (in_array($type, explode(',', $key))) { return true; } } return false; } /** * Function to convert a Horde_Icalendar_Vcard object into a Turba * Object Hash with Turba attributes suitable as a parameter for add(). * * @see add() * * @param Horde_Icalendar_Vcard $vcard The Horde_Icalendar_Vcard object * to parse. * * @return array A Turba attribute hash. */ public function toHash(Horde_Icalendar_Vcard $vcard) { global $attributes; $hash = array(); $attr = $vcard->getAllAttributes(); foreach ($attr as $item) { switch ($item['name']) { case 'UID': $hash['__uid'] = $item['value']; break; case 'FN': $hash['name'] = $item['value']; break; case 'N': $name = $item['values']; if (!empty($name[Horde_Icalendar_Vcard::N_FAMILY])) { $hash['lastname'] = $name[Horde_Icalendar_Vcard::N_FAMILY]; } if (!empty($name[Horde_Icalendar_Vcard::N_GIVEN])) { $hash['firstname'] = $name[Horde_Icalendar_Vcard::N_GIVEN]; } if (!empty($name[Horde_Icalendar_Vcard::N_ADDL])) { $hash['middlenames'] = $name[Horde_Icalendar_Vcard::N_ADDL]; } if (!empty($name[Horde_Icalendar_Vcard::N_PREFIX])) { $hash['namePrefix'] = $name[Horde_Icalendar_Vcard::N_PREFIX]; } if (!empty($name[Horde_Icalendar_Vcard::N_SUFFIX])) { $hash['nameSuffix'] = $name[Horde_Icalendar_Vcard::N_SUFFIX]; } break; case 'NICKNAME': case 'X-EPOCSECONDNAME': $hash['nickname'] = $item['value']; $hash['alias'] = $item['value']; break; // We use LABEL but also support ADR. case 'LABEL': if (isset($item['params']['HOME']) && !isset($hash['homeAddress'])) { $hash['homeAddress'] = $item['value']; } elseif (isset($item['params']['WORK']) && !isset($hash['workAddress'])) { $hash['workAddress'] = $item['value']; } elseif (!isset($hash['commonAddress'])) { $hash['commonAddress'] = $item['value']; } break; case 'ADR': if (isset($item['params']['TYPE'])) { if (!is_array($item['params']['TYPE'])) { $item['params']['TYPE'] = array($item['params']['TYPE']); } } else { $item['params']['TYPE'] = array(); if (isset($item['params']['WORK'])) { $item['params']['TYPE'][] = 'WORK'; } if (isset($item['params']['HOME'])) { $item['params']['TYPE'][] = 'HOME'; } if (count($item['params']['TYPE']) == 0) { $item['params']['TYPE'][] = 'COMMON'; } } $address = $item['values']; foreach ($item['params']['TYPE'] as $adr) { switch (Horde_String::upper($adr)) { case 'HOME': $prefix = 'home'; break; case 'WORK': $prefix = 'work'; break; default: $prefix = 'common'; } if (isset($hash[$prefix . 'Address'])) { continue; } $hash[$prefix . 'Address'] = ''; if (!empty($address[Horde_Icalendar_Vcard::ADR_STREET])) { $hash[$prefix . 'Street'] = $address[Horde_Icalendar_Vcard::ADR_STREET]; $hash[$prefix . 'Address'] .= $hash[$prefix . 'Street'] . "\n"; } if (!empty($address[Horde_Icalendar_Vcard::ADR_EXTEND])) { $hash[$prefix . 'Extended'] = $address[Horde_Icalendar_Vcard::ADR_EXTEND]; $hash[$prefix . 'Address'] .= $hash[$prefix . 'Extended'] . "\n"; } if (!empty($address[Horde_Icalendar_Vcard::ADR_POB])) { $hash[$prefix . 'POBox'] = $address[Horde_Icalendar_Vcard::ADR_POB]; $hash[$prefix . 'Address'] .= $hash[$prefix . 'POBox'] . "\n"; } if (!empty($address[Horde_Icalendar_Vcard::ADR_LOCALITY])) { $hash[$prefix . 'City'] = $address[Horde_Icalendar_Vcard::ADR_LOCALITY]; $hash[$prefix . 'Address'] .= $hash[$prefix . 'City']; } if (!empty($address[Horde_Icalendar_Vcard::ADR_REGION])) { $hash[$prefix . 'Province'] = $address[Horde_Icalendar_Vcard::ADR_REGION]; $hash[$prefix . 'Address'] .= ', ' . $hash[$prefix . 'Province']; } if (!empty($address[Horde_Icalendar_Vcard::ADR_POSTCODE])) { $hash[$prefix . 'PostalCode'] = $address[Horde_Icalendar_Vcard::ADR_POSTCODE]; $hash[$prefix . 'Address'] .= ' ' . $hash[$prefix . 'PostalCode']; } if (!empty($address[Horde_Icalendar_Vcard::ADR_COUNTRY])) { include 'Horde/Nls/Countries.php'; $country = array_search($address[Horde_Icalendar_Vcard::ADR_COUNTRY], $countries); if ($country === false) { $country = $address[Horde_Icalendar_Vcard::ADR_COUNTRY]; } $hash[$prefix . 'Country'] = $country; $hash[$prefix . 'Address'] .= "\n" . $address[Horde_Icalendar_Vcard::ADR_COUNTRY]; } $hash[$prefix . 'Address'] = trim($hash[$prefix . 'Address']); } break; case 'TZ': // We only support textual timezones. if (!isset($item['params']['VALUE']) || Horde_String::lower($item['params']['VALUE']) != 'text') { break; } $timezones = explode(';', $item['value']); $available_timezones = Horde_Nls::getTimezones(); foreach ($timezones as $timezone) { $timezone = trim($timezone); if (isset($available_timezones[$timezone])) { $hash['timezone'] = $timezone; break 2; } } break; case 'GEO': if (isset($item['params']['HOME'])) { $hash['homeLatitude'] = $item['value']['latitude']; $hash['homeLongitude'] = $item['value']['longitude']; } elseif (isset($item['params']['WORK'])) { $hash['workLatitude'] = $item['value']['latitude']; $hash['workLongitude'] = $item['value']['longitude']; } else { $hash['latitude'] = $item['value']['latitude']; $hash['longitude'] = $item['value']['longitude']; } break; case 'TEL': if (isset($item['params']['FAX'])) { if (isset($item['params']['WORK']) && !isset($hash['workFax'])) { $hash['workFax'] = $item['value']; } elseif (isset($item['params']['HOME']) && !isset($hash['homeFax'])) { $hash['homeFax'] = $item['value']; } elseif (!isset($hash['fax'])) { $hash['fax'] = $item['value']; } } elseif (isset($item['params']['PAGER']) && !isset($hash['pager'])) { $hash['pager'] = $item['value']; } elseif (isset($item['params']['TYPE'])) { if (!is_array($item['params']['TYPE'])) { $item['params']['TYPE'] = array($item['params']['TYPE']); } foreach ($item['params']['TYPE'] as &$type) { $type = Horde_String::upper($type); } // For vCard 3.0. if (in_array('CELL', $item['params']['TYPE'])) { if (in_array('HOME', $item['params']['TYPE']) && !isset($hash['homeCellPhone'])) { $hash['homeCellPhone'] = $item['value']; } elseif (in_array('WORK', $item['params']['TYPE']) && !isset($hash['workCellPhone'])) { $hash['workCellPhone'] = $item['value']; } elseif (!isset($hash['cellPhone'])) { $hash['cellPhone'] = $item['value']; } } elseif (in_array('FAX', $item['params']['TYPE'])) { if (in_array('HOME', $item['params']['TYPE']) && !isset($hash['homeFax'])) { $hash['homeFax'] = $item['value']; } elseif (in_array('WORK', $item['params']['TYPE']) && !isset($hash['workFax'])) { $hash['workFax'] = $item['value']; } elseif (!isset($hash['fax'])) { $hash['fax'] = $item['value']; } } elseif (in_array('VIDEO', $item['params']['TYPE'])) { if (in_array('HOME', $item['params']['TYPE']) && !isset($hash['homeVideoCall'])) { $hash['homeVideoCall'] = $item['value']; } elseif (in_array('WORK', $item['params']['TYPE']) && !isset($hash['workVideoCall'])) { $hash['workVideoCall'] = $item['value']; } elseif (!isset($hash['videoCall'])) { $hash['videoCall'] = $item['value']; } } elseif (in_array('PAGER', $item['params']['TYPE']) && !isset($hash['pager'])) { $hash['pager'] = $item['value']; } elseif (in_array('WORK', $item['params']['TYPE']) && !isset($hash['workPhone'])) { $hash['workPhone'] = $item['value']; } elseif (in_array('HOME', $item['params']['TYPE']) && !isset($hash['homePhone'])) { $hash['homePhone'] = $item['value']; } elseif (!isset($hash['phone'])) { $hash['phone'] = $item['value']; } } elseif (isset($item['params']['CELL'])) { if (isset($item['params']['WORK']) && !isset($hash['workCellPhone'])) { $hash['workCellPhone'] = $item['value']; } elseif (isset($item['params']['HOME']) && !isset($hash['homeCellPhone'])) { $hash['homeCellPhone'] = $item['value']; } elseif (!isset($hash['cellPhone'])) { $hash['cellPhone'] = $item['value']; } } elseif (isset($item['params']['VIDEO'])) { if (isset($item['params']['WORK']) && !isset($hash['workVideoCall'])) { $hash['workVideoCall'] = $item['value']; } elseif (isset($item['params']['HOME']) && !isset($hash['homeVideoCall'])) { $hash['homeVideoCall'] = $item['value']; } elseif (!isset($hash['videoCall'])) { $hash['videoCall'] = $item['value']; } } else { if (isset($item['params']['WORK']) && !isset($hash['workPhone'])) { $hash['workPhone'] = $item['value']; } elseif (isset($item['params']['HOME']) && !isset($hash['homePhone'])) { $hash['homePhone'] = $item['value']; } else { $hash['phone'] = $item['value']; } } break; case 'EMAIL': $email_set = false; if (isset($item['params']['HOME']) && !empty($this->map['homeEmail']) && (!isset($hash['homeEmail']) || isset($item['params']['PREF']))) { $e = Horde_Icalendar_Vcard::getBareEmail($item['value']); $hash['homeEmail'] = $e ? $e : ''; $email_set = true; } elseif (isset($item['params']['WORK']) && !empty($this->map['workEmail']) && (!isset($hash['workEmail']) || isset($item['params']['PREF']))) { $e = Horde_Icalendar_Vcard::getBareEmail($item['value']); $hash['workEmail'] = $e ? $e : ''; $email_set = true; } elseif (isset($item['params']['TYPE'])) { if (!is_array($item['params']['TYPE'])) { $item['params']['TYPE'] = array($item['params']['TYPE']); } foreach ($item['params']['TYPE'] as &$type) { $type = Horde_String::upper($type); } if (in_array('HOME', $item['params']['TYPE']) && !empty($this->map['homeEmail']) && (!isset($hash['homeEmail']) || in_array('PREF', $item['params']['TYPE']))) { $e = Horde_Icalendar_Vcard::getBareEmail($item['value']); $hash['homeEmail'] = $e ? $e : ''; $email_set = true; } elseif (in_array('WORK', $item['params']['TYPE']) && !empty($this->map['workEmail']) && (!isset($hash['workEmail']) || in_array('PREF', $item['params']['TYPE']))) { $e = Horde_Icalendar_Vcard::getBareEmail($item['value']); $hash['workEmail'] = $e ? $e : ''; $email_set = true; } } if (!$email_set && (!isset($hash['email']) || isset($item['params']['PREF']) || (!empty($item['params']['TYPE']) && is_array($item['params']['TYPE']) && in_array('PREF', $item['params']['TYPE'])))) { $e = Horde_Icalendar_Vcard::getBareEmail($item['value']); $hash['email'] = $e ? $e : ''; } if (!isset($hash['emails'])) { $hash['emails'] = ''; } if ($e = Horde_Icalendar_Vcard::getBareEmail($item['value'])) { if (strlen($hash['emails'])) { $hash['emails'] .= ','; } $hash['emails'] .= $e; } break; case 'TITLE': $hash['title'] = $item['value']; break; case 'ROLE': $hash['role'] = $item['value']; break; case 'ORG': // The VCARD 2.1 specification requires the presence of two // SEMI-COLON separated fields: Organizational Name and // Organizational Unit. Additional fields are optional. $hash['company'] = !empty($item['values'][0]) ? $item['values'][0] : ''; $hash['department'] = !empty($item['values'][1]) ? $item['values'][1] : ''; break; case 'NOTE': $hash['notes'] = $item['value']; break; case 'CATEGORIES': $hash['businessCategory'] = $item['value']; $hash['__tags'] = $item['values']; break; case 'URL': if (isset($item['params']['HOME']) && !isset($hash['homeWebsite'])) { $hash['homeWebsite'] = $item['value']; } elseif (isset($item['params']['WORK']) && !isset($hash['workWebsite'])) { $hash['workWebsite'] = $item['value']; } elseif (!isset($hash['website'])) { $hash['website'] = $item['value']; } break; case 'BDAY': if (empty($item['value'])) { $hash['birthday'] = null; } else { $hash['birthday'] = $item['value']['year'] . '-' . $item['value']['month'] . '-' . $item['value']['mday']; } break; case 'PHOTO': case 'LOGO': if (isset($item['params']['VALUE']) && Horde_String::lower($item['params']['VALUE']) == 'uri') { // No support for URIs yet. break; } if (!isset($item['params']['ENCODING']) || (Horde_String::lower($item['params']['ENCODING']) != 'b' && Horde_String::upper($item['params']['ENCODING']) != 'BASE64')) { // Invalid property. break; } $type = Horde_String::lower($item['name']); $hash[$type] = base64_decode($item['value']); if (isset($item['params']['TYPE'])) { $hash[$type . 'type'] = $item['params']['TYPE']; } break; case 'X-SIP': if (isset($item['params']['POC']) && !isset($hash['ptt'])) { $hash['ptt'] = $item['value']; } elseif (isset($item['params']['VOIP']) && !isset($hash['voip'])) { $hash['voip'] = $item['value']; } elseif (isset($item['params']['SWIS']) && !isset($hash['shareView'])) { $hash['shareView'] = $item['value']; } elseif (!isset($hash['sip'])) { $hash['sip'] = $item['value']; } break; case 'X-WV-ID': $hash['imaddress'] = $item['value']; break; case 'X-ANNIVERSARY': if (empty($item['value'])) { $hash['anniversary'] = null; } else { $hash['anniversary'] = $item['value']['year'] . '-' . $item['value']['month'] . '-' . $item['value']['mday']; } break; case 'X-CHILDREN': $hash['children'] = $item['value']; break; case 'X-SPOUSE': $hash['spouse'] = $item['value']; break; } } /* Ensure we have a valid name field. */ $hash = $this->_parseName($hash); return $hash; } /** * Convert the contact to an ActiveSync contact message * * @param Turba_Object $object The turba object to convert * @param array $options Options: * - protocolversion: (float) The EAS version to support * DEFAULT: 2.5 * - bodyprefs: (array) A BODYPREFERENCE array. * DEFAULT: none (No body prefs enforced). * - truncation: (integer) Truncate event body to this length * DEFAULT: none (No truncation). * - device: (Horde_ActiveSync_Device) The device object. * * @return Horde_ActiveSync_Message_Contact */ public function toASContact(Turba_Object $object, array $options = array()) { global $injector; $message = new Horde_ActiveSync_Message_Contact(array( 'logger' => $injector->getInstance('Horde_Log_Logger'), 'protocolversion' => $options['protocolversion'], 'device' => !empty($options['device']) ? $options['device'] : null )); $hash = $object->getAttributes(); if (!isset($hash['lastname']) && isset($hash['name'])) { $this->_guessName($hash); } // Ensure we have at least a good guess as to separate address fields. // Not ideal, but EAS does not have a single "address" field so we must // map "common" to either home or work. I choose home since // work/non-personal installs will be more likely to have separated // address fields. if (!empty($hash['commonAddress'])) { if (!isset($hash['commonStreet'])) { $hash['commonStreet'] = $hash['commonHome']; } foreach (array('Address', 'Street', 'POBox', 'Extended', 'City', 'Province', 'PostalCode', 'Country') as $field) { $hash['home' . $field] = $hash['common' . $field]; } } else { if (isset($hash['homeAddress']) && !isset($hash['homeStreet'])) { $hash['homeStreet'] = $hash['homeAddress']; } if (isset($hash['workAddress']) && !isset($hash['workStreet'])) { $hash['workStreet'] = $hash['workAddress']; } } $hooks = $injector->getInstance('Horde_Core_Hooks'); $decode_hook = $hooks->hookExists('decode_attribute', 'turba'); foreach ($hash as $field => $value) { if ($decode_hook) { try { $value = $hooks->callHook( 'decode_attribute', 'turba', array($field, $value, $object) ); } catch (Turba_Exception $e) { Horde::log($e); } } if (isset(self::$_asMap[$field])) { try { $message->{self::$_asMap[$field]} = $value; } catch (InvalidArgumentException $e) { } continue; } switch ($field) { case 'photo': $message->picture = base64_encode($value); break; case 'homeCountry': $message->homecountry = !empty($hash['homeCountryFree']) ? $hash['homeCountryFree'] : (!empty($hash['homeCountry']) ? Horde_Nls::getCountryISO($hash['homeCountry']) : null); break; case 'otherCountry': $message->othercountry = !empty($hash['otherCountryFree']) ? $hash['otherCountryFree'] : (!empty($hash['otherCountry']) ? Horde_Nls::getCountryISO($hash['otherCountry']) : null); break; case 'workCountry': $message->businesscountry = !empty($hash['workCountryFree']) ? $hash['workCountryFree'] : (!empty($hash['workCountry']) ? Horde_Nls::getCountryISO($hash['workCountry']) : null); break; case 'email': $message->email1address = $value; break; case 'homeEmail': $message->email2address = $value; break; case 'workEmail': $message->email3address = $value; break; case 'emails': $address = 1; foreach (explode(',', $value) as $email) { while ($address <= 3 && $message->{'email' . $address . 'address'}) { $address++; } if ($address > 3) { break; } $message->{'email' . $address . 'address'} = $email; $address++; } break; case 'children': // Children FROM horde are a simple string value. Even though EAS // uses an array stucture to pass them, we pass as a single // string since we can't assure what delimter the user will // use and (at least in some languages) a comma can be used // within a full name. $message->children = array($value); break; case 'notes': if (strlen($value) && $options['protocolversion'] > Horde_ActiveSync::VERSION_TWOFIVE) { $bp = $options['bodyprefs']; $note = new Horde_ActiveSync_Message_AirSyncBaseBody(); // No HTML supported in Turba's notes. Always use plaintext. $note->type = Horde_ActiveSync::BODYPREF_TYPE_PLAIN; if (isset($bp[Horde_ActiveSync::BODYPREF_TYPE_PLAIN]['truncationsize']) && Horde_String::length($value) > $bp[Horde_ActiveSync::BODYPREF_TYPE_PLAIN]['truncationsize']) { $note->data = Horde_String::substr($value, 0, $bp[Horde_ActiveSync::BODYPREF_TYPE_PLAIN]['truncationsize']); $note->truncated = 1; } else { $note->data = $value; } $note->estimateddatasize = Horde_String::length($value); $message->airsyncbasebody = $note; } elseif (strlen($value)) { // EAS 2.5 $message->body = $value; $message->bodysize = strlen($message->body); $message->bodytruncated = 0; } break; case 'birthday': case 'anniversary': if (!empty($value) && $value != '0000-00-00') { try { $date = new Horde_Date($value); } catch (Horde_Date_Exception $e) { $message->$field = null; } // Some sanity checking to make sure the date was // successfully parsed. if ($date->month != 0) { $message->$field = $date; } else { $message->$field = null; } } else { $message->$field = null; } break; } } /* Get tags. */ $message->categories = $injector->getInstance('Turba_Tagger') ->split($object->getValue('__tags')); if (empty($this->fileas)) { $message->fileas = Turba::formatName($object); } return $message; } /** * Convert an ActiveSync contact message into a hash suitable for * importing via self::add(). * * @param Horde_ActiveSync_Message_Contact $message The contact message * object. * * @return array A contact hash. */ public function fromASContact(Horde_ActiveSync_Message_Contact $message) { $hash = array(); foreach (self::$_asMap as $turbaField => $asField) { if (!$message->isGhosted($asField)) { try { $hash[$turbaField] = $message->{$asField}; } catch (InvalidArgumentException $e) { } } } // Try our best to get a name attribute; $hash = $this->_parseName($hash); /* Requires special handling */ try { if ($message->getProtocolVersion() >= Horde_ActiveSync::VERSION_TWELVE) { if (!empty($message->airsyncbasebody)) { $hash['notes'] = $message->airsyncbasebody->data; } } else { $hash['notes'] = $message->body; } } catch (InvalidArgumentException $e) {} // picture ($message->picture *should* already be base64 encdoed) if (!$message->isGhosted('picture')) { $hash['photo'] = base64_decode($message->picture); } /* Email addresses */ $hash['emails'] = array(); if (!$message->isGhosted('email1address')) { $e = Horde_Icalendar_Vcard::getBareEmail($message->email1address); $hash['emails'][] = $hash['email'] = $e ? $e : ''; } if (!$message->isGhosted('email2address')) { $e = Horde_Icalendar_Vcard::getBareEmail($message->email2address); $hash['emails'][] = $hash['homeEmail'] = $e ? $e : ''; } if (!$message->isGhosted('email3address')) { $e = Horde_Icalendar_Vcard::getBareEmail($message->email3address); $hash['emails'][] = $hash['workEmail'] = $e ? $e : ''; } $hash['emails'] = implode(',', $hash['emails']); /* Categories */ if (!$message->isGhosted('categories') && empty($message->categories)) { $hash['__tags'] = array(); } elseif (is_array($message->categories) && count($message->categories)) { $hash['__tags'] = $message->categories; } /* Children */ if (is_array($message->children) && count($message->children)) { // We use a comma as incoming delimiter as it's the most // common even though it might be used withing a name string. $hash['children'] = implode(', ', $message->children); } elseif (!$message->isGhosted('children')) { $hash['children'] = ''; } /* Birthday and Anniversary */ if (!empty($message->birthday)) { $bday = new Horde_Date($message->birthday); $bday->setTimezone(date_default_timezone_get()); $hash['birthday'] = $bday->format('Y-m-d'); } elseif (!$message->isGhosted('birthday')) { $hash['birthday'] = ''; } if (!empty($message->anniversary)) { $anniversary = new Horde_Date($message->anniversary); $anniversary->setTimezone(date_default_timezone_get()); $hash['anniversary'] = $anniversary->format('Y-m-d'); } elseif (!$message->isGhosted('anniversary')) { $hash['anniversary'] = ''; } /* Countries */ include 'Horde/Nls/Countries.php'; if (!empty($message->homecountry)) { if (!empty($this->map['homeCountryFree'])) { $hash['homeCountryFree'] = $message->homecountry; } else { $country = array_search($message->homecountry, $countries); if ($country === false) { $country = $message->homecountry; } $hash['homeCountry'] = $country; } } elseif (!$message->isGhosted('homecountry')) { $hash['homeCountry'] = ''; } if (!empty($message->businesscountry)) { if (!empty($this->map['workCountryFree'])) { $hash['workCountryFree'] = $message->businesscountry; } else { $country = array_search($message->businesscountry, $countries); if ($country === false) { $country = $message->businesscountry; } $hash['workCountry'] = $country; } } elseif (!$message->isGhosted('businesscountry')) { $hash['workCountry'] = ''; } if (!empty($message->othercountry)) { if (!empty($this->map['otherCountryFree'])) { $hash['otherCountryFree'] = $message->othercountry; } else { $country = array_search($message->othercountry, $countries); if ($country === false) { $country = $message->othercountry; } $hash['otherCountry'] = $country; } } elseif (!$message->isGhosted('othercountry')) { $hash['otherCountry'] = ''; } return $hash; } /** * Checks $hash for the presence of a 'name' attribute. If not found, * attempt to build one from other available values. * * @param array $hash A hash of turba attributes. * * @return array Hash of Turba attributes, with the 'name' attribute * populated. */ protected function _parseName(array $hash) { if (empty($hash['name'])) { /* If name is a composite field, it won't be present in the * $this->fields array, so check for that as well. */ if (isset($this->map['name']) && is_array($this->map['name']) && !empty($this->map['name']['attribute'])) { $fieldarray = array(); foreach ($this->map['name']['fields'] as $mapfields) { $fieldarray[] = isset($hash[$mapfields]) ? $hash[$mapfields] : ''; } $hash['name'] = Turba::formatCompositeField($this->map['name']['format'], $fieldarray); } else { $hash['name'] = isset($hash['firstname']) ? $hash['firstname'] : ''; if (!empty($hash['lastname'])) { $hash['name'] .= ' ' . $hash['lastname']; } $hash['name'] = trim($hash['name']); } } return $hash; } /** * Checks if the current user has the requested permissions on this * address book. * * @param integer $perm The permission to check for. * * @return boolean True if the user has permission, otherwise false. */ public function hasPermission($perm) { $perms = $GLOBALS['injector']->getInstance('Horde_Perms'); return $perms->exists('turba:sources:' . $this->_name) ? $perms->hasPermission('turba:sources:' . $this->_name, $GLOBALS['registry']->getAuth(), $perm) // Assume we have permissions if they're not explicitly set. : true; } /** * Return the name of this address book. * (This is the key into the cfgSources array) * * @string Address book name */ public function getName() { return $this->_name; } /** * Return the owner to use when searching or creating contacts in * this address book. * * @return string Contact owner. */ public function getContactOwner() { return empty($this->_contact_owner) ? $this->_getContactOwner() : $this->_contact_owner; } /** * Override the contactOwner setting for this driver. * * @param string $owner The contact owner. */ public function setContactOwner($owner) { $this->_contact_owner = $owner; } /** * Override the name setting for this driver. * * @param string $name The source name. This is the key into the * $cfgSources array. */ public function setSourceName($name) { $this->_name = $name; } /** * Return the owner to use when searching or creating contacts in * this address book. * * @return string Contact owner. */ protected function _getContactOwner() { return $GLOBALS['registry']->getAuth(); } /** * Creates a new Horde_Share for this source type. * * @param string $share_name The share name * @param array $params The params for the share. * * @return Horde_Share The share object. */ public function createShare($share_name, array $params) { // If the raw address book name is not set, use the share name if (empty($params['params']['name'])) { $params['params']['name'] = $share_name; } return Turba::createShare($share_name, $params); } /** * Runs any actions after setting a new default tasklist. * * @param string $share The default share ID. */ public function setDefaultShare($share) { } /** * Creates an object key for a new object. * * @param array $attributes The attributes (in driver keys) of the * object being added. * * @return string A unique ID for the new object. */ protected function _makeKey(array $attributes) { return hash('md5', mt_rand()); } /** * Creates an object UID for a new object. * * @return string A unique ID for the new object. */ protected function _makeUid() { return strval(new Horde_Support_Guid()); } /** * Initialize the driver. * * @throws Turba_Exception */ protected function _init() { } /** * Searches the address book with the given criteria and returns a * filtered list of results. If the criteria parameter is an empty array, * all records will be returned. * * @param array $criteria Array containing the search criteria. * @param array $fields List of fields to return. * @param array $blobFields Array of fields containing binary data. * @param boolean $count_only Only return the count of matching entries, * not the entries themselves. * * @return array Hash containing the search results. * @throws Turba_Exception */ protected function _search(array $criteria, array $fields, array $blobFields = array(), $count_only = false) { throw new Turba_Exception(_("Searching is not available.")); } /** * Reads the given data from the address book and returns the results. * * @param string $key The primary key field to use. * @param mixed $ids The ids of the contacts to load. * @param string $owner Only return contacts owned by this user. * @param array $fields List of fields to return. * @param array $blobFields Array of fields containing binary data. * @param array $dateFields Array of fields containing date data. * @since 4.2.0 * * @return array Hash containing the search results. * @throws Turba_Exception */ protected function _read($key, $ids, $owner, array $fields, array $blobFields = array(), array $dateFields = array()) { throw new Turba_Exception(_("Reading contacts is not available.")); } /** * Adds the specified contact to the addressbook. * * @param array $attributes The attribute values of the contact. * @param array $blob_fields Fields that represent binary data. * @param array $date_fields Fields that represent dates. @since 4.2.0 * * @throws Turba_Exception */ protected function _add(array $attributes, array $blob_fields = array(), array $date_fields = array()) { throw new Turba_Exception(_("Adding contacts is not available.")); } /** * Deletes the specified contact from the addressbook. * * @param string $object_key TODO * @param string $object_id TODO * * @throws Turba_Exception */ protected function _delete($object_key, $object_id) { throw new Turba_Exception(_("Deleting contacts is not available.")); } /** * Saves the specified object in the SQL database. * * @param Turba_Object $object The object to save * * @return string The object id, possibly updated. * @throws Turba_Exception */ protected function _save(Turba_Object $object) { throw new Turba_Exception(_("Saving contacts is not available.")); } /** * Remove all entries owned by the specified user. * * @param string $user The user's data to remove. * * @throws Turba_Exception */ public function removeUserData($user) { throw new Turba_Exception_NotSupported(_("Removing user data is not supported in the current address book storage driver.")); } /** * Check if the passed in share is the default share for this source. * * @param Horde_Share_Object $share The share object. * @param array $srcconfig The cfgSource entry for the share. * * @return boolean TODO */ public function checkDefaultShare(Horde_Share_Object $share, array $srcconfig) { $params = @unserialize($share->get('params')); if (!isset($params['default'])) { $params['default'] = ($params['name'] == $GLOBALS['registry']->getAuth()); $share->set('params', serialize($params)); $share->save(); } return $params['default']; } /* Countable methods. */ /** * Returns the number of contacts of the current user in this address book. * * @return integer The number of contacts that the user owns. * @throws Turba_Exception */ public function count() { if (is_null($this->_count)) { $this->_count = count( $this->_search(array('AND' => array( array('field' => $this->toDriver('__owner'), 'op' => '=', 'test' => $this->getContactOwner()))), array($this->toDriver('__key'))) ); } return $this->_count; } /** * Helper function for guessing name parts from a single name string. * * @param array $hash The attributes array. */ protected function _guessName(&$hash) { if (($pos = strpos($hash['name'], ',')) !== false) { // Assume Last, First $hash['lastname'] = Horde_String::substr($hash['name'], 0, $pos); $hash['firstname'] = trim(Horde_String::substr($hash['name'], $pos + 1)); } elseif (($pos = Horde_String::rpos($hash['name'], ' ')) !== false) { // Assume everything after last space as lastname $hash['lastname'] = trim(Horde_String::substr($hash['name'], $pos + 1)); $hash['firstname'] = Horde_String::substr($hash['name'], 0, $pos); } else { $hash['lastname'] = $hash['name']; $hash['firstname'] = ''; } } }
Simpan