nextcloud / nextcloud/contacts

Image not shown when importing a valid `.vcf`

Open
#4,189 1 comment 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

1. to develop bug feature: contacts
Dominant language
JavaScript
Stars
642
Forks
220
Avg merge
14h 39m
Merged PRs (30d)
51

Description

Describe the bug

When importing a .vcf file with the (expected) escaped \; inside the PHOTO child value, the contact photo is not correctly displayed in the contact list.

Steps to reproduce

Create a contacts.vcf file like:

BEGIN:VCARD
VERSION:4.0
UID:898c5f96-8bfc-4d35-a198-085d25fb27c5
FN:JPEG escape
ORG:ASDF
PHOTO;VALUE=URI:data:image/jpeg\;base64\,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
END:VCARD
BEGIN:VCARD
VERSION:4.0
UID:c99a0a23-0800-417d-ba56-f3411628598c
FN:JPEG no escape
ORG:ASDF
PHOTO;VALUE=URI:data:image/jpeg;base64\,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
END:VCARD

Where the only difference is (note the escaped \; in one of them):

PHOTO;VALUE=URI:data:image/jpeg\;base64\,/9j/4AAQSkZJR...
PHOTO;VALUE=URI:data:image/jpeg;base64\,/9j/4AAQSkZJR...

Import the file into a new address book.

Right after importing, both contacts have a profile picture in the list and within the contact details:

https://github.com/user-attachments/assets/cd364d9f-d8fd-4643-83f4-3e7a8de41fe3

Now, after refreshing the view (or, basically, any time you open the contacts app afterwards), the escaped contact is not displayed on the list, nor when clicking the first time to see the contact details:

https://github.com/user-attachments/assets/3dbf5b58-060f-423c-af2e-4712a35ee785

The browser's console shows a 404 error on contact list load:

EDDD3AFC-D6B0-4D59-862E-D91CCA43E090.vcf:1
  GET https://domain.com/remote.php/dav/addressbooks/users/User/asdf/EDDD3AFC-D6B0-4D59-862E-D91CCA43E090.vcf?photo 404 (Not Found)

Also, when downloading the address book as a .vcf file after import+refresh, the downloaded address book has two contacts with the following PHOTO value:

PHOTO;VALUE=URI:data:image/jpeg;base64\,/9j/4AAQSkZJR...
PHOTO;VALUE=URI:data:image/jpeg\\;base64\,/9j/4AAQSkZJR...

So, the unescaped contact is exported unescaped again, but the escaped contact is exported with two \\ next to the ;.

It looks like both exports are incorrect, since according to vobject:

import vobject

card = vobject.vCard()

card.add('fn')
card.fn.value = "John Doe"

card.add('photo')
card.photo.params = {"VALUE": ["URI"]}
card.photo.value = "URI:data:image/jpeg;base64,/9j/4AAQSkZJR..."

print(card.serialize())

The serialization should be like (escaping both ; and ,):

BEGIN:VCARD
VERSION:3.0
FN:John Doe
PHOTO;VALUE=URI:URI:data:image/jpeg\;base64\,/9j/4AAQSkZJR...
END:VCARD
Expected behavior
  • The correctly escaped .vcf (with \; in the photo URI) should be displayed correctly in the contact list.
  • The exported .vcf should escape the \; too.
Actual behavior
  • The correctly escaped .vcf (with \; in the photo URI) is not displayed correctly in the contact list.
  • The exported .vcf does not escape \; , or it escapes it twice (if it was imported with the escaped \;.
Contact version

6.1.0

Operating system

No response

PHP engine version

None

Web server

None

Database

None

Additional info

No response

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Reproduce the issue by importing the two-card VCF and refreshing the contact list. Start with the contacts app's import/export and contact-list loading paths, including the DAV request ending in ?photo, and compare the PHOTO values before and after export. Done means the escaped contact remains visible and exported PHOTO values preserve the required escaping without doubling it.

Written by the indexing model from the issue text.

Assessment

Tech stack
javascript
Domain
api, frontend
Issue type
Bug
Difficulty
4/5
Estimated time
3-5 days
Activity status
Stale
Clarity
Mostly clear
Newbie friendliness
38/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.