问题描述
我有一台带有 CardDAV 服务器(Synology 通讯录)的 Synology DS718。 现在我想检查 PHP,是否存在特定的联系人。 为此,我找到了这个脚本: https://github.com/christian-putzke/CardDAV-PHP/blob/master/carddav.php
为了更好的调试,我稍微修改了一下。
<?PHP
$carddav = new carddav_backend('https://carddav_url:port/carddav/SynoContacts/');
$carddav->set_auth('CardDAV_Username','CardDAV_Password');
// CONNECTION CHECK
echo '/***************** CONNECTION CHECK *******************/<br /><br />';
echo $carddav->check_connection();
echo '<br /><br /><br />';
// Simple CardDAV query
echo '/***************** Simple CardDAV query *******************/<br /><br />';
echo $carddav->get();
echo '<br /><br /><br />';
// CardDAV debug
$carddav->enable_debug();
$carddav->get();
echo '/***************** CardDAV debug *******************/<br /><br />';
echo '<pre>';
var_dump($carddav->get_debug());
echo '</pre>';
class carddav_backend
{
/**
* CardDAV PHP Version
*
* @constant string
*/
const VERSION = '0.6';
/**
* User agent displayed in http requests
*
* @constant string
*/
const USERAGENT = 'CardDAV PHP/';
/**
* CardDAV server url
*
* @var string
*/
private $url = null;
/**
* CardDAV server url_parts
*
* @var array
*/
private $url_parts = null;
/**
* Authentication string
*
* @var string
*/
private $auth = null;
/**
* Authentication: username
*
* @var string
*/
private $username = null;
/**
* Authentication: password
*
* @var string
*/
private $password = null;
/**
* Characters used for vCard id generation
*
* @var array
*/
private $vcard_id_chars = array(0,1,2,3,4,5,6,7,8,9,'A','B','C','D','E','F');
/**
* CardDAV server connection (curl handle)
*
* @var resource
*/
private $curl;
/**
* Debug on or off
*
* @var boolean
*/
private $debug = false;
/**
* All available debug @R_336_4045@ion
*
* @var array
*/
private $debug_@R_336_4045@ion = array();
/**
* Exception codes
*/
const EXCEPTION_WRONG_HTTP_STATUS_CODE_GET = 1000;
const EXCEPTION_WRONG_HTTP_STATUS_CODE_GET_VCARD = 1001;
const EXCEPTION_WRONG_HTTP_STATUS_CODE_GET_XML_VCARD = 1002;
const EXCEPTION_WRONG_HTTP_STATUS_CODE_DELETE = 1003;
const EXCEPTION_WRONG_HTTP_STATUS_CODE_ADD = 1004;
const EXCEPTION_WRONG_HTTP_STATUS_CODE_UPDATE = 1005;
const EXCEPTION_MALFORMED_XML_RESPONSE = 1006;
const EXCEPTION_Could_NOT_GENERATE_NEW_VCARD_ID = 1007;
/**
* Constructor
* Sets the CardDAV server url
*
* @param string $url CardDAV server url
*/
public function __construct($url = null)
{
if ($url !== null)
{
$this->set_url($url);
}
}
/**
* Sets debug @R_336_4045@ion
*
* @param array $debug_@R_336_4045@ion Debug @R_336_4045@ion
* @return void
*/
public function set_debug(array $debug_@R_336_4045@ion)
{
$this->debug_@R_336_4045@ion[] = $debug_@R_336_4045@ion;
}
/**
* Sets the CardDAV server url
*
* @param string $url CardDAV server url
* @return void
*/
public function set_url($url)
{
$this->url = $url;
if (substr($this->url,-1,1) !== '/')
{
$this->url = $this->url . '/';
}
$this->url_parts = parse_url($this->url);
}
/**
* Sets authentication @R_336_4045@ion
*
* @param string $username CardDAV server username
* @param string $password CardDAV server password
* @return void
*/
public function set_auth($username,$password)
{
$this->username = $username;
$this->password = $password;
$this->auth = $username . ':' . $password;
}
/**
* Gets all available debug @R_336_4045@ion
*
* @return array $this->debug_@R_336_4045@ion All available debug @R_336_4045@ion
*/
public function get_debug()
{
return $this->debug_@R_336_4045@ion;
}
/**
* Gets all vCards including additional @R_336_4045@ion from the CardDAV server
*
* @param boolean $include_vcards Include vCards within the response (simplified only)
* @param boolean $raw Get response raw or simplified
* @return string Raw or simplified XML response
*/
public function get($include_vcards = true,$raw = true)
{
$result = $this->query($this->url,'PROPFIND');
switch ($result['http_code'])
{
case 200:
case 207:
if ($raw === true)
{
return $result['response'];
}
else
{
return $this->simplify($result['response'],$include_vcards);
}
break;
default:
throw new Exception('ERROR ' . $result['http_code'] . '.',self::EXCEPTION_WRONG_HTTP_STATUS_CODE_GET);
break;
}
}
/**
* Gets a clean vCard from the CardDAV server
*
* @param string $vcard_id vCard id on the CardDAV server
* @return string vCard (text/vcard)
*/
public function get_vcard($vcard_id)
{
echo $vcard_id;
$vcard_id = str_replace('.vcf',null,$vcard_id);
$result = $this->query($this->url . $vcard_id . '.vcf','GET');
switch ($result['http_code'])
{
case 200:
case 207:
return $result['response'];
break;
default:
throw new Exception('ERROR ' . $result['http_code'] . '.',self::EXCEPTION_WRONG_HTTP_STATUS_CODE_GET_VCARD);
break;
}
}
/**
* Gets a vCard + XML from the CardDAV Server
*
* @param string $vcard_id vCard id on the CardDAV Server
* @return string Raw or simplified vCard (text/xml)
*/
public function get_xml_vcard($vcard_id)
{
$vcard_id = str_replace('.vcf',$vcard_id);
$xml = new XMLWriter();
$xml->openMemory();
$xml->setIndent(4);
$xml->startDocument('1.0','utf-8');
$xml->startElement('C:addressbook-multiget');
$xml->writeAttribute('xmlns:D','DAV:');
$xml->writeAttribute('xmlns:C','urn:ietf:params:xml:ns:carddav');
$xml->startElement('D:prop');
$xml->writeElement('D:getetag');
$xml->writeElement('D:getlastmodified');
$xml->endElement();
$xml->writeElement('D:href',$this->url_parts['path'] . $vcard_id . '.vcf');
$xml->endElement();
$xml->endDocument();
$result = $this->query($this->url,'REPORT',$xml->outputMemory(),'text/xml');
switch ($result['http_code'])
{
case 200:
case 207:
return $this->simplify($result['response'],true);
break;
default:
throw new Exception('ERROR ' . $result['http_code'] . '.',self::EXCEPTION_WRONG_HTTP_STATUS_CODE_GET_XML_VCARD);
break;
}
}
/**
* Enables the debug mode
*
* @return void
*/
public function enable_debug()
{
$this->debug = true;
}
/**
* Checks if the CardDAV server is reachable
*
* @return boolean
*/
public function check_connection()
{
$result = $this->query($this->url,'OPTIONS');
if ($result['http_code'] === 200)
{
return "CONNECTION : OK";
}
else
{
return "CONNECTION : ERROR";
}
}
/**
* Cleans the vCard
*
* @param string $vcard vCard
* @return string $vcard vCard
*/
private function clean_vcard($vcard)
{
$vcard = str_replace("\t",$vcard);
return $vcard;
}
/**
* Deletes an entry from the CardDAV server
*
* @param string $vcard_id vCard id on the CardDAV server
* @return boolean
*/
public function delete($vcard_id)
{
$result = $this->query($this->url . $vcard_id . '.vcf','DELETE');
switch ($result['http_code'])
{
case 204:
return true;
break;
default:
throw new Exception('ERROR ' . $result['http_code'] . '.',self::EXCEPTION_WRONG_HTTP_STATUS_CODE_DELETE);
break;
}
}
/**
* Adds an entry to the CardDAV server
*
* @param string $vcard vCard
* @param string $vcard_id vCard id on the CardDAV server
* @return string The new vCard id
*/
public function add($vcard,$vcard_id = null)
{
if ($vcard_id === null)
{
$vcard_id = $this->generate_vcard_id();
}
$vcard = $this->clean_vcard($vcard);
$result = $this->query($this->url . $vcard_id . '.vcf','PUT',$vcard,'text/vcard');
switch($result['http_code'])
{
case 201:
return $vcard_id;
break;
default:
throw new Exception('ERROR ' . $result['http_code'] . '.',self::EXCEPTION_WRONG_HTTP_STATUS_CODE_ADD);
break;
}
}
/**
* Updates an entry to the CardDAV server
*
* @param string $vcard vCard
* @param string $vcard_id vCard id on the CardDAV server
* @return boolean
*/
public function update($vcard,$vcard_id)
{
try
{
return $this->add($vcard,$vcard_id);
}
catch (Exception $e)
{
throw new Exception($e->getMessage(),self::EXCEPTION_WRONG_HTTP_STATUS_CODE_UPDATE);
}
}
/**
* Simplify CardDAV XML response
*
* @param string $response CardDAV XML response
* @param boolean $include_vcards Include vCards or not
* @return string Simplified CardDAV XML response
*/
private function simplify($response,$include_vcards = true)
{
$response = $this->clean_response($response);
try
{
$xml = new SimpleXMLElement($response);
}
catch(Exception $e)
{
throw new Exception('The XML response seems to be malformed and can\'t be simplified!',self::EXCEPTION_MALFORMED_XML_RESPONSE,$e);
}
$simplified_xml = new XMLWriter();
$simplified_xml->openMemory();
$simplified_xml->setIndent(4);
$simplified_xml->startDocument('1.0','utf-8');
$simplified_xml->startElement('response');
if (!empty($xml->response))
{
foreach ($xml->response as $response)
{
if (preg_match('/vcard/',$response->propstat->prop->getcontenttype) || preg_match('/vcf/',$response->href))
{
$id = basename($response->href);
$id = str_replace('.vcf',$id);
if (!empty($id))
{
$simplified_xml->startElement('element');
$simplified_xml->writeElement('id',$id);
$simplified_xml->writeElement('etag',str_replace('"',$response->propstat->prop->getetag));
$simplified_xml->writeElement('last_modified',$response->propstat->prop->getlastmodified);
if ($include_vcards === true)
{
$simplified_xml->writeElement('vcard',$this->get_vcard($id));
}
$simplified_xml->endElement();
}
}
else if (preg_match('/unix-directory/',$response->propstat->prop->getcontenttype))
{
if (isset($response->propstat->prop->href))
{
$href = $response->propstat->prop->href;
}
else if (isset($response->href))
{
$href = $response->href;
}
else
{
$href = null;
}
$url = str_replace($this->url_parts['path'],$this->url) . $href;
$simplified_xml->startElement('addressbook_element');
$simplified_xml->writeElement('display_name',$response->propstat->prop->displayname);
$simplified_xml->writeElement('url',$url);
$simplified_xml->writeElement('last_modified',$response->propstat->prop->getlastmodified);
$simplified_xml->endElement();
}
}
}
$simplified_xml->endElement();
$simplified_xml->endDocument();
return $simplified_xml->outputMemory();
}
/**
* Cleans CardDAV XML response
*
* @param string $response CardDAV XML response
* @return string $response Cleaned CardDAV XML response
*/
private function clean_response($response)
{
$response = utf8_encode($response);
$response = str_replace('D:',$response);
$response = str_replace('d:',$response);
$response = str_replace('C:',$response);
$response = str_replace('c:',$response);
return $response;
}
/**
* Curl initialization
*
* @return void
*/
public function curl_init()
{
if (empty($this->curl))
{
$this->curl = curl_init();
curl_setopt($this->curl,CURLOPT_HEADER,true);
curl_setopt($this->curl,CURLOPT_SSL_VERIFYHOST,false);
curl_setopt($this->curl,CURLOPT_SSL_VERIFYPEER,CURLOPT_RETURNTRANSFER,CURLOPT_USERAGENT,self::USERAGENT.self::VERSION);
if ($this->auth !== null)
{
curl_setopt($this->curl,CURLOPT_HTTPAUTH,CURLAUTH_ANY);
curl_setopt($this->curl,CURLOPT_USERPWD,$this->auth);
}
}
}
/**
* Query the CardDAV server via curl and returns the response
*
* @param string $url CardDAV server URL
* @param string $method HTTP method like (OPTIONS,GET,HEAD,POST,PUT,DELETE,TRACE,copY,MOVE)
* @param string $content Content for CardDAV queries
* @param string $content_type Set content type
* @return array Raw CardDAV Response and http status code
*/
private function query($url,$method,$content = null,$content_type = null)
{
$this->curl_init();
curl_setopt($this->curl,CURLOPT_URL,$url);
curl_setopt($this->curl,CURLOPT_CUSTomrEQUEST,$method);
if ($content !== null)
{
curl_setopt($this->curl,CURLOPT_POST,CURLOPT_POSTFIELDS,$content);
}
else
{
curl_setopt($this->curl,null);
}
if ($content_type !== null)
{
curl_setopt($this->curl,CURLOPT_HTTPHEADER,array('Content-type: '.$content_type));
}
else
{
curl_setopt($this->curl,array());
}
$complete_response = curl_exec($this->curl);
$header_size = curl_getinfo($this->curl,CURLINFO_HEADER_SIZE);
$http_code = curl_getinfo($this->curl,CURLINFO_HTTP_CODE);
$header = trim(substr($complete_response,$header_size));
$response = substr($complete_response,$header_size);
$return = array(
'response' => $response,'http_code' => $http_code
);
if ($this->debug === true)
{
$debug = $return;
$debug['url'] = $url;
$debug['method'] = $method;
$debug['content'] = $content;
$debug['content_type'] = $content_type;
$debug['header'] = $header;
$this->set_debug($debug);
}
return $return;
}
/**
* Returns a valid and unused vCard id
*
* @return string $vcard_id Valid vCard id
*/
private function generate_vcard_id()
{
$vcard_id = null;
for ($number = 0; $number <= 25; $number ++)
{
if ($number == 8 || $number == 17)
{
$vcard_id .= '-';
}
else
{
$vcard_id .= $this->vcard_id_chars[mt_rand(0,(count($this->vcard_id_chars) - 1))];
}
}
try
{
$carddav = new carddav_backend($this->url);
$carddav->set_auth($this->username,$this->password);
$result = $carddav->query($this->url . $vcard_id . '.vcf','GET');
if ($result['http_code'] !== 404)
{
$vcard_id = $this->generate_vcard_id();
}
return $vcard_id;
}
catch (Exception $e)
{
throw new Exception($e->getMessage(),self::EXCEPTION_Could_NOT_GENERATE_NEW_VCARD_ID);
}
}
/**
* Destructor
* Close curl connection if it's open
*
* @return void
*/
public function __destruct()
{
if (!empty($this->curl))
{
curl_close($this->curl);
}
}
}
?>
输出:
/***************** CONNECTION CHECK *******************/
CONNECTION : OK
/***************** Simple CardDAV query *******************/
/carddav/SynoContacts//carddav//carddav/SynoContacts//carddav/SynoContacts//carddav/SynoContacts//carddav/SynoContacts//carddav/SynoContacts//carddav/SynoContacts/HTTP/1.1 200 OK
/***************** CardDAV debug *******************/
array(1) {
[0]=>
array(7) {
["response"]=>
string(1393) "
/carddav/SynoContacts//carddav//carddav/SynoContacts//carddav/SynoContacts//carddav/SynoContacts//carddav/SynoContacts//carddav/SynoContacts//carddav/SynoContacts/HTTP/1.1 200 OK"
["http_code"]=>
int(207)
["url"]=>
string(52) "https://carddav_url:port/carddav/SynoContacts/"
["method"]=>
string(8) "PROPFIND"
["content"]=>
NULL
["content_type"]=>
NULL
["header"]=>
string(184) "HTTP/2 207
server: Nginx
date: Sun,03 Jan 2021 10:05:18 GMT
content-type: text/xml; charset=utf-8
content-length: 1393
dav: 1,calendar-access,addressbook,extended-mkcol"
}
}
连接应该没问题,但是使用 $carddav-get()
我应该获得所有 vcf 文件。
那行不通。
如果我通过 finder 连接到 carddav 服务器(我使用的是 macOS),我可以看到这个结构:
有什么想法吗?
解决方法
暂无找到可以解决该程序问题的有效方法,小编努力寻找整理中!
如果你已经找到好的解决方法,欢迎将解决方案带上本链接一起发送给小编。
小编邮箱:dio#foxmail.com (将#修改为@)