public function getCards($addressbookId) {
$stmt = $this->pdo->prepare('SELECT id, uri, lastmodified, etag, size FROM ' . $this->cardsTableName . ' WHERE addressbookid = 1 AND status != \'deleted\''); $stmt->execute(array());
$result = []; while($row = $stmt->fetch(\PDO::FETCH_ASSOC)) { $row['etag'] = '"' . $row['etag'] . '"'; $result[] = $row; } return $result;
} public function getCard($addressBookId, $cardUri) {
$stmt = $this->pdo->prepare('SELECT id, carddata, uri, lastmodified, etag, size FROM ' . $this->cardsTableName . ' WHERE addressbookid = 1 AND uri = ? LIMIT 1'); $stmt->execute(array($cardUri));
$result = $stmt->fetch(\PDO::FETCH_ASSOC);
if (!$result) return false;
$result['etag'] = '"' . $result['etag'] . '"'; return $result;
} public function getMultipleCards($addressBookId, array $uris) {
return array_map(function($uri) use ($addressBookId) { return $this->getCard($addressBookId, $uri); }, $uris);
$query = 'SELECT id, uri, lastmodified, etag, size FROM ' . $this->cardsTableName . ' WHERE addressbookid = 1 AND uri = IN ('; // Inserting a whole bunch of question marks $query.=implode(',', array_fill(0, count($uris), '?')); $query.=')';
$stmt = $this->pdo->prepare($query); $stmt->execute(array($uris)); $result = []; while($row = $stmt->fetch(\PDO::FETCH_ASSOC)) { $row['etag'] = '"' . $row['etag'] . '"'; $result[] = $row; } return $result;
} public function createCard($addressBookId, $cardUri, $cardData) {
$stmt = $this->pdo->prepare('INSERT INTO ' . $this->cardsTableName . ' (carddata, uri, lastmodified, addressbookid, size, etag) VALUES (?, ?, ?, 1, ?, ?)');
$etag = md5($cardData);
$result = $stmt->execute([ $cardData, $cardUri, time(), strlen($cardData), $etag, ]);
$this->addChange($addressBookId, $cardUri, 1);
return '"' . $etag . '"';
} public function updateCard($addressBookId, $cardUri, $cardData) {
$stmt = $this->pdo->prepare('UPDATE ' . $this->cardsTableName . ' SET carddata = ?, lastmodified = ?, size = ?, etag = ? WHERE uri = ?');
$etag = md5($cardData); $stmt->execute([ $cardData, time(), strlen($cardData), $etag, $cardUri ]);
$this->addChange($addressBookId, $cardUri, 2);
return '"' . $etag . '"';
}public function getChangesForAddressBook($addressBookId, $syncToken, $syncLevel, $limit = null) {
// Current synctoken $stmt = $this->pdo->prepare('SELECT synctoken FROM ' . $this->addressBooksTableName . ' WHERE id = 1'); $stmt->execute(); $currentToken = $stmt->fetchColumn(0);
if (is_null($currentToken)) return null;
$result = [ 'syncToken' => $currentToken, 'added' => [], 'modified' => [], 'deleted' => [], ];
if ($syncToken) {
$query = "SELECT uri, operation FROM " . $this->addressBookChangesTableName . " WHERE synctoken >= ? AND synctoken < ? AND addressbookid = 1 ORDER BY synctoken"; if ($limit>0) $query.= " LIMIT " . (int)$limit;
// Fetching all changes $stmt = $this->pdo->prepare($query); $stmt->execute([$syncToken, $currentToken]);
$changes = [];
// This loop ensures that any duplicates are overwritten, only the // last change on a node is relevant. while($row = $stmt->fetch(\PDO::FETCH_ASSOC)) {
$changes[$row['uri']] = $row['operation'];
}
foreach($changes as $uri => $operation) {
switch($operation) { case 1: $result['added'][] = $uri; break; case 2: $result['modified'][] = $uri; break; case 3: $result['deleted'][] = $uri; break; }
} } else { // No synctoken supplied, this is the initial sync. $query = "SELECT uri FROM " . $this->cardsTableName . " WHERE addressbookid = ?"; $stmt = $this->pdo->prepare($query); $stmt->execute([$addressBookId]);
$result['added'] = $stmt->fetchAll(\PDO::FETCH_COLUMN); } return $result;
} protected function addChange($addressBookId, $objectUri, $operation) {
$stmt = $this->pdo->prepare('INSERT INTO ' . $this->addressBookChangesTableName .' (uri, synctoken, addressbookid, operation) SELECT ?, synctoken, id, ? FROM ' . $this->addressBooksTableName); $stmt->execute([ $objectUri, $operation ]); $stmt = $this->pdo->prepare('UPDATE ' . $this->addressBooksTableName . ' SET synctoken = synctoken + 1'); $stmt->execute();
}$sql = "UPDATE contacts SET `uri`='$filename', `carddata`='$vCard', `lastmodified`='$timenow', `addressbookid`='1', `size`='$size', `etag`='$etag' WHERE id = '$contactId'"; if (!mysqli_query($con, $sql)) { echo "error updating contact vcard"; echo $sql." "; die('Error: ' . ((is_object($GLOBALS["___mysqli_ston"])) ? mysqli_error($GLOBALS["___mysqli_ston"]) : (($___mysqli_res = mysqli_connect_error()) ? $___mysqli_res : false))); } else { $sql = 'UPDATE addressbooks SET synctoken = synctoken + 1'; mysqli_query($con, $sql); $sql = 'INSERT INTO addressbookchanges (uri, synctoken, addressbookid, operation) SELECT \''.$filename.'\', synctoken, id, 2 FROM addressbooks'; if (!mysqli_query($con, $sql)){ echo "Error! filename: $filename $sql"; } }OPTIONS /principals/jgarrison/ HTTP/1.1Host: DOMAINPragma: no-cacheConnection: keep-aliveAccept: */*User-Agent: Mac OS X/10.9.2 (13C1021) AddressBook/1369Accept-Language: en-usContent-Length: 0Accept-Encoding: gzip, deflate
HTTP/1.1 401 UnauthorizedPROPFIND /addressbooks/jgarrison/ HTTP/1.1Host: DOMAINPragma: no-cacheAccept: */*Brief: tAccept-Language: en-usAccept-Encoding: gzip, deflateContent-Type: text/xmlContent-Length: 717Depth: 1Connection: keep-aliveUser-Agent: Mac OS X/10.9.2 (13C1021) AddressBook/1369Prefer: return-minimal
<?xml version="1.0" encoding="UTF-8"?><A:propfind xmlns:A="DAV:"> <A:prop> <A:add-member/> <D:bulk-requests xmlns:D="http://me.com/_namespace/"/> <A:current-user-privilege-set/> <A:displayname/> <B:max-image-size xmlns:B="urn:ietf:params:xml:ns:carddav"/> <B:max-resource-size xmlns:B="urn:ietf:params:xml:ns:carddav"/> <C:me-card xmlns:C="http://calendarserver.org/ns/"/> <A:owner/> <C:push-transports xmlns:C="http://calendarserver.org/ns/"/> <C:pushkey xmlns:C="http://calendarserver.org/ns/"/> <A:quota-available-bytes/> <A:quota-used-bytes/> <A:resource-id/> <A:resourcetype/> <A:supported-report-set/> <A:sync-token/> </A:prop></A:propfind>
HTTP/1.1 401 UnauthorizedDate: Tue, 15 Jul 2014 15:14:23 GMTServer: Apache/2.2.26 (Unix) mod_wsgi/3.3 Python/2.7.5 mod_fastcgi/2.4.6 mod_ssl/2.2.26 OpenSSL/0.9.8y DAV/2 PHP/5.4.24X-Powered-By: PHP/5.4.24Set-Cookie: PHPSESSID=kdjpvgkll5gh5bpbvucruvf8vtqo00qsanveb3bnm9n0m1kqqqsi4begblu97sv534l75s90m68r5sh17kb077br8nar02g3ca4mu12; path=/Expires: Thu, 19 Nov 1981 08:52:00 GMTCache-Control: no-store, no-cache, must-revalidate, post-check=0, pre-check=0Pragma: no-cacheWWW-Authenticate: Digest realm="SabreDAV",qop="auth",nonce="*********",opaque="*************"MS-Author-Via: DAVContent-Length: 292Keep-Alive: timeout=15, max=98Connection: Keep-AliveContent-Type: application/xml; charset=utf-8
<?xml version="1.0" encoding="utf-8"?><d:error xmlns:d="DAV:" xmlns:s="http://sabredav.org/ns"> <s:sabredav-version>2.0.1</s:sabredav-version> <s:exception>Sabre\DAV\Exception\NotAuthenticated</s:exception> <s:message>No digest authentication headers were found</s:message></d:error>
public function getDigestHash($realm,$username) {
$stmt = $this->pdo->prepare('SELECT digesta1 FROM '.$this->tableName.' WHERE username = ?'); $stmt->execute(array($username)); return $stmt->fetchColumn() ?: null;
}Ok, I think I have properly implemented these changes. However, I'm thinking this problem might actually be originating somewhere else. As I've been playing to Charles, I keep finding two constant errors.