-
Notifications
You must be signed in to change notification settings - Fork 4
/
quickstart.php
196 lines (159 loc) · 7.27 KB
/
quickstart.php
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
<?php
///////////////////////////////////////
// CONFIGURATION OF ACCOUNT DATA //
///////////////////////////////////////
// To run, configure the account data in this block and remove / comment out
// the following line with the exit statement
// DO NOT SERVE THIS SCRIPT VIA WEBSERVER
echo "Please configure first - exiting\n"; exit(1);
const DISCOVERY_URI = "example.com";
const USERNAME = "[email protected]";
// Note: for providers using 2-factor authentication (common today, e.g. Apple iCloud,
// Google, Nextcloud if 2FA enabled by user), you need to provide an application-specific
// password here, not your account password. You can typically create such
// application-specific passwords in the account settings of your provider.
const PASSWORD = "theSecretPassword";
///////////////////////////////////////
// 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()): void
{
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" => USERNAME, "password" => 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',
[
'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