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
const FLAG_NEWMESSAGE = 'NewMessage';
/* Folder types */
- // Other constants
const FOLDER_TYPE_OTHER = 1;
const FOLDER_TYPE_INBOX = 2;
const FOLDER_TYPE_DRAFTS = 3;
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
*
}
/**
- * @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.
*
/**
* 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.');
}
/**
* 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;
}
/**
* @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)
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.
*
--- /dev/null
+<?php
+/**
+ * ActiveSync Handler for Search requests
+ *
+ * Copyright 2009 - 2010 The Horde Project (http://www.horde.org)
+ *
+ * @author Michael J. Rubinsky <mrubinsk@horde.org>
+ * @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
<file name="Provision.php" role="php" />
<file name="GetItemEstimate.php" role="php" />
<file name="SendMail.php" role="php" />
+ <file name="Search.php" role="php" />
</dir>
<file name="Exception.php" role="php" />
<file name="Wbxml.php" role="php" />
<install name="lib/Horde/ActiveSync/Request/Provision.php" as="Horde/ActiveSync/Request/Provision.php" />
<install name="lib/Horde/ActiveSync/Request/GetItemEstimate.php" as="Horde/ActiveSync/Request/GetItemEstimate.php" />
<install name="lib/Horde/ActiveSync/Request/SendMail.php" as="Horde/ActiveSync/Request/SendMail.php" />
+ <install name="lib/Horde/ActiveSync/Request/Search.php" as="Horde/ActiveSync/Request/Search.php" />
<install name="lib/Horde/ActiveSync/Exception.php" as="Horde/ActiveSync/Exception.php" />
<install name="lib/Horde/ActiveSync/Wbxml.php" as="Horde/ActiveSync/Wbxml.php" />
<install name="lib/Horde/ActiveSync/Timezone.php" as="Horde/ActiveSync/Timezone.php" />