2021-05-28 16:20:24 +00:00
|
|
|
<?php
|
|
|
|
|
|
|
|
///////////////////////////////////////
|
|
|
|
// CONFIGURATION OF ACCOUNT DATA //
|
|
|
|
///////////////////////////////////////
|
|
|
|
|
|
|
|
// DO NOT SERVE THIS SCRIPT VIA WEBSERVER
|
2021-05-28 16:22:09 +00:00
|
|
|
require 'quickconfig.php';
|
2021-05-28 16:20:24 +00:00
|
|
|
|
|
|
|
///////////////////////////////////////
|
|
|
|
// END CONFIGURATION OF ACCOUNT DATA //
|
|
|
|
///////////////////////////////////////
|
|
|
|
|
|
|
|
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;
|
|
|
|
|
|
|
|
// This is just a sample logger for demo purposes. You can use any PSR-3 compliant logger,
|
|
|
|
// there are many implementations available (e.g. monolog)
|
|
|
|
class StdoutLogger extends AbstractLogger
|
|
|
|
{
|
|
|
|
public function log($level, $message, array $context = array())
|
|
|
|
{
|
|
|
|
if ($level !== LogLevel::DEBUG) {
|
|
|
|
$ctx = empty($context) ? "" : json_encode($context);
|
|
|
|
echo $message . $ctx . "\n";
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
class EchoSyncHandler implements SyncHandler
|
|
|
|
{
|
|
|
|
public function addressObjectChanged(string $uri, string $etag, ?VCard $card): void
|
|
|
|
{
|
|
|
|
if (isset($card)) {
|
|
|
|
$fn = $card->FN ?? "<no name>";
|
|
|
|
echo " +++ Changed or new card $uri (ETag $etag): $fn\n";
|
|
|
|
} else {
|
|
|
|
echo " +++ Changed or new card $uri (ETag $etag): Error: failed to retrieve/parse card's address data\n";
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
public function addressObjectDeleted(string $uri): void
|
|
|
|
{
|
|
|
|
echo " --- Deleted Card $uri\n";
|
|
|
|
}
|
|
|
|
|
|
|
|
public function getExistingVCardETags(): array
|
|
|
|
{
|
|
|
|
return [];
|
|
|
|
}
|
|
|
|
|
|
|
|
public function finalizeSync(): void
|
|
|
|
{
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
$log = new StdoutLogger();
|
|
|
|
$httplog = new NullLogger(); // parameter could simply be omitted for the same effect
|
|
|
|
|
|
|
|
// Initialize the library. Currently, only objects for logging need to be provided, which are two optional logger
|
|
|
|
// objects implementing the PSR-3 logger interface. The first object logs the log messages of the library itself, the
|
|
|
|
// second can be used to log the HTTP traffic. If no logger is given, no log output will be created. For that, simply
|
|
|
|
// call Config::init() and the library will internally use NullLogger objects.
|
|
|
|
Config::init($log, $httplog);
|
|
|
|
|
|
|
|
// Now create an Account object that contains credentials and discovery information
|
|
|
|
$account = new Account(DISCOVERY_URI, USERNAME, PASSWORD);
|
|
|
|
|
|
|
|
// Discover the addressbooks for that account
|
|
|
|
try {
|
|
|
|
$log->notice("Attempting discovery of addressbooks");
|
|
|
|
|
|
|
|
$discover = new Discovery();
|
|
|
|
$abooks = $discover->discoverAddressbooks($account);
|
|
|
|
} catch (\Exception $e) {
|
|
|
|
$log->error("!!! Error during addressbook discovery: " . $e->getMessage());
|
|
|
|
exit(1);
|
|
|
|
}
|
|
|
|
|
|
|
|
$log->notice(">>> " . count($abooks) . " addressbooks discovered");
|
|
|
|
foreach ($abooks as $abook) {
|
|
|
|
$log->info(">>> - $abook");
|
|
|
|
}
|
|
|
|
|
|
|
|
if (count($abooks) <= 0) {
|
|
|
|
$log->warning("Cannot proceed because no addressbooks were found - exiting");
|
|
|
|
exit(0);
|
|
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////
|
|
|
|
// THE FOLLOWING SHOWS HOW TO PERFORM A SYNCHRONIZATION //
|
|
|
|
//////////////////////////////////////////////////////////
|
|
|
|
$abook = $abooks[0];
|
|
|
|
$synchandler = new EchoSyncHandler();
|
|
|
|
$syncmgr = new Sync();
|
|
|
|
|
|
|
|
// initial sync - we don't have a sync-token yet
|
|
|
|
$log->notice("Performing initial sync");
|
|
|
|
$lastSyncToken = $syncmgr->synchronize($abook, $synchandler, [ "FN" ], "");
|
|
|
|
$log->notice(">>> Initial Sync completed, new sync token is $lastSyncToken");
|
|
|
|
|
|
|
|
// every subsequent sync would be passed the sync-token returned by the previous sync
|
|
|
|
// there most certainly won't be any changes to the preceding one at this point and we
|
|
|
|
// can expect the same sync-token be returned again
|
|
|
|
$log->notice("Performing followup sync");
|
|
|
|
$lastSyncToken = $syncmgr->synchronize($abook, $synchandler, [ "FN" ], $lastSyncToken);
|
|
|
|
$log->notice(">>> Re-Sync completed, new sync token is $lastSyncToken");
|
|
|
|
|
|
|
|
//////////////////////////////////////////////////////////////
|
|
|
|
// THE FOLLOWING SHOWS HOW TO PERFORM CHANGES ON THE SERVER //
|
|
|
|
//////////////////////////////////////////////////////////////
|
|
|
|
|
|
|
|
|
|
|
|
// First, we want to insert a new card, so we create a fresh one
|
|
|
|
// See https://sabre.io/vobject/vcard/ on how to work with Sabre VCards
|
|
|
|
// CardDAV VCards require a UID property, which the carddavclient library will
|
|
|
|
// generate and insert automatically upon storing a new card lacking this property
|
|
|
|
|
|
|
|
try {
|
|
|
|
$vcard = new VCard([
|
|
|
|
'FN' => 'John Doe',
|
|
|
|
'N' => ['Doe', 'John', '', '', ''],
|
|
|
|
]);
|
|
|
|
|
|
|
|
|
|
|
|
$log->notice("Attempting to create a new card on the server");
|
|
|
|
[ 'uri' => $cardUri, 'etag' => $cardETag ] = $abook->createCard($vcard);
|
|
|
|
$log->notice(">>> New card created at $cardUri with ETag $cardETag");
|
|
|
|
|
|
|
|
// now a sync should return that card as well - lets see!
|
|
|
|
$log->notice("Performing followup sync");
|
|
|
|
$lastSyncToken = $syncmgr->synchronize($abook, $synchandler, [ "FN" ], $lastSyncToken);
|
|
|
|
$log->notice(">>> Re-Sync completed, new sync token is $lastSyncToken");
|
|
|
|
|
|
|
|
// add an EMAIL address to the card and update the card on the server
|
|
|
|
$vcard->add(
|
|
|
|
'EMAIL',
|
|
|
|
'johndoe@example.org',
|
|
|
|
[
|
|
|
|
'type' => ['home'],
|
|
|
|
'pref' => 1,
|
|
|
|
]
|
|
|
|
);
|
|
|
|
|
|
|
|
// we pass the ETag of our local copy of the card to updateCard. This
|
|
|
|
// will make the update operation fail if the card has changed on the
|
|
|
|
// server since we fetched our local copy
|
|
|
|
$log->notice("Attempting to update the previously created card at $cardUri");
|
|
|
|
$cardETag = $abook->updateCard($cardUri, $vcard, $cardETag);
|
|
|
|
$log->notice(">>> Card updated, new ETag: $cardETag");
|
|
|
|
|
|
|
|
// again, a sync should report that the card was updated
|
|
|
|
$log->notice("Performing followup sync");
|
|
|
|
$lastSyncToken = $syncmgr->synchronize($abook, $synchandler, [ "FN" ], $lastSyncToken);
|
|
|
|
$log->notice(">>> Re-Sync completed, new sync token is $lastSyncToken");
|
|
|
|
|
|
|
|
// finally, delete the card
|
|
|
|
$log->notice("Deleting card at $cardUri");
|
|
|
|
$abook->deleteCard($cardUri);
|
|
|
|
// now, the sync should report the card was deleted
|
|
|
|
$log->notice("Performing followup sync");
|
|
|
|
$lastSyncToken = $syncmgr->synchronize($abook, $synchandler, [ "FN" ], $lastSyncToken);
|
|
|
|
$log->notice(">>> Re-Sync completed, new sync token is $lastSyncToken");
|
|
|
|
|
|
|
|
$log->notice("All done, good bye");
|
|
|
|
} catch (\Exception $e) {
|
|
|
|
$log->error("Error while making changes to the addressbook: " . $e->getMessage());
|
|
|
|
$log->error("Manual cleanup (deletion of the John Doe card) may be needed");
|
|
|
|
|
|
|
|
// do one final attempt to delete the card
|
|
|
|
try {
|
|
|
|
if (isset($cardUri)) {
|
|
|
|
$abook->deleteCard($cardUri);
|
|
|
|
}
|
|
|
|
} catch (\Exception $e) {
|
|
|
|
}
|
|
|
|
|
|
|
|
exit(1);
|
|
|
|
}
|
|
|
|
|
|
|
|
// vim: ts=4:sw=4:expandtab:fenc=utf8:ff=unix:tw=120
|