2021-05-28 15:49:42 +00:00
|
|
|
<?php
|
|
|
|
/**
|
2021-05-28 16:01:42 +00:00
|
|
|
* plugins/abook_carddav/abook_class.php -- main class
|
2021-05-28 15:49:42 +00:00
|
|
|
*
|
2021-05-28 16:01:42 +00:00
|
|
|
* SquirrelMail Address Book CardDAV Backend
|
|
|
|
* Copyright (C) 2021 Aleksei Shpakovsky
|
|
|
|
* This program is licensed under GPLv3. See COPYING for details
|
|
|
|
* based on:
|
2021-05-28 15:49:42 +00:00
|
|
|
* SquirrelMail Address Book Backend template
|
|
|
|
* Copyright (C) 2004 Tomas Kuliavas <tokul@users.sourceforge.net>
|
|
|
|
* This program is licensed under GPL. See COPYING for details
|
|
|
|
*/
|
|
|
|
|
2021-05-28 20:49:31 +00:00
|
|
|
require 'quickconfig.php';
|
|
|
|
require 'vendor/autoload.php';
|
|
|
|
|
|
|
|
use MStilkerich\CardDavClient\{Account, AddressbookCollection, Config};
|
|
|
|
use MStilkerich\CardDavClient\Services\{Discovery, Sync, SyncHandler};
|
|
|
|
|
|
|
|
use Psr\Log\{AbstractLogger, NullLogger, LogLevel};
|
|
|
|
use Sabre\VObject\Component\VCard;
|
|
|
|
|
|
|
|
class NullSyncHandler implements SyncHandler
|
|
|
|
{
|
|
|
|
public function addressObjectChanged(string $uri, string $etag, ?VCard $card): void
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|
|
|
|
public function addressObjectDeleted(string $uri): void
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|
|
|
|
public function getExistingVCardETags(): array
|
|
|
|
{
|
|
|
|
return [];
|
|
|
|
}
|
|
|
|
|
|
|
|
public function finalizeSync(): void
|
|
|
|
{
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-05-28 21:18:54 +00:00
|
|
|
Config::init();
|
2021-05-28 20:49:31 +00:00
|
|
|
|
2021-05-28 15:49:42 +00:00
|
|
|
/**
|
2021-05-28 16:01:42 +00:00
|
|
|
* address book carddav backend class
|
2021-05-28 15:49:42 +00:00
|
|
|
*/
|
2021-05-28 16:01:42 +00:00
|
|
|
class abook_carddav extends addressbook_backend {
|
2021-05-28 15:49:42 +00:00
|
|
|
var $btype = 'local';
|
2021-05-28 16:01:42 +00:00
|
|
|
var $bname = 'carddav';
|
2021-05-28 22:35:01 +00:00
|
|
|
var $writeable = false;
|
2021-05-28 15:49:42 +00:00
|
|
|
|
|
|
|
/* ========================== Private ======================= */
|
|
|
|
|
|
|
|
/* Constructor */
|
2021-05-28 16:01:42 +00:00
|
|
|
function abook_carddav($param) {
|
2021-05-28 20:49:31 +00:00
|
|
|
$this->sname = _("carddav address book");
|
2021-05-28 15:49:42 +00:00
|
|
|
|
|
|
|
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'];
|
|
|
|
}
|
|
|
|
|
2021-05-28 21:22:23 +00:00
|
|
|
return $this->open(true);
|
2021-05-28 15:49:42 +00:00
|
|
|
}
|
|
|
|
else {
|
|
|
|
return $this->set_error('Invalid argument to constructor');
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
*
|
|
|
|
*/
|
|
|
|
function open() {
|
2021-05-28 20:49:31 +00:00
|
|
|
// backend open function
|
2021-05-28 21:04:28 +00:00
|
|
|
$this->account = new Account(DISCOVERY_URI, USERNAME, PASSWORD);
|
2021-05-28 20:49:31 +00:00
|
|
|
// Discover the addressbooks for that account
|
|
|
|
try {
|
|
|
|
$discover = new Discovery();
|
2021-05-28 21:04:28 +00:00
|
|
|
$abooks = $discover->discoverAddressbooks($this->account);
|
2021-05-28 20:49:31 +00:00
|
|
|
} catch (\Exception $e) {
|
2021-05-28 21:22:23 +00:00
|
|
|
return $this->set_error("!!! Error during addressbook discovery: " . $e->getMessage());
|
2021-05-28 20:49:31 +00:00
|
|
|
}
|
|
|
|
if (count($abooks) <= 0) {
|
2021-05-28 21:22:23 +00:00
|
|
|
return $this->set_error("Cannot proceed because no addressbooks were found - exiting");
|
2021-05-28 20:49:31 +00:00
|
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////
|
|
|
|
// THE FOLLOWING SHOWS HOW TO PERFORM A SYNCHRONIZATION //
|
|
|
|
//////////////////////////////////////////////////////////
|
|
|
|
$this->abook = $abooks[0];
|
|
|
|
$this->synchandler = new NullSyncHandler();
|
|
|
|
$this->syncmgr = new Sync();
|
|
|
|
|
|
|
|
// initial sync - we don't have a sync-token yet
|
2021-05-28 22:35:01 +00:00
|
|
|
$this->lastSyncToken = $this->syncmgr->synchronize($this->abook, $this->synchandler, ["FN", "N", "EMAIL", "ORG"], "");
|
2021-05-28 15:49:42 +00:00
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
*
|
|
|
|
*/
|
|
|
|
function close() {
|
|
|
|
// ADDME: backend close function
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/* ========================== Public ======================== */
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Search address function
|
|
|
|
* @param expr string search expression
|
|
|
|
*/
|
|
|
|
function search($expr) {
|
|
|
|
$ret = array();
|
|
|
|
|
|
|
|
// ADDME: search by nickname function
|
|
|
|
|
|
|
|
return $ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Lookup alias
|
|
|
|
* @param alias string
|
|
|
|
*/
|
2021-05-28 20:49:31 +00:00
|
|
|
function lookup($value, $field=SM_ABOOK_FIELD_NICKNAME) {
|
|
|
|
// if (empty($alias)) {
|
2021-05-28 15:49:42 +00:00
|
|
|
return array();
|
2021-05-28 20:49:31 +00:00
|
|
|
// }
|
|
|
|
/*
|
2021-05-28 15:49:42 +00:00
|
|
|
|
|
|
|
$alias = strtolower($alias);
|
|
|
|
|
|
|
|
// ADDME: address lookup function
|
|
|
|
|
|
|
|
$ret = array('nickname' => "nickname",
|
|
|
|
'name' => "firstname lastname",
|
|
|
|
'firstname' => "firstname",
|
|
|
|
'lastname' => "lastname",
|
|
|
|
'email' => "email@address",
|
|
|
|
'label' => "info",
|
|
|
|
'backend' => $this->bnum,
|
|
|
|
'source' => $this->sname);
|
|
|
|
|
|
|
|
return $ret;
|
2021-05-28 20:49:31 +00:00
|
|
|
*/
|
2021-05-28 15:49:42 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* List all addresses
|
|
|
|
* @return array
|
|
|
|
*/
|
|
|
|
function list_addr() {
|
|
|
|
$ret = array();
|
|
|
|
|
2021-05-28 20:49:31 +00:00
|
|
|
// list all addresses having an email
|
2021-05-28 22:35:01 +00:00
|
|
|
$all=$this->abook->query(['EMAIL' => "//"],["FN", "N", "EMAIL", "ORG"]);
|
2021-05-28 20:49:31 +00:00
|
|
|
/*
|
|
|
|
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'];
|
|
|
|
$names = $vcard->N->getParts();
|
|
|
|
// last,first,additional,prefix,suffix
|
|
|
|
array_push($ret,array(
|
2021-05-28 22:35:08 +00:00
|
|
|
'nickname' => $uri,
|
2021-05-28 20:49:31 +00:00
|
|
|
'name' => (string)$vcard->FN,
|
|
|
|
'firstname' => (string)$names[1],
|
|
|
|
'lastname' => (string)$names[0],
|
|
|
|
'email' => (string)$vcard->EMAIL,
|
|
|
|
'label' => (string)$vcard->ORG,
|
2021-05-28 15:49:42 +00:00
|
|
|
'backend' => $this->bnum,
|
|
|
|
'source' => $this->sname));
|
2021-05-28 20:49:31 +00:00
|
|
|
}
|
2021-05-28 15:49:42 +00:00
|
|
|
|
|
|
|
return $ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Add address
|
|
|
|
* @param userdata
|
|
|
|
* @return boolean
|
|
|
|
*/
|
|
|
|
function add($userdata) {
|
|
|
|
if (!$this->writeable) {
|
|
|
|
return $this->set_error(_("Addressbook is read-only"));
|
|
|
|
}
|
|
|
|
|
|
|
|
/* See if user exist already */
|
2021-05-28 20:49:31 +00:00
|
|
|
/*
|
2021-05-28 15:49:42 +00:00
|
|
|
$ret = $this->lookup($userdata['nickname']);
|
|
|
|
if (!empty($ret)) {
|
|
|
|
return $this->set_error(sprintf(_("User '%s' already exist"),
|
|
|
|
$ret['nickname']));
|
|
|
|
}
|
2021-05-28 20:49:31 +00:00
|
|
|
*/
|
|
|
|
try {
|
|
|
|
$vcard = new VCard([
|
|
|
|
'FN' => $userdata['name'],
|
|
|
|
'N' => [$userdata['lastname'], $userdata['firstname'], '', '', ''],
|
|
|
|
'EMAIL' => $userdata['email'],
|
|
|
|
'ORG' => $userdata['label'],
|
|
|
|
]);
|
|
|
|
|
|
|
|
// insert address function
|
|
|
|
$this->abook->createCard($vcard);
|
|
|
|
// now a sync should return that card as well - lets see!
|
|
|
|
$this->lastSyncToken = $this->syncmgr->synchronize($this->abook, $this->synchandler, ["FN", "N", "EMAIL", "ORG"], $this->lastSyncToken);
|
|
|
|
|
|
|
|
// return true if operation is succesful.
|
|
|
|
return true;
|
|
|
|
} catch (\Exception $e) {
|
|
|
|
// Return error message if operation fails
|
|
|
|
return $this->set_error(_("Address add operation failed: ") . $e->getMessage());
|
|
|
|
}
|
2021-05-28 15:49:42 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Delete address
|
|
|
|
* @param alias
|
|
|
|
* @return boolean
|
|
|
|
*/
|
|
|
|
function remove($alias) {
|
|
|
|
if (!$this->writeable) {
|
|
|
|
return $this->set_error(_("Addressbook is read-only"));
|
|
|
|
}
|
|
|
|
|
|
|
|
// ADD: delete address function
|
|
|
|
|
|
|
|
// FIXME:
|
|
|
|
// return true if operation is succesful.
|
|
|
|
return true;
|
|
|
|
// Return error message if operation fails
|
|
|
|
return $this->set_error(_("Address delete operation failed"));
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Modify address
|
|
|
|
* @param alias
|
|
|
|
* @param userdata
|
|
|
|
* @return boolean
|
|
|
|
*/
|
|
|
|
function modify($alias, $userdata) {
|
|
|
|
if (!$this->writeable) {
|
|
|
|
return $this->set_error(_("Addressbook is read-only"));
|
|
|
|
}
|
|
|
|
|
|
|
|
/* See if user exist */
|
|
|
|
$ret = $this->lookup($alias);
|
|
|
|
if (empty($ret)) {
|
|
|
|
return $this->set_error(sprintf(_("User '%s' does not exist"),
|
|
|
|
$alias));
|
|
|
|
}
|
|
|
|
// ADD: modify address function
|
|
|
|
|
|
|
|
// FIXME:
|
|
|
|
// return true if operation is succesful.
|
|
|
|
return true;
|
|
|
|
// Return error message if operation fails
|
|
|
|
return $this->set_error(_("Address modify operation failed"));
|
|
|
|
}
|
2021-05-28 16:01:42 +00:00
|
|
|
} /* End of class abook_carddav */
|
|
|
|
?>
|