Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

ORG processing injecting semicolons into strings during serializing #112

Open
shawnc59 opened this issue Feb 20, 2025 · 4 comments
Open

Comments

@shawnc59
Copy link

shawnc59 commented Feb 20, 2025

Quick Summary
After adding an ORG item to a vCard object, the value looks correct when viewing it in the IDE debugger, but after it's serialized, its string value has semicolons injected between every character.

Context

  • vObject version: 0.9.9
  • Python version: 3.12.3
  • Operating system and version: macOS 15.3.1

Description
Code reads data from Google Contacts and uses it to construct a vCard file. It extracts the org and suborg info using the Contacts API and then vobject to format it for output. In the vcard object (when examined with the VS Code debugger) it looks correct, e.g.:

v.contents['org'][0] = <ORG{}Foo;Foo QA>

But then after using v.serialize() to format it for output:

ORG:F;o;o;\;;F;o;o; ;Q;A`

To Reproduce
A description of how to reproduce the issue, ideally including

  • a short Python script that demonstrates the problem

This is the relevant code:

   v = vobject.vCard()
...
      org = get_attr(organizations[0], "name")
      dept = get_attr(organizations[0], "department")
      if len(org) > 0: v.add("org").value = f"{org};{dept}"
...
print(v.serialize())

Before the call to v.add("org").value, the values for org and dept are "Foo" and "Foo QA" respectively.

  • the iCalendar / vCard that triggers the issue: Not really relevant as I'm creating vCard data from another API.

Further Notes

I assume this is happening in the OrgBehavior() class when the vobject is serialized, but that's just a guess. Also, if I set it to a hardcoded string (i.e., v.add("org").value = "Foo"), it still injects the semicolons: ORG:F;o;o.

@shawnc59
Copy link
Author

Found another issue with serialize() ... when you have a string value with a colon, it looks like it needs to be escaped so apps which import the new vCard data won't treat it as a field separator, e.g.:

item1.URL:https\://testcontact.us

So you can escape the colon when creating the vobject field, e.g.:

v.add(f"item{customItemCnt}.URL").value = (get_attr(urls[0], "value")).replace(":", "\:")

But the serialize() method will escape the escape character:

ITEM1.URL:https\\://testcontact.us

Note: I tried to use v.add("url") originally, but still ran into the double escape issue with serialize().

@da4089
Copy link

da4089 commented Feb 21, 2025

First off, thanks for the report!

I think the problem here is an unfortunate aspect of the vobject API: compound property values like "org" are actually lists.

The call to v.add("org").value = f"{org};{dept}" doesn't behave as expected: it assumes that the string is is a sequence of values, and so when serialising that, it emits them separated by semi-colons. Note that it's expecting the "native" format of the property value here, not the serialised format, so it doesn't attempt to parse the string and separate the two fields itself.

If you do v.add("org").value = [org, dept], all will work as expected:

>>> import vobject
>>> card = vobject.vCard()
>>> card.add("fn").value = "example-fn"   # Required field for vCard 3.0
>>> card.add("org").value = ["example-org", "example-dept"]
>>> print(card.serialize())
BEGIN:VCARD
VERSION:3.0
FN:example-fn
ORG:example-org;example-dept
END:VCARD

>>>

This is definitely an issue with the API, and is a recurring source of problems. I hope to have a good alternative in place in the next major release. Suggestions welcome!

@da4089
Copy link

da4089 commented Feb 21, 2025

Regarding the colon in the property value: this should be accepted by parsers. The exact requirements have changed over v2.1, v3.0, and now v4.0, but in all cases, the value of the URL property doesn't need the colon quoted.

See s2.6.4 in the vCard 2.1 spec, s3.6.8 in vCard 3.0 spec (RFC-2426), and s6.7.8 in vCard 4.0 spec (RFC-6350) (all in the specs dir of the vobject code here on GitHub).

That said, I guess you're using one one that doesn't accept it. As you've discovered, you cannot quote it with a backslash, since the backslash itself must be quoted in the output, leading to the doubling you saw.

One option might be to encode it using base64, however, I'm running into a different bug when I try to do that ... let me play a little more.

Out of curiosity, which parser is rejecting the value containing an colon?

@shawnc59
Copy link
Author

Thanks for the quick response! :-)

  1. the suggestion for ORG worked perfectly.
  2. I actually haven't tried importing the vCard content into anything yet; that's the next step.

What I'm trying to do is write some code which will produce identical output from Google Contacts that the web UI does when you export it as vCard (which is somewhat clunky doing on the web). This is for my own personal use ... I had a scare where I thought I lost all my Google Contacts data which I use as my single source of truth for everything (macOS/iOS/iPadOS apps, Outlook, etc.). Something happened (no idea what) and all my Google Contacts data was gone. Fortunately it mostly had been synced to Apple Contacts and I was able to export vCard data from that and get most of it back into Google. So I wanted to create a tool to quickly get the data with the Google Contacts API and write it out as vCard as my DR in case something goes squirrelly again. So I've created a test contact in Google with all fields populated, exported that manually as vCard and am writing a CLI to recreate that as exactly as possible. Google outputs the colon in the URL field, so was trying to do the same with this code.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants