diff --git a/abook_class.php b/abook_class.php index d5edea3..d06b511 100755 --- a/abook_class.php +++ b/abook_class.php @@ -34,22 +34,18 @@ class abook_carddav extends addressbook_backend { /* Constructor */ function abook_carddav($param) { - $this->sname = _("carddav address book"); + // defaults + $this->sname = _("CardDAV Address Book"); if (is_array($param)) { - if (!empty($param['name'])) { - $this->sname = $param['name']; - } - - if (isset($param['writeable'])) { - $this->writeable = $param['writeable']; - } - - if (isset($param['listing'])) { - $this->listing = $param['listing']; - } - - return $this->open(true); + if (!empty($param['name'])) { $this->sname = $param['name']; } + if (!empty($param['abook_uri'])) { $this->abook_uri = $param['abook_uri']; } + if (!empty($param['base_uri'])) { $this->base_uri = $param['base_uri']; } + if (!empty($param['username'])) { $this->username = $param['username']; } + if (!empty($param['password'])) { $this->password = $param['password']; } + if (isset($param['writeable'])) { $this->writeable = $param['writeable']; } + if (isset($param['listing'])) { $this->listing = $param['listing']; } + return $this->open(); } else { return $this->set_error('Invalid argument to constructor'); @@ -60,15 +56,13 @@ class abook_carddav extends addressbook_backend { * */ function open() { - // backend open function - $this->account = new Account(DISCOVERY_URI, USERNAME, PASSWORD); - // Use stored addressbook uri if it exists - if(defined('abook_uri')){ - $this->abook = new AddressbookCollection(abook_uri, $this->account); - // TODO: check that it's valid - return true; - } + // backend open function + $this->account = new Account($this->base_uri, $this->username, $this->password, $this->base_uri); + $this->abook = new AddressbookCollection($this->abook_uri, $this->account); + // TODO: check that it's valid + return true; + /* TODO: move this to discover page // Discover the addressbooks for that account try { $discover = new Discovery(); @@ -83,37 +77,26 @@ class abook_carddav extends addressbook_backend { // HINT: use this line to get your discovered addressbook URI // echo "discovered: " . $this->abook->getUri(); return true; + */ } /** - * + * Run query against addressbook and return squurrelmail-type address(es) + * Params are same as in https://mstilkerich.github.io/carddavclient/classes/MStilkerich-CardDavClient-AddressbookCollection.html#method_query + * except the lack of 2nd parameter: + * @param array $query + * The query filter conditions, for format see https://mstilkerich.github.io/carddavclient/classes/MStilkerich-CardDavClient-XmlElements-Filter.html#method___construct + * @param bool $matchAll + * Whether all or any of the conditions needs to match. + * @param int $limit + * Tell the server to return at most $limit results. 0 means no limit. + * @return either: + * * a single address (array) - if $limit==1 + * * or array of addresses (arrays) */ - function close() { - // ADDME: backend close function - - } - - - /* ========================== Public ======================== */ - - /** - * Search addressesbook for entries where any field matches expr. - * It's expected to support * and ? wildcards, and search all fields in any position. - * Note that currently, it does not. - * @param expr string search expression. - * @return array of addresses (arrays) - */ - function search($expr) { - $ret = array(); - - /* To be replaced by advanded search expression parsing */ - if(is_array($expr)) { return; } - - // list all addresses where any of these fields contains $expr. - // wildcards not supported. - // Also note that we don't check for presence of email here, - // this will be filtered out later - $all=$this->abook->query(['FN' => "/$expr/", 'EMAIL' => "/$expr/", 'ORG' => "/$expr/", 'NOTE' => "/$expr/"],["FN", "N", "EMAIL", "ORG"]); + function run_query($query, $match_all=false, $limit=0) { + // TODO: add nickname to list of fields if $this->writeable + $all=$this->abook->query($query,["FN", "N", "EMAIL", "ORG"],$match_all,$limit); /* Returns an array of matched VCards: The keys of the array are the URIs of the vcards @@ -126,7 +109,10 @@ class abook_carddav extends addressbook_backend { if(!isset($vcard->EMAIL)) { continue; } $names = $vcard->N->getParts(); // last,first,additional,prefix,suffix - array_push($ret,array( + // TODO: if !$this->writeable then we want to add one row per each email this vcard has + // foreach($vcard->EMAIL as $email) { ... } + $value = array( + // TODO: nickname depends on $this->writeable 'nickname' => substr($uri, $abook_uri_len), 'name' => (string)$vcard->FN, 'firstname' => (string)$names[1], @@ -134,11 +120,34 @@ class abook_carddav extends addressbook_backend { 'email' => (string)$vcard->EMAIL, 'label' => (string)$vcard->ORG, 'backend' => $this->bnum, - 'source' => $this->sname)); + 'source' => $this->sname); + if($limit == 1) { return $value; } + array_push($ret,$value); } + return $ret; + } + + /* ========================== Public ======================== */ + /** + * Search addressesbook for entries where any field matches expr. + * It's expected to support * and ? wildcards, and search all fields in any position. + * Note that currently, it does not. + * @param expr string search expression. + * @return array of addresses (arrays) + */ + function search($expr) { - return $ret; + /* To be replaced by advanded search expression parsing */ + if(is_array($expr)) { return; } + + if ($expr=='*') { return $this->list_addr(); } + + // list all addresses where any of these fields contains $expr. + // wildcards are not supported. + // Also note that we don't check for presence of email in the filter, + // this will be filtered out inside run_query + return $this->run_query(['FN' => "/$expr/", 'EMAIL' => "/$expr/", 'ORG' => "/$expr/", 'NOTE' => "/$expr/"]); } /** @@ -174,6 +183,7 @@ class abook_carddav extends addressbook_backend { $vcard = $one['vcard']; $names = $vcard->N->getParts(); return array( + // TODO: nickname depends on $this->writeable 'nickname' => substr($uri, $abook_uri_len), 'name' => (string)$vcard->FN, 'firstname' => (string)$names[1], @@ -193,31 +203,11 @@ class abook_carddav extends addressbook_backend { $filter=['EMAIL' => "/$value/="]; } if($field == SM_ABOOK_FIELD_LABEL) { - $filter=['ORG' => "/$value/="]; + $filter=['ORG' => "/$value/=", 'EMAIL' => "//"]; } if(!isset($filter)) { return array(); } - $all=$this->abook->query($filter,["FN", "N", "EMAIL", "ORG"],true,1); - /* - Returns an array of matched VCards: - The keys of the array are the URIs of the vcards - The values are associative arrays with keys etag (type: string) and vcard (type: VCard) - */ - foreach($all as $uri => $one) { - $vcard = $one['vcard']; - if(!isset($vcard->EMAIL)) { continue; } - $names = $vcard->N->getParts(); - // last,first,additional,prefix,suffix - return array( - 'nickname' => substr($uri, $abook_uri_len), - 'name' => (string)$vcard->FN, - 'firstname' => (string)$names[1], - 'lastname' => (string)$names[0], - 'email' => (string)$vcard->EMAIL, - 'label' => (string)$vcard->ORG, - 'backend' => $this->bnum, - 'source' => $this->sname); - } + return $this->run_query($filter,true,1); } /** @@ -225,34 +215,8 @@ class abook_carddav extends addressbook_backend { * @return array of addresses (arrays) */ function list_addr() { - $ret = array(); - // list all addresses having an email - $all=$this->abook->query(['EMAIL' => "//"],["FN", "N", "EMAIL", "ORG"]); - /* - Returns an array of matched VCards: - The keys of the array are the URIs of the vcards - The values are associative arrays with keys etag (type: string) and vcard (type: VCard) - */ - - $abook_uri_len=strlen($this->abook->getUriPath()); - foreach($all as $uri => $one) { - $vcard = $one['vcard']; - // if(!isset($vcard->EMAIL)) { continue; } - $names = $vcard->N->getParts(); - // last,first,additional,prefix,suffix - array_push($ret,array( - 'nickname' => substr($uri, $abook_uri_len), - 'name' => (string)$vcard->FN, - 'firstname' => (string)$names[1], - 'lastname' => (string)$names[0], - 'email' => (string)$vcard->EMAIL, - 'label' => (string)$vcard->ORG, - 'backend' => $this->bnum, - 'source' => $this->sname)); - } - - return $ret; + return $this->run_query(['EMAIL' => "//"]); } /** diff --git a/setup.php b/setup.php index 55abdc2..d3a0dcb 100755 --- a/setup.php +++ b/setup.php @@ -24,6 +24,8 @@ function squirrelmail_plugin_init_abook_carddav() { $squirrelmail_plugin_hooks['abook_init']['abook_carddav'] = 'abook_carddav_init'; $squirrelmail_plugin_hooks['abook_add_class']['abook_carddav'] = 'abook_carddav_class'; + $squirrelmail_plugin_hooks['optpage_loadhook_display']['abook_carddav'] = 'abook_carddav_optpage'; + } /** @@ -38,7 +40,24 @@ function abook_carddav_init(&$argv) { bindtextdomain ('abook_carddav', SM_PATH . 'locale'); textdomain ('abook_carddav'); - $r=$abook->add_backend('carddav',array('name'=>_("CardDAV Address Book"))); + // TODO: consider multiple uris + $abook_uri = getPref($data_dir, $username, 'plugin_abook_carddav_abook_uri'); + $abook_base_uri = getPref($data_dir, $username, 'plugin_abook_carddav_base_uri'); + $abook_username = getPref($data_dir, $username, 'plugin_abook_carddav_username'); + $abook_password = getPref($data_dir, $username, 'plugin_abook_carddav_password'); + $abook_writeable = getPref($data_dir, $username, 'plugin_abook_carddav_writeable'); + $abook_listing = getPref($data_dir, $username, 'plugin_abook_carddav_listing'); + if(substr_compare($abook_uri, 'http', 0)) { + $r=$abook->add_backend('carddav',array( + 'name'=>_("CardDAV Address Book"), + 'abook_uri'=>$abook_uri, + 'base_uri'=>$abook_base_uri, + 'username'=>$abook_username, + 'password'=>$abook_password, + 'writeable'=>$abook_writeable, + 'listing'=>$abook_listing, + )); + } bindtextdomain ('squirrelmail', SM_PATH . 'locale'); textdomain ('squirrelmail'); @@ -48,12 +67,59 @@ function abook_carddav_class() { bindtextdomain ('abook_carddav', SM_PATH . 'locale'); textdomain ('abook_carddav'); - require_once(SM_PATH . 'plugins/abook_carddav/abook_class.php'); + // load file only if $abook_uri is set + $abook_uri = getPref($data_dir, $username, 'plugin_abook_carddav_abook_uri'); + if(substr_compare($abook_uri, 'http', 0)) { + require_once(SM_PATH . 'plugins/abook_carddav/abook_class.php'); + } bindtextdomain ('squirrelmail', SM_PATH . 'locale'); textdomain ('squirrelmail'); } +function abook_carddav_optpage() { + global $optpage_data; + global $username, $data_dir; + + sq_change_text_domain('abook_carddav'); + $optpage_data['grps']['abook_carddav'] = _("CardDAV Address Book"); + $optpage_data['vals']['abook_carddav'][] = array( + 'name' => 'plugin_abook_carddav_abook_uri', + 'caption' => _("URI of addressbook - MUST start with 'http' (both 'http://' and 'https://' are ok)"), + 'trailing_text' => _("use discovery page to get one"), + 'type' => SMOPT_TYPE_STRING, + // 'initial_value' => $abook_uri, + ); + $optpage_data['vals']['abook_carddav'][] = array( + 'name' => 'plugin_abook_carddav_base_uri', + 'caption' => _("base URI - usually a server name with protocol and port"), + 'trailing_text' => _("use discovery page to get one"), + 'type' => SMOPT_TYPE_STRING, + // 'initial_value' => $abook_uri, + ); + $optpage_data['vals']['abook_carddav'][] = array( + 'name' => 'plugin_abook_carddav_username', + 'caption' => _("Username"), + 'type' => SMOPT_TYPE_STRING, + ); + $optpage_data['vals']['abook_carddav'][] = array( + 'name' => 'plugin_abook_carddav_password', + 'caption' => _("Password"), + 'type' => SMOPT_TYPE_STRING, + ); + $optpage_data['vals']['abook_carddav'][] = array( + 'name' => 'plugin_abook_carddav_writeable', + 'caption' => _("Writeable (nickname field shows part of vcard URI)"), + 'type' => SMOPT_TYPE_BOOLEAN, + ); + $optpage_data['vals']['abook_carddav'][] = array( + 'name' => 'plugin_abook_carddav_listing', + 'caption' => _("Listing allowed (otherwise, only addressbook search is useful)"), + 'type' => SMOPT_TYPE_BOOLEAN, + ); +} + + /** * shows plugin's version * @return string