From: Michael J. Rubinsky Date: Thu, 6 May 2010 20:58:40 +0000 (-0400) Subject: Add support for searching the GAL via activesync devices X-Git-Url: https://git.internetallee.de/?a=commitdiff_plain;h=ef8adb7217fc922f68828ae7db4bf5a75c860b2d;p=horde.git Add support for searching the GAL via activesync devices --- diff --git a/framework/ActiveSync/lib/Horde/ActiveSync.php b/framework/ActiveSync/lib/Horde/ActiveSync.php index d6b8c661d..fa68ee8f1 100644 --- a/framework/ActiveSync/lib/Horde/ActiveSync.php +++ b/framework/ActiveSync/lib/Horde/ActiveSync.php @@ -150,43 +150,6 @@ define("SYNC_VALIDATECERT_CERTIFICATECHAIN","ValidateCert:CertificateChain"); define("SYNC_VALIDATECERT_CHECKCRL","ValidateCert:CheckCRL"); define("SYNC_VALIDATECERT_STATUS","ValidateCert:Status"); -//Search -define("SYNC_SEARCH_SEARCH", "Search:Search"); -define("SYNC_SEARCH_STORE", "Search:Store"); -define("SYNC_SEARCH_NAME", "Search:Name"); -define("SYNC_SEARCH_QUERY", "Search:Query"); -define("SYNC_SEARCH_OPTIONS", "Search:Options"); -define("SYNC_SEARCH_RANGE", "Search:Range"); -define("SYNC_SEARCH_STATUS", "Search:Status"); -define("SYNC_SEARCH_RESPONSE", "Search:Response"); -define("SYNC_SEARCH_RESULT", "Search:Result"); -define("SYNC_SEARCH_PROPERTIES", "Search:Properties"); -define("SYNC_SEARCH_TOTAL", "Search:Total"); -define("SYNC_SEARCH_EQUALTO", "Search:EqualTo"); -define("SYNC_SEARCH_VALUE", "Search:Value"); -define("SYNC_SEARCH_AND", "Search:And"); -define("SYNC_SEARCH_OR", "Search:Or"); -define("SYNC_SEARCH_FREETEXT", "Search:FreeText"); -define("SYNC_SEARCH_DEEPTRAVERSAL", "Search:DeepTraversal"); -define("SYNC_SEARCH_LONGID", "Search:LongId"); -define("SYNC_SEARCH_REBUILDRESULTS", "Search:RebuildResults"); -define("SYNC_SEARCH_LESSTHAN", "Search:LessThan"); -define("SYNC_SEARCH_GREATERTHAN", "Search:GreaterThan"); -define("SYNC_SEARCH_SCHEMA", "Search:Schema"); -define("SYNC_SEARCH_SUPPORTED", "Search:Supported"); - -//GAL -define("SYNC_GAL_DISPLAYNAME", "GAL:DisplayName"); -define("SYNC_GAL_PHONE", "GAL:Phone"); -define("SYNC_GAL_OFFICE", "GAL:Office"); -define("SYNC_GAL_TITLE", "GAL:Title"); -define("SYNC_GAL_COMPANY", "GAL:Company"); -define("SYNC_GAL_ALIAS", "GAL:Alias"); -define("SYNC_GAL_FIRSTNAME", "GAL:FirstName"); -define("SYNC_GAL_LASTNAME", "GAL:LastName"); -define("SYNC_GAL_HOMEPHONE", "GAL:HomePhone"); -define("SYNC_GAL_MOBILEPHONE", "GAL:MobilePhone"); -define("SYNC_GAL_EMAILADDRESS", "GAL:EmailAddress"); /** * Main ActiveSync class. Entry point for performing all ActiveSync operations @@ -279,7 +242,6 @@ class Horde_ActiveSync const FLAG_NEWMESSAGE = 'NewMessage'; /* Folder types */ - // Other constants const FOLDER_TYPE_OTHER = 1; const FOLDER_TYPE_INBOX = 2; const FOLDER_TYPE_DRAFTS = 3; @@ -301,15 +263,30 @@ class Horde_ActiveSync const FOLDER_TYPE_RECIPIENT_CACHE = 19; const FOLDER_TYPE_DUMMY = '__dummy.Folder.Id__'; + /** Origin of changes **/ const CHANGE_ORIGIN_PIM = 0; const CHANGE_ORIGIN_SERVER = 1; const CHANGE_ORIGIN_NA = 3; + /** Remote wipe **/ const RWSTATUS_NA = 0; const RWSTATUS_OK = 1; const RWSTATUS_PENDING = 2; const RWSTATUS_WIPED = 3; + /** GAL **/ + const GAL_DISPLAYNAME = 'GAL:DisplayName'; + const GAL_PHONE = 'GAL:Phone'; + const GAL_OFFICE = 'GAL:Office'; + const GAL_TITLE = 'GAL:Title'; + const GAL_COMPANY = 'GAL:Company'; + const GAL_ALIAS = 'GAL:Alias'; + const GAL_FIRSTNAME = 'GAL:FirstName'; + const GAL_LASTNAME = 'GAL:LastName'; + const GAL_HOMEPHONE = 'GAL:HomePhone'; + const GAL_MOBILEPHONE = 'GAL:MobilePhone'; + const GAL_EMAILADDRESS = 'GAL:EmailAddress'; + /** * Logger * @@ -850,137 +827,6 @@ class Horde_ActiveSync } /** - * @param $devid - * @param $protocolversion - * @return unknown_type - */ - public function handleSearch($devid, $protocolversion) - { - $searchrange = '0'; - if (!$this->_decoder->getElementStartTag(SYNC_SEARCH_SEARCH)) { - return false; - } - - if (!$this->_decoder->getElementStartTag(SYNC_SEARCH_STORE)) { - return false; - } - - if (!$this->_decoder->getElementStartTag(SYNC_SEARCH_NAME)) { - return false; - } - $searchname = $this->_decoder->getElementContent(); - if (!$this->_decoder->getElementEndTag()) { - return false; - } - - if (!$this->_decoder->getElementStartTag(SYNC_SEARCH_QUERY)) { - return false; - } - $searchquery = $this->_decoder->getElementContent(); - if (!$this->_decoder->getElementEndTag()) { - return false; - } - - if ($this->_decoder->getElementStartTag(SYNC_SEARCH_OPTIONS)) { - while(1) { - if ($this->_decoder->getElementStartTag(SYNC_SEARCH_RANGE)) { - $searchrange = $this->_decoder->getElementContent(); - if (!$this->_decoder->getElementEndTag()) { - return false; - } - } - $e = $this->_decoder->peek(); - if ($e[Horde_ActiveSync_Wbxml::EN_TYPE] == Horde_ActiveSync_Wbxml::EN_TYPE_ENDTAG) { - $this->_decoder->getElementEndTag(); - break; - } - } - } - if (!$this->_decoder->getElementEndTag()) {//store - return false; - } - - if (!$this->_decoder->getElementEndTag()) {//search - return false; - } - - if (strtoupper($searchname) != "GAL") { - $this->_logger->err('Searchtype ' . $searchname . 'is not supported'); - return false; - } - //get search results from backend - $rows = $this->_driver->getSearchResults($searchquery, $searchrange); - - $this->_encoder->startWBXML(); - $this->_encoder->startTag(SYNC_SEARCH_SEARCH); - - $this->_encoder->startTag(SYNC_SEARCH_STATUS); - $this->_encoder->content(1); - $this->_encoder->endTag(); - - $this->_encoder->startTag(SYNC_SEARCH_RESPONSE); - $this->_encoder->startTag(SYNC_SEARCH_STORE); - - $this->_encoder->startTag(SYNC_SEARCH_STATUS); - $this->_encoder->content(1); - $this->_encoder->endTag(); - - if (is_array($rows) && !empty($rows)) { - $searchrange = $rows['range']; - unset($rows['range']); - foreach ($rows as $u) { - $this->_encoder->startTag(SYNC_SEARCH_RESULT); - $this->_encoder->startTag(SYNC_SEARCH_PROPERTIES); - - $this->_encoder->startTag(SYNC_GAL_DISPLAYNAME); - $this->_encoder->content($u["fullname"]); - $this->_encoder->endTag(); - - $this->_encoder->startTag(SYNC_GAL_PHONE); - $this->_encoder->content($u["businessphone"]); - $this->_encoder->endTag(); - - $this->_encoder->startTag(SYNC_GAL_ALIAS); - $this->_encoder->content($u["username"]); - $this->_encoder->endTag(); - - //it's not possible not get first and last name of an user - //from the gab and user functions, so we just set fullname - //to lastname and leave firstname empty because nokia needs - //first and lastname in order to display the search result - $this->_encoder->startTag(SYNC_GAL_FIRSTNAME); - $this->_encoder->content(""); - $this->_encoder->endTag(); - - $this->_encoder->startTag(SYNC_GAL_LASTNAME); - $this->_encoder->content($u["fullname"]); - $this->_encoder->endTag(); - - $this->_encoder->startTag(SYNC_GAL_EMAILADDRESS); - $this->_encoder->content($u["emailaddress"]); - $this->_encoder->endTag(); - - $this->_encoder->endTag();//result - $this->_encoder->endTag();//properties - } - $this->_encoder->startTag(SYNC_SEARCH_RANGE); - $this->_encoder->content($searchrange); - $this->_encoder->endTag(); - - $this->_encoder->startTag(SYNC_SEARCH_TOTAL); - $this->_encoder->content(count($rows)); - $this->_encoder->endTag(); - } - - $this->_encoder->endTag();//store - $this->_encoder->endTag();//response - $this->_encoder->endTag();//search - - - return true; - } - - /** * The heart of the server. Dispatch a request to the request object to * handle. * diff --git a/framework/ActiveSync/lib/Horde/ActiveSync/Driver/Base.php b/framework/ActiveSync/lib/Horde/ActiveSync/Driver/Base.php index 718cd1f1e..9d4086597 100644 --- a/framework/ActiveSync/lib/Horde/ActiveSync/Driver/Base.php +++ b/framework/ActiveSync/lib/Horde/ActiveSync/Driver/Base.php @@ -519,11 +519,12 @@ abstract class Horde_ActiveSync_Driver_Base /** * Returns array of items which contain contact information * - * @param string $searchquery + * @param string $query + * @param string $range * * @return array */ - public function getSearchResults($searchquery) + public function getSearchResults($query, $range) { throw new Horde_ActiveSync_Exception('getSearchResults not implemented.'); } diff --git a/framework/ActiveSync/lib/Horde/ActiveSync/Driver/Horde.php b/framework/ActiveSync/lib/Horde/ActiveSync/Driver/Horde.php index f9148196d..9d6a9c535 100644 --- a/framework/ActiveSync/lib/Horde/ActiveSync/Driver/Horde.php +++ b/framework/ActiveSync/lib/Horde/ActiveSync/Driver/Horde.php @@ -574,14 +574,49 @@ class Horde_ActiveSync_Driver_Horde extends Horde_ActiveSync_Driver_Base /** * Returns array of items which contain contact information * - * @param string $searchquery + * @param string $query The text string to match against any textual ANR + * (Automatic Name Resolution) properties. Exchange's + * searchable ANR properties are currently: + * firstname, lastname, alias, displayname, email + * @param string $range The range to return (for example, 1-50). * - * @return array + * @return array with 'rows' and 'range' keys */ - public function getSearchResults($searchquery) + public function getSearchResults($query, $range) { - $this->_logger->err('getSearchResults not yet implemented'); - return array(); + /* Get results */ + $return = array('rows' => array(), + 'range' => $range); + try { + $results = $this->_connector->contacts_search($query); + } catch (Horde_ActiveSync_Exception $e) { + $this->_logger->err($e->getMessage()); + return $return; + } + + /* Honor range */ + $count = count($results); + + $this->_logger->info('Horde::getSearchResults found ' . $count . ' matches.'); + + preg_match('/(.*)\-(.*)/', $range, $matches); + $return_count = $matches[2] - $matches[1]; + $rows = array_slice($results, $matches[1], $return_count + 1, true); + $rows = array_pop($rows); + foreach ($rows as $row) { + $return['rows'][] = array( + Horde_ActiveSync::GAL_ALIAS => $row['alias'], + Horde_ActiveSync::GAL_DISPLAYNAME => $row['name'], + Horde_ActiveSync::GAL_EMAILADDRESS => $row['email'], + Horde_ActiveSync::GAL_FIRSTNAME => $row['firstname'], + Horde_ActiveSync::GAL_LASTNAME => $row['lastname'], + Horde_ActiveSync::GAL_COMPANY => $row['company'], + Horde_ActiveSync::GAL_HOMEPHONE => $row['homePhone'], + Horde_ActiveSync::GAL_PHONE => $row['workPhone'] + ); + } + + return $return; } /** diff --git a/framework/ActiveSync/lib/Horde/ActiveSync/Driver/Horde/Connector/Registry.php b/framework/ActiveSync/lib/Horde/ActiveSync/Driver/Horde/Connector/Registry.php index 267240293..b9a1ca076 100644 --- a/framework/ActiveSync/lib/Horde/ActiveSync/Driver/Horde/Connector/Registry.php +++ b/framework/ActiveSync/lib/Horde/ActiveSync/Driver/Horde/Connector/Registry.php @@ -227,6 +227,7 @@ class Horde_ActiveSync_Driver_Horde_Connector_Registry * @param string $action The action to check for (add, modify, delete) * @param integer $from_ts The timestamp to start checking from * @param integer $to_ts The ending timestamp + * * @return array An array of event uids */ public function contacts_listBy($action, $from_ts, $to_ts) @@ -234,6 +235,23 @@ class Horde_ActiveSync_Driver_Horde_Connector_Registry return $this->_registry->contacts->listBy($action, $from_ts, null, $to_ts); } + public function contacts_search($query) + { + $gal = $this->contacts_getGal(); + $fields = array($gal => array('firstname', 'lastname', 'alias', 'name', 'email')); + return $this->_registry->contacts->search(array($query), array($gal), $fields, true, true); + } + + /** + * Get the GAL source uid. + * + * @return string | boolean + */ + public function contacts_getGal() + { + return $this->_registry->contacts->getGalUid(); + } + /** * List all tasks in the user's default tasklist. * diff --git a/framework/ActiveSync/lib/Horde/ActiveSync/Request/Search.php b/framework/ActiveSync/lib/Horde/ActiveSync/Request/Search.php new file mode 100644 index 000000000..979eb5218 --- /dev/null +++ b/framework/ActiveSync/lib/Horde/ActiveSync/Request/Search.php @@ -0,0 +1,208 @@ + + * @package Horde_ActiveSync + */ +/** + * Zarafa Deutschland GmbH, www.zarafaserver.de + * This file is distributed under GPL v2. + * Consult LICENSE file for details + */ +class Horde_ActiveSync_Request_Search extends Horde_ActiveSync_Request_Base +{ + + /** Search code page **/ + const SEARCH_SEARCH = 'Search:Search'; + const SEARCH_STORE = 'Search:Store'; + const SEARCH_NAME = 'Search:Name'; + const SEARCH_QUERY = 'Search:Query'; + const SEARCH_OPTIONS = 'Search:Options'; + const SEARCH_RANGE = 'Search:Range'; + const SEARCH_STATUS = 'Search:Status'; + const SEARCH_RESPONSE = 'Search:Response'; + const SEARCH_RESULT = 'Search:Result'; + const SEARCH_PROPERTIES = 'Search:Properties'; + const SEARCH_TOTAL = 'Search:Total'; + const SEARCH_EQUALTO = 'Search:EqualTo'; + const SEARCH_VALUE = 'Search:Value'; + const SEARCH_AND = 'Search:And'; + const SEARCH_OR = 'Search:Or'; + const SEARCH_FREETEXT = 'Search:FreeText'; + const SEARCH_DEEPTRAVERSAL = 'Search:DeepTraversal'; + const SEARCH_LONGID = 'Search:LongId'; + const SEARCH_REBUILDRESULTS = 'Search:RebuildResults'; + const SEARCH_LESSTHAN = 'Search:LessThan'; + const SEARCH_GREATERTHAN = 'Search:GreaterThan'; + const SEARCH_SCHEMA = 'Search:Schema'; + const SEARCH_SUPPORTED = 'Search:Supported'; + + /** Search Status **/ + const SEARCH_STATUS_SUCCESS = 1; + const SEARCH_STATUS_ERROR = 3; + + /** Store Status **/ + const STORE_STATUS_SUCCESS = 1; + const STORE_STATUS_PROTERR = 2; + const STORE_STATUS_SERVERERR = 3; + const STORE_STATUS_BADLINK = 4; + const STORE_STATUS_NOTFOUND = 6; + const STORE_STATUS_CONNECTIONERR = 7; + const STORE_STATUS_COMPLEX = 8; + + + /** + * Handle request + * + * @return boolean + */ + public function handle() + { + parent::handle(); + $this->_logger->info('[' . $this->_device->id . '] Beginning SEARCH'); + + $searchrange = '0'; + $search_status = self::SEARCH_STATUS_SUCCESS; + $store_status = self::STORE_STATUS_SUCCESS; + + if (!$this->_decoder->getElementStartTag(self::SEARCH_SEARCH) || + !$this->_decoder->getElementStartTag(self::SEARCH_STORE) || + !$this->_decoder->getElementStartTag(self::SEARCH_NAME)) { + + $search_status = self::SEARCH_STATUS_ERROR; + } + + /* The type of search, we only support GAL right now */ + $searchname = $this->_decoder->getElementContent(); + if (!$this->_decoder->getElementEndTag()) { + $search_status = self::SEARCH_STATUS_ERROR; + $store_status = self::STORE_STATUS_PROTERR; + } + + /* The search query */ + if (!$this->_decoder->getElementStartTag(self::SEARCH_QUERY)) { + $search_status = self::SEARCH_STATUS_ERROR; + $store_status = self::STORE_STATUS_PROTERR; + } + $searchquery = $this->_decoder->getElementContent(); + if (!$this->_decoder->getElementEndTag()) { + $search_status = self::SEARCH_STATUS_ERROR; + $store_status = self::STORE_STATUS_PROTERR; + } + + /* Range */ + if ($this->_decoder->getElementStartTag(self::SEARCH_OPTIONS)) { + while(1) { + if ($this->_decoder->getElementStartTag(self::SEARCH_RANGE)) { + $searchrange = $this->_decoder->getElementContent(); + if (!$this->_decoder->getElementEndTag()) { + $search_status = self::SEARCH_STATUS_ERROR; + $store_status = self::STORE_STATUS_PROTERR; + } + } + $e = $this->_decoder->peek(); + if ($e[Horde_ActiveSync_Wbxml::EN_TYPE] == Horde_ActiveSync_Wbxml::EN_TYPE_ENDTAG) { + $this->_decoder->getElementEndTag(); + break; + } + } + } + + /* Close the store container */ + if (!$this->_decoder->getElementEndTag()) {//store + $search_status = self::SEARCH_STATUS_ERROR; + $store_status = self::STORE_STATUS_PROTERR; + } + + /* Close the search container */ + if (!$this->_decoder->getElementEndTag()) {//search + $search_status = self::SEARCH_STATUS_ERROR; + $store_status = self::STORE_STATUS_PROTERR; + } + + /* We only support the GAL */ + if (strtoupper($searchname) != "GAL") { + $this->_logger->debug('Searchtype ' . $searchname . 'is not supported'); + $store_status = self::STORE_STATUS_COMPLEX; + } + + /* Get search results from backend */ + $rows = $this->_driver->getSearchResults($searchquery, $searchrange); + + /* Send output */ + $this->_encoder->startWBXML(); + $this->_encoder->startTag(self::SEARCH_SEARCH); + + $this->_encoder->startTag(self::SEARCH_STATUS); + $this->_encoder->content($search_status); + $this->_encoder->endTag(); + + $this->_encoder->startTag(self::SEARCH_RESPONSE); + $this->_encoder->startTag(self::SEARCH_STORE); + + $this->_encoder->startTag(self::SEARCH_STATUS); + $this->_encoder->content($store_status); + $this->_encoder->endTag(); + + $searchrange = $rows['range']; + + /* Build the results */ + foreach ($rows['rows'] as $u) { + $this->_encoder->startTag(self::SEARCH_RESULT); + $this->_encoder->startTag(self::SEARCH_PROPERTIES); + + $this->_encoder->startTag(Horde_ActiveSync::GAL_DISPLAYNAME); + $this->_encoder->content($u[Horde_ActiveSync::GAL_DISPLAYNAME]); + $this->_encoder->endTag(); + + $this->_encoder->startTag(Horde_ActiveSync::GAL_PHONE); + $this->_encoder->content($u[Horde_ActiveSync::GAL_PHONE]); + $this->_encoder->endTag(); + + $this->_encoder->startTag(Horde_ActiveSync::GAL_ALIAS); + $this->_encoder->content($u[Horde_ActiveSync::GAL_ALIAS]); + $this->_encoder->endTag(); + + $this->_encoder->startTag(Horde_ActiveSync::GAL_FIRSTNAME); + $this->_encoder->content($u[Horde_ActiveSync::GAL_FIRSTNAME]); + $this->_encoder->endTag(); + + $this->_encoder->startTag(Horde_ActiveSync::GAL_LASTNAME); + $this->_encoder->content($u[Horde_ActiveSync::GAL_LASTNAME]); + $this->_encoder->endTag(); + + $this->_encoder->startTag(Horde_ActiveSync::GAL_EMAILADDRESS); + $this->_encoder->content($u[Horde_ActiveSync::GAL_EMAILADDRESS]); + $this->_encoder->endTag(); + + $this->_encoder->startTag(Horde_ActiveSync::GAL_HOMEPHONE); + $this->_encoder->content($u[Horde_ActiveSync::GAL_HOMEPHONE]); + $this->_encoder->endTag(); + + $this->_encoder->startTag(Horde_ActiveSync::GAL_COMPANY); + $this->_encoder->content($u[Horde_ActiveSync::GAL_COMPANY]); + $this->_encoder->endTag(); + + $this->_encoder->endTag();//result + $this->_encoder->endTag();//properties + + $this->_encoder->startTag(self::SEARCH_RANGE); + $this->_encoder->content($searchrange); + $this->_encoder->endTag(); + + $this->_encoder->startTag(self::SEARCH_TOTAL); + $this->_encoder->content(count($rows)); + $this->_encoder->endTag(); + } + + $this->_encoder->endTag();//store + $this->_encoder->endTag();//response + $this->_encoder->endTag();//search + + return true; + } + +} \ No newline at end of file diff --git a/framework/ActiveSync/package.xml b/framework/ActiveSync/package.xml index 9d16796de..36d4d3596 100644 --- a/framework/ActiveSync/package.xml +++ b/framework/ActiveSync/package.xml @@ -71,6 +71,7 @@ http://pear.php.net/dtd/package-2.0.xsd"> + @@ -123,6 +124,7 @@ http://pear.php.net/dtd/package-2.0.xsd"> +