nextcloud / nextcloud/contacts
Image not shown when importing a valid `.vcf`
Nobody has claimed this yet.
- 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
.vcfshould escape the\;too.
Actual behavior
- The correctly escaped
.vcf(with\;in the photo URI) is not displayed correctly in the contact list. - The exported
.vcfdoes 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
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- 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