Merge pull request #19901 from nextcloud/bugfix/noid/vcard-photo-handling

Improved vcard photo handling
This commit is contained in:
Roeland Jago Douma 2020-04-17 11:43:20 +02:00 committed by GitHub
commit ed56619a20
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 89 additions and 27 deletions

View File

@ -449,7 +449,7 @@ class CardDavBackend implements BackendInterface, SyncSupport {
*/ */
public function deleteAddressBook($addressBookId) { public function deleteAddressBook($addressBookId) {
$query = $this->db->getQueryBuilder(); $query = $this->db->getQueryBuilder();
$query->delete('cards') $query->delete($this->dbCardsTable)
->where($query->expr()->eq('addressbookid', $query->createParameter('addressbookid'))) ->where($query->expr()->eq('addressbookid', $query->createParameter('addressbookid')))
->setParameter('addressbookid', $addressBookId) ->setParameter('addressbookid', $addressBookId)
->execute(); ->execute();
@ -493,7 +493,7 @@ class CardDavBackend implements BackendInterface, SyncSupport {
public function getCards($addressBookId) { public function getCards($addressBookId) {
$query = $this->db->getQueryBuilder(); $query = $this->db->getQueryBuilder();
$query->select(['id', 'uri', 'lastmodified', 'etag', 'size', 'carddata', 'uid']) $query->select(['id', 'uri', 'lastmodified', 'etag', 'size', 'carddata', 'uid'])
->from('cards') ->from($this->dbCardsTable)
->where($query->expr()->eq('addressbookid', $query->createNamedParameter($addressBookId))); ->where($query->expr()->eq('addressbookid', $query->createNamedParameter($addressBookId)));
$cards = []; $cards = [];
@ -501,7 +501,13 @@ class CardDavBackend implements BackendInterface, SyncSupport {
$result = $query->execute(); $result = $query->execute();
while ($row = $result->fetch()) { while ($row = $result->fetch()) {
$row['etag'] = '"' . $row['etag'] . '"'; $row['etag'] = '"' . $row['etag'] . '"';
$row['carddata'] = $this->readBlob($row['carddata']);
$modified = false;
$row['carddata'] = $this->readBlob($row['carddata'], $modified);
if ($modified) {
$row['size'] = strlen($row['carddata']);
}
$cards[] = $row; $cards[] = $row;
} }
$result->closeCursor(); $result->closeCursor();
@ -524,7 +530,7 @@ class CardDavBackend implements BackendInterface, SyncSupport {
public function getCard($addressBookId, $cardUri) { public function getCard($addressBookId, $cardUri) {
$query = $this->db->getQueryBuilder(); $query = $this->db->getQueryBuilder();
$query->select(['id', 'uri', 'lastmodified', 'etag', 'size', 'carddata', 'uid']) $query->select(['id', 'uri', 'lastmodified', 'etag', 'size', 'carddata', 'uid'])
->from('cards') ->from($this->dbCardsTable)
->where($query->expr()->eq('addressbookid', $query->createNamedParameter($addressBookId))) ->where($query->expr()->eq('addressbookid', $query->createNamedParameter($addressBookId)))
->andWhere($query->expr()->eq('uri', $query->createNamedParameter($cardUri))) ->andWhere($query->expr()->eq('uri', $query->createNamedParameter($cardUri)))
->setMaxResults(1); ->setMaxResults(1);
@ -535,7 +541,12 @@ class CardDavBackend implements BackendInterface, SyncSupport {
return false; return false;
} }
$row['etag'] = '"' . $row['etag'] . '"'; $row['etag'] = '"' . $row['etag'] . '"';
$row['carddata'] = $this->readBlob($row['carddata']);
$modified = false;
$row['carddata'] = $this->readBlob($row['carddata'], $modified);
if ($modified) {
$row['size'] = strlen($row['carddata']);
}
return $row; return $row;
} }
@ -562,7 +573,7 @@ class CardDavBackend implements BackendInterface, SyncSupport {
$query = $this->db->getQueryBuilder(); $query = $this->db->getQueryBuilder();
$query->select(['id', 'uri', 'lastmodified', 'etag', 'size', 'carddata', 'uid']) $query->select(['id', 'uri', 'lastmodified', 'etag', 'size', 'carddata', 'uid'])
->from('cards') ->from($this->dbCardsTable)
->where($query->expr()->eq('addressbookid', $query->createNamedParameter($addressBookId))) ->where($query->expr()->eq('addressbookid', $query->createNamedParameter($addressBookId)))
->andWhere($query->expr()->in('uri', $query->createParameter('uri'))); ->andWhere($query->expr()->in('uri', $query->createParameter('uri')));
@ -572,7 +583,13 @@ class CardDavBackend implements BackendInterface, SyncSupport {
while ($row = $result->fetch()) { while ($row = $result->fetch()) {
$row['etag'] = '"' . $row['etag'] . '"'; $row['etag'] = '"' . $row['etag'] . '"';
$row['carddata'] = $this->readBlob($row['carddata']);
$modified = false;
$row['carddata'] = $this->readBlob($row['carddata'], $modified);
if ($modified) {
$row['size'] = strlen($row['carddata']);
}
$cards[] = $row; $cards[] = $row;
} }
$result->closeCursor(); $result->closeCursor();
@ -611,7 +628,7 @@ class CardDavBackend implements BackendInterface, SyncSupport {
$q = $this->db->getQueryBuilder(); $q = $this->db->getQueryBuilder();
$q->select('uid') $q->select('uid')
->from('cards') ->from($this->dbCardsTable)
->where($q->expr()->eq('addressbookid', $q->createNamedParameter($addressBookId))) ->where($q->expr()->eq('addressbookid', $q->createNamedParameter($addressBookId)))
->andWhere($q->expr()->eq('uid', $q->createNamedParameter($uid))) ->andWhere($q->expr()->eq('uid', $q->createNamedParameter($uid)))
->setMaxResults(1); ->setMaxResults(1);
@ -676,7 +693,7 @@ class CardDavBackend implements BackendInterface, SyncSupport {
$uid = $this->getUID($cardData); $uid = $this->getUID($cardData);
$etag = md5($cardData); $etag = md5($cardData);
$query = $this->db->getQueryBuilder(); $query = $this->db->getQueryBuilder();
$query->update('cards') $query->update($this->dbCardsTable)
->set('carddata', $query->createNamedParameter($cardData, IQueryBuilder::PARAM_LOB)) ->set('carddata', $query->createNamedParameter($cardData, IQueryBuilder::PARAM_LOB))
->set('lastmodified', $query->createNamedParameter(time())) ->set('lastmodified', $query->createNamedParameter(time()))
->set('size', $query->createNamedParameter(strlen($cardData))) ->set('size', $query->createNamedParameter(strlen($cardData)))
@ -712,7 +729,7 @@ class CardDavBackend implements BackendInterface, SyncSupport {
$cardId = null; $cardId = null;
} }
$query = $this->db->getQueryBuilder(); $query = $this->db->getQueryBuilder();
$ret = $query->delete('cards') $ret = $query->delete($this->dbCardsTable)
->where($query->expr()->eq('addressbookid', $query->createNamedParameter($addressBookId))) ->where($query->expr()->eq('addressbookid', $query->createNamedParameter($addressBookId)))
->andWhere($query->expr()->eq('uri', $query->createNamedParameter($cardUri))) ->andWhere($query->expr()->eq('uri', $query->createNamedParameter($cardUri)))
->execute(); ->execute();
@ -872,12 +889,41 @@ class CardDavBackend implements BackendInterface, SyncSupport {
]); ]);
} }
private function readBlob($cardData) { /**
* @param resource|string $cardData
* @param bool $modified
* @return string
*/
private function readBlob($cardData, &$modified=false) {
if (is_resource($cardData)) { if (is_resource($cardData)) {
return stream_get_contents($cardData); $cardData = stream_get_contents($cardData);
} }
return $cardData; $cardDataArray = explode("\r\n", $cardData);
$cardDataFiltered = [];
$removingPhoto = false;
foreach ($cardDataArray as $line) {
if (strpos($line, 'PHOTO:data:') === 0
&& strpos($line, 'PHOTO:data:image/') !== 0) {
// Filter out PHOTO data of non-images
$removingPhoto = true;
$modified = true;
continue;
}
if ($removingPhoto) {
if (strpos($line, ' ') === 0) {
continue;
}
// No leading space means this is a new property
$removingPhoto = false;
}
$cardDataFiltered[] = $line;
}
return implode("\r\n", $cardDataFiltered);
} }
/** /**
@ -929,7 +975,11 @@ class CardDavBackend implements BackendInterface, SyncSupport {
$result->closeCursor(); $result->closeCursor();
return array_map(function ($array) { return array_map(function ($array) {
$array['carddata'] = $this->readBlob($array['carddata']); $modified = false;
$array['carddata'] = $this->readBlob($array['carddata'], $modified);
if ($modified) {
$array['size'] = strlen($array['carddata']);
}
return $array; return $array;
}, $cards); }, $cards);
} }
@ -994,6 +1044,13 @@ class CardDavBackend implements BackendInterface, SyncSupport {
$queryResult->closeCursor(); $queryResult->closeCursor();
if (is_array($contact)) { if (is_array($contact)) {
$modified = false;
$contact['etag'] = '"' . $contact['etag'] . '"';
$contact['carddata'] = $this->readBlob($contact['carddata'], $modified);
if ($modified) {
$contact['size'] = strlen($contact['carddata']);
}
$result = $contact; $result = $contact;
} }

View File

@ -62,7 +62,12 @@ class HasPhotoPlugin extends ServerPlugin {
if ($node instanceof Card) { if ($node instanceof Card) {
$propFind->handle($ns . 'has-photo', function () use ($node) { $propFind->handle($ns . 'has-photo', function () use ($node) {
$vcard = Reader::read($node->get()); $vcard = Reader::read($node->get());
return ($vcard instanceof VCard && $vcard->PHOTO); return $vcard instanceof VCard
&& $vcard->PHOTO
// Either the PHOTO is a url (doesn't start with data:) or the mimetype has to start with image/
&& (strpos($vcard->PHOTO->getValue(), 'data:') !== 0
|| strpos($vcard->PHOTO->getValue(), 'data:image/') === 0)
;
}); });
} }
} }

View File

@ -812,7 +812,7 @@ class CardDavBackendTest extends TestCase {
$this->assertSame(0, (int)$result['addressbookid']); $this->assertSame(0, (int)$result['addressbookid']);
$this->assertSame('uri0', $result['uri']); $this->assertSame('uri0', $result['uri']);
$this->assertSame(5489543, (int)$result['lastmodified']); $this->assertSame(5489543, (int)$result['lastmodified']);
$this->assertSame('etag0', $result['etag']); $this->assertSame('"etag0"', $result['etag']);
$this->assertSame(120, (int)$result['size']); $this->assertSame(120, (int)$result['size']);
// this shouldn't return any result because 'uri1' is in address book 1 // this shouldn't return any result because 'uri1' is in address book 1