diff -urN ../z-push-2.5.orig/src/backend/searchmysql/config.php.dist ./src/backend/searchmysql/config.php.dist --- ../z-push-2.5.orig/src/backend/searchmysql/config.php.dist 1970-01-01 03:00:00.000000000 +0300 +++ ./src/backend/searchmysql/config.php.dist 2019-10-18 22:16:07.528201000 +0300 @@ -0,0 +1,67 @@ +. +* +* Consult LICENSE file for details +************************************************/ + +// MySQL host, port, user, password and database +define("MYSQL_HOST", "127.0.0.1"); +define("MYSQL_PORT", 3306); +define("MYSQL_USER", "roundcube"); +define("MYSQL_PASSWORD", "changeme"); +define("MYSQL_DATABASE", "roundcube"); + +// Search query +// the SEARCHVALUE string is substituded by the value inserted into the search field +// the MAILUSER string is substituded by $_SERVER['PHP_AUTH_USER'] or $_GET['User'] +// the QUERYLIMIT string is substituded by the value of $searchrange +define("MYSQL_SEARCH_QUERY", "SELECT `contacts`.`name` AS displayname, `contacts`.`firstname` AS firstname, `contacts`.`surname` AS lastname, `contacts`.`email` AS email, `contacts`.`vcard` AS vcard +FROM `contacts`, `users` +WHERE + `users`.`username`='MAILUSER' AND `contacts`.`user_id`=`users`.`user_id` AND `contacts`.`del`=0 AND ( + `contacts`.`email` LIKE '%SEARCHVALUE%' + OR `contacts`.`name` LIKE '%SEARCHVALUE%' + OR `contacts`.`firstname` LIKE '%SEARCHVALUE%' + OR `contacts`.`surname` LIKE '%SEARCHVALUE%' + ) +LIMIT QUERYLIMIT;"); + +// SQL vcard field in search query above +define("MYSQL_SEARCH_QUERY_RESULT_VCARD_FIELD", "vcard"); + +// SQL field mapping. +// values correspond to the search query above +global $mysql_field_map; +$mysql_field_map = array( + SYNC_GAL_DISPLAYNAME => 'displayname', + SYNC_GAL_PHONE => 'vcard_phone', + SYNC_GAL_OFFICE => 'vcard_x-department', + SYNC_GAL_TITLE => 'vcard_title', + SYNC_GAL_COMPANY => 'vcard_org', + SYNC_GAL_ALIAS => 'vcard_nickname', + SYNC_GAL_FIRSTNAME => 'firstname', + SYNC_GAL_LASTNAME => 'lastname', + SYNC_GAL_HOMEPHONE => 'vcard_tel_home', + SYNC_GAL_MOBILEPHONE => 'vcard_tel_cell', + SYNC_GAL_EMAILADDRESS => 'email', + ); diff -urN ../z-push-2.5.orig/src/backend/searchmysql/searchmysql.php ./src/backend/searchmysql/searchmysql.php --- ../z-push-2.5.orig/src/backend/searchmysql/searchmysql.php 1970-01-01 03:00:00.000000000 +0300 +++ ./src/backend/searchmysql/searchmysql.php 2019-10-18 22:01:38.102380000 +0300 @@ -0,0 +1,222 @@ +. +* +* Consult LICENSE file for details +************************************************/ + +/********************************************************************* + * The BackendSearchMySQL is a stub to implement own search funtionality + * + * If you wish to implement an alternative search method, you should implement the + * ISearchProvider interface like the BackendSearchLDAP backend + */ + +require_once("backend/searchmysql/config.php"); + +class BackendSearchMySQL implements ISearchProvider{ + private $connection = false; + + /** + * Constructor + * initializes the searchprovider to perform the search + * + * @access public + * @return + * @throws StatusException, FatalException + */ + public function __construct() { + if (!function_exists("mysqli_connect")) { + throw new StatusException("BackendSearchMySQL(): php-mysqli is not installed. Search aborted.", SYNC_SEARCHSTATUS_STORE_SERVERERROR, null, LOGLEVEL_FATAL); + } + + if (!defined("MYSQL_HOST")) { + throw new StatusException("BackendSearchMySQL(): MYSQL_HOST not defined. Search aborted.", SYNC_SEARCHSTATUS_STORE_SERVERERROR, null, LOGLEVEL_FATAL); + } + if (!defined("MYSQL_USER")) { + throw new StatusException("BackendSearchMySQL(): MYSQL_USER not defined. Search aborted.", SYNC_SEARCHSTATUS_STORE_SERVERERROR, null, LOGLEVEL_FATAL); + } + if (!defined("MYSQL_PASSWORD")) { + throw new StatusException("BackendSearchMySQL(): MYSQL_PASSWORD not defined. Search aborted.", SYNC_SEARCHSTATUS_STORE_SERVERERROR, null, LOGLEVEL_FATAL); + } + if (!defined("MYSQL_DATABASE")) { + throw new StatusException("BackendSearchMySQL(): MYSQL_DATABASE not defined. Search aborted.", SYNC_SEARCHSTATUS_STORE_SERVERERROR, null, LOGLEVEL_FATAL); + } + if (!defined("MYSQL_PORT")) { + define("MYSQL_PORT", 3306); + } elseif (!is_int(MYSQL_PORT)) { + throw new StatusException("BackendSearchMySQL(): MYSQL_PORT " . MYSQL_PORT . " in not a numeric. Search aborted.", SYNC_SEARCHSTATUS_STORE_SERVERERROR, null, LOGLEVEL_FATAL); + } + + // connect to MySQL + $this->connection = @mysqli_connect(MYSQL_HOST, MYSQL_USER, MYSQL_PASSWORD, MYSQL_DATABASE, MYSQL_PORT); + if (!$this->connection) { + if (mysqli_connect_errno()) { + $error_message = sprintf("BackendSearchMySQL(): Could not connect to MySQL server %s:%d: error %d (%s). Search aborted.", MYSQL_HOST, MYSQL_PORT, mysqli_connect_errno($this->connection), mysqli_connect_error($this->connection)); + $this->connection = false; + throw new StatusException($error_message, SYNC_SEARCHSTATUS_STORE_CONNECTIONFAILED, null, LOGLEVEL_ERROR); + } else { + $error_message = sprintf("BackendSearchMySQL(): Could not authorize as user '%s' and specified password for accessing to db '%s': error %d (%s). Search aborted.", MYSQL_USER, MYSQL_DATABASE, mysqli_errno($this->connection), mysqli_error($this->connection)); + @mysqli_close($this->connection); + $this->connection = false; + throw new StatusException($error_message, SYNC_SEARCHSTATUS_STORE_ACCESSDENIED, null, LOGLEVEL_ERROR); + } + } + + $mysql_query = "SET NAMES 'utf8';"; + if (!@mysqli_query($this->connection, $mysql_query)) { + $error_message = sprintf("BackendSearchMySQL(): Error executing query '%s': error %d (%s).", $mysql_query, mysqli_errno($this->connection), mysqli_error($this->connection)); + ZLog::Write(LOGLEVEL_ERROR, $error_message); + return false; + } + } + + /** + * Indicates if a search type is supported by this SearchProvider + * Currently only the type ISearchProvider::SEARCH_GAL (Global Address List) is implemented + * + * @param string $searchtype + * + * @access public + * @return boolean + */ + public function SupportsType($searchtype) { + return ($searchtype == ISearchProvider::SEARCH_GAL); + } + + /** + * Searches the GAL + * + * @param string $searchquery string to be searched for + * @param string $searchrange specified searchrange + * @param SyncResolveRecipientsPicture $searchpicture limitations for picture + * + * @access public + * @return array search results + * @throws StatusException + */ + public function GetGALSearchResults($searchquery, $searchrange, $searchpicture) { + global $mysql_field_map; + if (isset($this->connection) && $this->connection !== false) { + + $mail_user = $_SERVER['PHP_AUTH_USER']; + if (empty($mail_user)) $mail_user = $_GET['User']; + if (empty($mail_user)) { + ZLog::Write(LOGLEVEL_ERROR, "BackendSearchMySQL: Unable to determine mail user. Search aborted"); + return false; + } + $mail_user = mysqli_real_escape_string($this->connection, $mail_user); + + $rangestart = 0; + $rangeend = 50; + if (!empty($searchrange) and ($searchrange != '0')) { + $pos = strpos($searchrange, '-'); + if ($pos !== false) { + $rangestart = substr($searchrange, 0, $pos); + $rangeend = substr($searchrange, ($pos + 1)); + } + } + $mysql_query_limit = sprintf("%d, %d", $rangestart, $rangeend-$rangestart+1); + + $mysql_search_query = str_replace("QUERYLIMIT", $mysql_query_limit, str_replace("MAILUSER", $mail_user, str_replace("SEARCHVALUE", $searchquery, MYSQL_SEARCH_QUERY))); + ZLog::Write(LOGLEVEL_DEBUG, sprintf("BackendSearchMySQL: execute query '%s'", str_replace("\n", " ", $mysql_search_query))); + $result = @mysqli_query($this->connection, $mysql_search_query); + if (!$result) { + $error_message = sprintf("BackendSearchMySQL(): Error executing query '%s': error %d (%s). Search aborted.", str_replace("\n", " ", $mysql_search_query), mysqli_errno($this->connection), mysqli_error($this->connection)); + ZLog::Write(LOGLEVEL_ERROR, $error_message); + return false; + } + + $items = array(); + $items['range'] = $rangestart . '-' . (mysqli_num_rows($result) - 1); + $items['searchtotal'] = mysqli_num_rows($result); + + $rc = 0; + while ($item = mysqli_fetch_array($result, MYSQLI_ASSOC)) { + if (defined("MYSQL_SEARCH_QUERY_RESULT_VCARD_FIELD")) { + $vcard = preg_split('/\r?\n/', $item[MYSQL_SEARCH_QUERY_RESULT_VCARD_FIELD]); + foreach ($vcard as $vcard_line) { + if (!empty($vcard_line)) { + $pos = strpos($vcard_line, ':'); + if ($pos !== false) { + $vcard_key = str_replace(';type=', '_', strtolower(substr($vcard_line, 0, $pos))); + $vcard_value = substr($vcard_line, ($pos + 1)); + $item['vcard_' . $vcard_key] = $vcard_value; + } + } + } + } + + foreach ($mysql_field_map as $key => $value ) { + if (isset($item[$value])) { + $items[$rc][$key] = $item[$value]; + } + } + $rc++; + } + + mysqli_free_result($result); + + return $items; + } else { + return false; + } + } + + /** + * Searches for the emails on the server + * + * @param ContentParameter $cpo + * + * @return array + */ + public function GetMailboxSearchResults($cpo){ + return array(); + } + + /** + * Terminates a search for a given PID + * + * @param int $pid + * + * @return boolean + */ + public function TerminateSearch($pid) { + return true; + } + + /** + * Disconnects from the current search provider + * + * @access public + * @return boolean + */ + public function Disconnect() { + if ($this->connection) + @mysqli_close($this->connection); + $this->connection = false; + + return true; + } +}