Skip to content

Commit ed56619

Browse files
authored
Merge pull request #19901 from nextcloud/bugfix/noid/vcard-photo-handling
Improved vcard photo handling
2 parents 7c4b7e9 + fe4527a commit ed56619

3 files changed

Lines changed: 89 additions & 27 deletions

File tree

apps/dav/lib/CardDAV/CardDavBackend.php

Lines changed: 71 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -449,7 +449,7 @@ public function createAddressBook($principalUri, $url, array $properties) {
449449
*/
450450
public function deleteAddressBook($addressBookId) {
451451
$query = $this->db->getQueryBuilder();
452-
$query->delete('cards')
452+
$query->delete($this->dbCardsTable)
453453
->where($query->expr()->eq('addressbookid', $query->createParameter('addressbookid')))
454454
->setParameter('addressbookid', $addressBookId)
455455
->execute();
@@ -493,15 +493,21 @@ public function deleteAddressBook($addressBookId) {
493493
public function getCards($addressBookId) {
494494
$query = $this->db->getQueryBuilder();
495495
$query->select(['id', 'uri', 'lastmodified', 'etag', 'size', 'carddata', 'uid'])
496-
->from('cards')
496+
->from($this->dbCardsTable)
497497
->where($query->expr()->eq('addressbookid', $query->createNamedParameter($addressBookId)));
498498

499499
$cards = [];
500500

501501
$result = $query->execute();
502502
while ($row = $result->fetch()) {
503503
$row['etag'] = '"' . $row['etag'] . '"';
504-
$row['carddata'] = $this->readBlob($row['carddata']);
504+
505+
$modified = false;
506+
$row['carddata'] = $this->readBlob($row['carddata'], $modified);
507+
if ($modified) {
508+
$row['size'] = strlen($row['carddata']);
509+
}
510+
505511
$cards[] = $row;
506512
}
507513
$result->closeCursor();
@@ -524,7 +530,7 @@ public function getCards($addressBookId) {
524530
public function getCard($addressBookId, $cardUri) {
525531
$query = $this->db->getQueryBuilder();
526532
$query->select(['id', 'uri', 'lastmodified', 'etag', 'size', 'carddata', 'uid'])
527-
->from('cards')
533+
->from($this->dbCardsTable)
528534
->where($query->expr()->eq('addressbookid', $query->createNamedParameter($addressBookId)))
529535
->andWhere($query->expr()->eq('uri', $query->createNamedParameter($cardUri)))
530536
->setMaxResults(1);
@@ -535,7 +541,12 @@ public function getCard($addressBookId, $cardUri) {
535541
return false;
536542
}
537543
$row['etag'] = '"' . $row['etag'] . '"';
538-
$row['carddata'] = $this->readBlob($row['carddata']);
544+
545+
$modified = false;
546+
$row['carddata'] = $this->readBlob($row['carddata'], $modified);
547+
if ($modified) {
548+
$row['size'] = strlen($row['carddata']);
549+
}
539550

540551
return $row;
541552
}
@@ -562,7 +573,7 @@ public function getMultipleCards($addressBookId, array $uris) {
562573

563574
$query = $this->db->getQueryBuilder();
564575
$query->select(['id', 'uri', 'lastmodified', 'etag', 'size', 'carddata', 'uid'])
565-
->from('cards')
576+
->from($this->dbCardsTable)
566577
->where($query->expr()->eq('addressbookid', $query->createNamedParameter($addressBookId)))
567578
->andWhere($query->expr()->in('uri', $query->createParameter('uri')));
568579

@@ -572,7 +583,13 @@ public function getMultipleCards($addressBookId, array $uris) {
572583

573584
while ($row = $result->fetch()) {
574585
$row['etag'] = '"' . $row['etag'] . '"';
575-
$row['carddata'] = $this->readBlob($row['carddata']);
586+
587+
$modified = false;
588+
$row['carddata'] = $this->readBlob($row['carddata'], $modified);
589+
if ($modified) {
590+
$row['size'] = strlen($row['carddata']);
591+
}
592+
576593
$cards[] = $row;
577594
}
578595
$result->closeCursor();
@@ -611,7 +628,7 @@ public function createCard($addressBookId, $cardUri, $cardData) {
611628

612629
$q = $this->db->getQueryBuilder();
613630
$q->select('uid')
614-
->from('cards')
631+
->from($this->dbCardsTable)
615632
->where($q->expr()->eq('addressbookid', $q->createNamedParameter($addressBookId)))
616633
->andWhere($q->expr()->eq('uid', $q->createNamedParameter($uid)))
617634
->setMaxResults(1);
@@ -676,7 +693,7 @@ public function updateCard($addressBookId, $cardUri, $cardData) {
676693
$uid = $this->getUID($cardData);
677694
$etag = md5($cardData);
678695
$query = $this->db->getQueryBuilder();
679-
$query->update('cards')
696+
$query->update($this->dbCardsTable)
680697
->set('carddata', $query->createNamedParameter($cardData, IQueryBuilder::PARAM_LOB))
681698
->set('lastmodified', $query->createNamedParameter(time()))
682699
->set('size', $query->createNamedParameter(strlen($cardData)))
@@ -712,7 +729,7 @@ public function deleteCard($addressBookId, $cardUri) {
712729
$cardId = null;
713730
}
714731
$query = $this->db->getQueryBuilder();
715-
$ret = $query->delete('cards')
732+
$ret = $query->delete($this->dbCardsTable)
716733
->where($query->expr()->eq('addressbookid', $query->createNamedParameter($addressBookId)))
717734
->andWhere($query->expr()->eq('uri', $query->createNamedParameter($cardUri)))
718735
->execute();
@@ -872,12 +889,41 @@ protected function addChange($addressBookId, $objectUri, $operation) {
872889
]);
873890
}
874891

875-
private function readBlob($cardData) {
892+
/**
893+
* @param resource|string $cardData
894+
* @param bool $modified
895+
* @return string
896+
*/
897+
private function readBlob($cardData, &$modified=false) {
876898
if (is_resource($cardData)) {
877-
return stream_get_contents($cardData);
899+
$cardData = stream_get_contents($cardData);
900+
}
901+
902+
$cardDataArray = explode("\r\n", $cardData);
903+
904+
$cardDataFiltered = [];
905+
$removingPhoto = false;
906+
foreach ($cardDataArray as $line) {
907+
if (strpos($line, 'PHOTO:data:') === 0
908+
&& strpos($line, 'PHOTO:data:image/') !== 0) {
909+
// Filter out PHOTO data of non-images
910+
$removingPhoto = true;
911+
$modified = true;
912+
continue;
913+
}
914+
915+
if ($removingPhoto) {
916+
if (strpos($line, ' ') === 0) {
917+
continue;
918+
}
919+
// No leading space means this is a new property
920+
$removingPhoto = false;
921+
}
922+
923+
$cardDataFiltered[] = $line;
878924
}
879925

880-
return $cardData;
926+
return implode("\r\n", $cardDataFiltered);
881927
}
882928

883929
/**
@@ -929,7 +975,11 @@ public function search($addressBookId, $pattern, $searchProperties, $options = [
929975
$result->closeCursor();
930976

931977
return array_map(function ($array) {
932-
$array['carddata'] = $this->readBlob($array['carddata']);
978+
$modified = false;
979+
$array['carddata'] = $this->readBlob($array['carddata'], $modified);
980+
if ($modified) {
981+
$array['size'] = strlen($array['carddata']);
982+
}
933983
return $array;
934984
}, $cards);
935985
}
@@ -994,6 +1044,13 @@ public function getContact($addressBookId, $uri) {
9941044
$queryResult->closeCursor();
9951045

9961046
if (is_array($contact)) {
1047+
$modified = false;
1048+
$contact['etag'] = '"' . $contact['etag'] . '"';
1049+
$contact['carddata'] = $this->readBlob($contact['carddata'], $modified);
1050+
if ($modified) {
1051+
$contact['size'] = strlen($contact['carddata']);
1052+
}
1053+
9971054
$result = $contact;
9981055
}
9991056

apps/dav/lib/CardDAV/HasPhotoPlugin.php

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -62,7 +62,12 @@ public function propFind(PropFind $propFind, INode $node) {
6262
if ($node instanceof Card) {
6363
$propFind->handle($ns . 'has-photo', function () use ($node) {
6464
$vcard = Reader::read($node->get());
65-
return ($vcard instanceof VCard && $vcard->PHOTO);
65+
return $vcard instanceof VCard
66+
&& $vcard->PHOTO
67+
// Either the PHOTO is a url (doesn't start with data:) or the mimetype has to start with image/
68+
&& (strpos($vcard->PHOTO->getValue(), 'data:') !== 0
69+
|| strpos($vcard->PHOTO->getValue(), 'data:image/') === 0)
70+
;
6671
});
6772
}
6873
}

apps/dav/tests/unit/CardDAV/CardDavBackendTest.php

Lines changed: 12 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -630,7 +630,7 @@ public function testGetCardId() {
630630
$this->invokePrivate($this->backend, 'getCardId', [1, 'uri']));
631631
}
632632

633-
633+
634634
public function testGetCardIdFailed() {
635635
$this->expectException(\InvalidArgumentException::class);
636636

@@ -690,15 +690,15 @@ public function testSearch($pattern, $properties, $options, $expected) {
690690
);
691691
$query->execute();
692692
$query->insert($this->dbCardsPropertiesTable)
693-
->values(
694-
[
695-
'addressbookid' => $query->createNamedParameter(0),
696-
'cardid' => $query->createNamedParameter($vCardIds[0]),
697-
'name' => $query->createNamedParameter('CLOUD'),
698-
'value' => $query->createNamedParameter('John@nextcloud.com'),
699-
'preferred' => $query->createNamedParameter(0)
700-
]
701-
);
693+
->values(
694+
[
695+
'addressbookid' => $query->createNamedParameter(0),
696+
'cardid' => $query->createNamedParameter($vCardIds[0]),
697+
'name' => $query->createNamedParameter('CLOUD'),
698+
'value' => $query->createNamedParameter('John@nextcloud.com'),
699+
'preferred' => $query->createNamedParameter(0)
700+
]
701+
);
702702
$query->execute();
703703
$query->insert($this->dbCardsPropertiesTable)
704704
->values(
@@ -783,7 +783,7 @@ public function testGetCardUri() {
783783
$this->assertSame('uri', $this->backend->getCardUri($id));
784784
}
785785

786-
786+
787787
public function testGetCardUriFailed() {
788788
$this->expectException(\InvalidArgumentException::class);
789789

@@ -812,7 +812,7 @@ public function testGetContact() {
812812
$this->assertSame(0, (int)$result['addressbookid']);
813813
$this->assertSame('uri0', $result['uri']);
814814
$this->assertSame(5489543, (int)$result['lastmodified']);
815-
$this->assertSame('etag0', $result['etag']);
815+
$this->assertSame('"etag0"', $result['etag']);
816816
$this->assertSame(120, (int)$result['size']);
817817

818818
// this shouldn't return any result because 'uri1' is in address book 1

0 commit comments

Comments
 (0)