nuovo / vcard-parser Goto Github PK
View Code? Open in Web Editor NEWEasier and more concise vCard (.vcf) parser for PHP
Home Page: http://nuovo.lv/
License: MIT License
Easier and more concise vCard (.vcf) parser for PHP
Home Page: http://nuovo.lv/
License: MIT License
The parser returns an error when a VCARD contains an AGENT element - reproducible using the example given at http://tools.ietf.org/html/rfc2426#section-3.5.4
If the vcf file contains
begin:vcard
end:vcard
(e.g as exported by Thunderbird) instead of
BEGIN:VCARD
END:VCARD
it fails because it tries to split the file using:
$this -> RawData = explode( 'BEGIN:VCARD', $this -> RawData);
This can be fixed by changing line 132 of vcard.php to
$this -> RawData = preg_split('{^BEGIN:VCARD}miS', $this -> RawData);
Hi,
I am able to create simple entry with type HOME for example:
$vCard -> tel('555-1111', 'Home');
$vCard -> tel('555-1234', 'Work');
But how to do the same for ADR. with type and subvalue like StreetAddress
I am unable to figure it out.
Shold not be like this:
$vCard -> adr('My Country', 'Work', 'StreetAddress');
$vCard -> adr('My Country', 'StreetAddress', 'Work' );
I try to pass it as an array but no luck
$vCard -> adr('My Country', array('StreetAddress', 'Work') );
Any ideas?
Hi,
On lines 92 and 93 it generate PHP warning.
$vCardBeginCount = preg_match_all('{^BEGIN:VCARD}miS', $this -> RawData);
$vCardEndCount = preg_match_all('{^END:VCARD}miS', $this -> RawData);
PHP Warning: preg_match_all() expects at least 3 parameters, 2 given in vCard.php on line 92
PHP Warning: preg_match_all() expects at least 3 parameters, 2 given in vCard.php on line 93
Hi,
As soon as I instantiate the object, this happens:
"Fatal error: Uncaught Error: Xdebug has detected a possible infinite loop, and aborted your script with a stack depth of '256' frames in .../vCard-parser-master/vCard.php on line 576"
Add support for non-standard ("X-...") elements......
Empty vcard are count as valid even so there is no value.
Here are 2 samples:
BEGIN:VCARD
UID:4165-4FBBCC00-33-9470200.vcf
VERSION:3.0
CLASS:PUBLIC
PROFILE:VCARD
END:VCARD
Or
BEGIN:VCARD
VERSION:3.0
PRODID:-//Apple Inc.//Address Book 6.1.2//EN
UID:bb332361-6c7e-4782-babd-d7d5a5f158f4
END:VCARD
The count check should return something different than the NB card as there is no value.
I try to parse vcards from Apple Addressbook, but
foreach ($vCard -> ADR as $Address) {
// do something
}
doesn't work, cause Apples vcards looks like:
item1.ADR;type=HOME:;;Street;Foo;;00000;Bar
item2.ADR;type=HOME:;;Street;Foo;;00000;Bar
Any suggestions? :)
I've grouped my iCloud contacts into groups and would have expected them as CATEGORIES. A search has shown that Apple keeps the groups in
See example:
BEGIN:VCARD
VERSION:3.0
X-ADDRESSBOOKSERVER-KIND:group
PRODID:-//Apple Inc.//iOS 9.3.5//EN
N:PERS
FN:PERS
UID:6284484E-A4E4-42EF-8CFB-301C0DE2B2D7
X-ADDRESSBOOKSERVER-MEMBER:urn:uuid:8C5292AA-F2D8-41DB-A5B6-582C39AD9BF3
X-ADDRESSBOOKSERVER-MEMBER:urn:uuid:35185E23-D65D-46CA-9638-8DB5117740B9...
X-ADDRESSBOOKSERVER-MEMBER:urn:uuid:EDA333D2-2300-43A5-AFE9-045A8A5D6FF1
X-ADDRESSBOOKSERVER-MEMBER:urn:uuid:2A009E2D-5A49-4550-B64A-E4CE40611DEB
X-ADDRESSBOOKSERVER-MEMBER:urn:uuid:44287153-AE5A-4131-BBFF-D20E8F3A0ECD
END:VCARD
It would be extremely helpful if these groups were assigned as CATEGORIES to the respective contacts.
If upload a vcard with multiple contacts shows error message
Fatal error: Uncaught exception 'Exception' with message 'vCard: invalid vCard'
TYPE=WORK:;extended;street 1;city;state;zip;country
is parsed to
Array (
[POBox] =>
[ExtendedAddress] => extended
[StreetAddress] => street
[Locality] => 1
[Region] => city
[PostalCode] => state
[Country] =>zip
[Type] => Array ( [0] => work )
)
If parsed without extended everything is in place.
But when extended is given the street is split and the house-number is set as locality.
Getting fatal error when creating a new Vcard() that has a photo in it's data. Even 128M php memory isn't enough.
Maybe add a possibility to ignore certain vcard properties?
When using the parser some field disapear more likely the 'itemX' and 'X-'.
Where the 'X-' does not have too much value the 'itemX' does have a usefull value.
BEGIN:VCARD
VERSION:3.0
PRODID:-//Apple Inc.//Address Book 6.1.2//EN
N:von Besser;Hans;;Dr.;
FN:Eppendorf Vertrieb Deutschland GmbH
ORG:Eppendorf Vertrieb Deutschland GmbH;Vertriebsspezialist Systeme
EMAIL;type=INTERNET;type=WORK;type=pref:[email protected]
TEL;type=WORK;type=VOICE;type=pref:+49 931 28 67 51
TEL;type=CELL;type=VOICE:+49 171 230 05 95
item1.ADR;type=WORK;type=pref:;;Büro Würzburg\nOberer Kühlenberg 14;Würzburg;;97078;Germany
item1.X-ABADR:de
item2.URL;type=pref:www.eppendorf.de
item2.X-ABLabel:$!!$
X-ABShowAs:COMPANY
CATEGORIES:Personal Address Book
UID:bb332361-6c7e-4782-babd-d7d5a5f158f4
X-ABUID:75F1F559-5C43-4763-8BDE-E02A9FB9F937:ABPerson
END:VCARD
Small bug:
While parsing
$vcard -> url
it returns an array like
Array(
'0' => Array(
'0' => URL
)
);
if it is vcard of 2.1 version
and if it is 3.0 it gives a different result, such as
'0' => Array(
'Value' => URL
)
);
Parse error: syntax error, unexpected T_FUNCTION, expecting ')' in include/vCard.php on line 552
The line is missing the closing ')'
$Parameters = array_map(function($Item))
PHP Parse error: syntax error, unexpected T_VARIABLE in vCard.php on line 209
$ItemIndex = (int)str_ireplace('item', '' $TmpKey[0]);
$ItemIndex = (int)str_ireplace('item', '', $TmpKey[0]);
It seems the NOTE entry is missing
Original issue from CardDAV project, which used your parser.
VERSION:3.0
NICKNAME:alu
EMAIL;TYPE=INTERNET;TYPE=HOME;TYPE=PREF:[email protected]
X-AFTERLOGIC-USE-FRIENDLY-NAME:1
FN:abu
PRODID:-//Afterlogic//6.5.x//EN
UID:b6a17e3a-3b0a-4f8e-9c4d-cec6d570097f
N:apu;asu;;;;
END:VCARD
])
01/11/2012 15:26:20 [ 3524] [DEBUG] [[email protected]] [android1334515447810] BackendCardDAV->_ParseVCardToAS(vCard fileas [abu])
01/11/2012 15:26:20 [ 3524] [WBXML] [[email protected]] [android1334515447810] O <Add>
01/11/2012 15:26:20 [ 3524] [WBXML] [[email protected]] [android1334515447810] O <ServerEntryId>
01/11/2012 15:26:20 [ 3524] [WBXML] [[email protected]] [android1334515447810] O b6a17e3a-3b0a-4f8e-9c4d-cec6d570097f
01/11/2012 15:26:20 [ 3524] [WBXML] [[email protected]] [android1334515447810] O </ServerEntryId>
01/11/2012 15:26:20 [ 3524] [WBXML] [[email protected]] [android1334515447810] O <Data>
01/11/2012 15:26:20 [ 3524] [WBXML] [[email protected]] [android1334515447810] O <POOMCONTACTS:FileAs>
01/11/2012 15:26:20 [ 3524] [WBXML] [[email protected]] [android1334515447810] O abu
01/11/2012 15:26:20 [ 3524] [WBXML] [[email protected]] [android1334515447810] O </POOMCONTACTS:FileAs>
01/11/2012 15:26:20 [ 3524] [WBXML] [[email protected]] [android1334515447810] O <POOMCONTACTS:FirstName>
01/11/2012 15:26:20 [ 3524] [WBXML] [[email protected]] [android1334515447810] O asu
01/11/2012 15:26:20 [ 3524] [WBXML] [[email protected]] [android1334515447810] O </POOMCONTACTS:FirstName>
01/11/2012 15:26:20 [ 3524] [WBXML] [[email protected]] [android1334515447810] O <POOMCONTACTS:LastName>
01/11/2012 15:26:20 [ 3524] [WBXML] [[email protected]] [android1334515447810] O apu
01/11/2012 15:26:20 [ 3524] [WBXML] [[email protected]] [android1334515447810] O </POOMCONTACTS:LastName>
01/11/2012 15:26:20 [ 3524] [WBXML] [[email protected]] [android1334515447810] O <POOMCONTACTS2:NickName>
01/11/2012 15:26:20 [ 3524] [WBXML] [[email protected]] [android1334515447810] O alu
01/11/2012 15:26:20 [ 3524] [WBXML] [[email protected]] [android1334515447810] O </POOMCONTACTS2:NickName>
01/11/2012 15:26:20 [ 3524] [WBXML] [[email protected]] [android1334515447810] O </Data>
01/11/2012 15:26:20 [ 3524] [WBXML] [[email protected]] [android1334515447810] O </Add>
When you crop the photo of an entry in the Apple Addressbook,
it looks like
PHOTO;X-ABCROP-RECTANGLE=ABClipRect_1&-9&20&283&283&WGHe9zKmBvRvhyIyYvN/1g=
=;ENCODING=b;TYPE=JPEG:/9j/4AAQSkZJRgABAQAAAQABAAD/4gQUSUNDX1BST0ZJTEUAAQE
AAAQEYXBwbAIAAABtbnRyUkdCIFhZWiAH2QADAA0AFQAWACNhY3NwQVBQTAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAA9tYAAQAAAADTLWFwcGzV7zp1myHv5rYyPVUXGqoJAAAAAAAAAAAAAAA
which becomes:
array(4) {
[0]=>
string(5) "photo"
[1]=>
string(70) "x-abcrop-rectangle=abcliprect_1&-9&20&283&283&wghe9zkmbvrvhyiyyvn/1g ="
[2]=>
string(10) "encoding=b"
[3]=>
string(9) "type=jpeg"
}
This yields to an endless recursion of self::ParseParameters
Therefore I added this lines:
if ($Key=='photo'){
if ($RawParams!=null){
$RawParams = array_filter($RawParams,create_function('$k','return (substr(trim($k),0,3)=="enc" || substr(trim($k),0,4)=="type");'));
}
}
Now only encoding and type are permitted for photos.
I have been testing this parser library, intending to use it in a project, and have noted some opportunities for changes. They take the following forms:
My changes are as follows, in patch form:
@@ -50,11 +50,13 @@
private static $Spec_ElementTypes = array(
'email' => array('internet', 'x400', 'pref'),
'adr' => array('dom', 'intl', 'postal', 'parcel', 'home', 'work', 'pref'),
'label' => array('dom', 'intl', 'postal', 'parcel', 'home', 'work', 'pref'),
- 'tel' => array('home', 'msg', 'work', 'pref', 'voice', 'fax', 'cell', 'video', 'pager', 'bbs', 'modem', 'car', 'isdn', 'pcs')
+ 'tel' => array('home', 'msg', 'work', 'pref', 'voice', 'fax', 'cell', 'video', 'pager', 'bbs', 'modem', 'car', 'isdn', 'pcs'),
+ 'url' => array('work', 'home'), // not per RFC but may be present in practice
+ 'impp' => array('personal','business','home','work','mobile','pref') // http://tools.ietf.org/html/rfc4770
);
private static $Spec_FileElements = array('photo', 'logo', 'sound');
/**
@@ -138,16 +140,23 @@
$this -> Data[] = new vCard(false, $SinglevCardRawData);
}
}
else
{
+ // Protect the BASE64 final = sign (detected by the line beginning with whitespace), otherwise the next replace will get rid of it
+ $this -> RawData = preg_replace('/(\n\s.+)=(\n)/', '$1-base64=-$2', $this -> RawData);
+
// Joining multiple lines that are split with a hard wrap and indicated by an equals sign at the end of line
+ // i.e. quoted-printable-encoded values in v2.1 vCards
$this -> RawData = str_replace("=\n", '', $this -> RawData);
// Joining multiple lines that are split with a soft wrap (space or tab on the beginning of the next line
$this -> RawData = str_replace(array("\n ", "\n\t"), '-wrap-', $this -> RawData);
+ // Restore the BASE64 =
+ $this -> RawData = str_replace("-base64=-\n", "=\n", $this -> RawData);
+
$Lines = explode("\n", $this -> RawData);
foreach ($Lines as $Line)
{
// Lines without colons are skipped because, most likely, they contain no data.
@@ -160,10 +169,11 @@
// value is just the value
list($Key, $Value) = explode(':', $Line, 2);
// Key is transformed to lowercase because, even though the element and parameter names are written in uppercase,
// it is quite possible that they will be in lower- or mixed case.
+ // The key is trimmed to allow for non-significant WSP characters as allowed by v2.1
$Key = strtolower(trim(self::Unescape($Key)));
// These two lines can be skipped as they aren't necessary at all.
if ($Key == 'begin' || $Key == 'end')
{
@@ -183,10 +193,13 @@
else
{
$Value = str_replace('-wrap-', '', $Value);
}
+ // The value is trimmed to allow for non-significant WSP characters as allowed by v2.1
+ // (This might remove white space which a v3.0 parser is meant to retain)
+ // WARNING! unescaping ; characters at this point causes the ParseStructuredValue later to parse the values wrongly!
$Value = trim(self::Unescape($Value));
$Type = array();
// Here additional parameters are parsed
$KeyParts = explode(';', $Key);
@@ -201,20 +214,22 @@
{
switch ($ParamKey)
{
case 'encoding':
$Encoding = $ParamValue;
- if ($ParamValue == 'b')
+ // Allow for both v3.0 B and v2.1 BASE64 encoding parameter values
+ //if ($ParamValue == 'b')
+ if (in_array($ParamValue, array('b', 'base64')))
{
//$Value = base64_decode($Value);
}
- elseif ($ParamValue == 'quoted-printable')
+ elseif ($ParamValue == 'quoted-printable') // v2.1
{
$Value = quoted_printable_decode($Value);
}
break;
- case 'charset':
+ case 'charset': // v2.1
if ($ParamValue != 'utf-8' && $ParamValue != 'utf8')
{
$Value = mb_convert_encoding($Value, 'UTF-8', $ParamValue);
}
break;
@@ -336,11 +351,13 @@
}
if (is_writable($TargetPath) || (!file_exists($TargetPath) && is_writable(dirname($TargetPath))))
{
$RawContent = $this -> Data[$Key][$Index]['Value'];
- if (isset($this -> Data[$Key][$Index]['Encoding']) && $this -> Data[$Key][$Index]['Encoding'] == 'b')
+ // Allow for both v3.0 B and v2.1 BASE64 encoding parameter values
+ //if (isset($this -> Data[$Key][$Index]['Encoding']) && $this -> Data[$Key][$Index]['Encoding'] == 'b')
+ if (isset($this -> Data[$Key][$Index]['Encoding']) && (in_array($this -> Data[$Key][$Index]['Encoding'], array('b', 'base64'))) )
{
$RawContent = base64_decode($RawContent);
}
$Status = file_put_contents($TargetPath, $RawContent);
return (bool)$Status;
@@ -493,10 +510,12 @@
* @return string Resulting text.
*/
private static function Unescape($Text)
{
return str_replace(array('\:', '\;', '\,', "\n"), array(':', ';', ',', ''), $Text);
+ // The line above removes encoded newlines ("\n") rather than converting them back to CRLF character sequences!
+ // "\N" should be treated in the same way as "\n"
}
/**
* Separates the various parts of a structured value according to the spec.
*
@@ -582,11 +601,13 @@
else
{
switch ($Parameter[0])
{
case 'encoding':
- if (in_array($Parameter[1], array('quoted-printable', 'b')))
+ // Allow for both v3.0 B and v2.1 BASE64 encoding parameter values
+ //if (in_array($Parameter[1], array('quoted-printable', 'b')))
+ if (in_array($Parameter[1], array('quoted-printable', 'b', 'base64')))
{
$Result['encoding'] = $Parameter[1];
}
break;
case 'charset':
This is the output from Apples vCard, I believe it's because of using items.
Array
(
[0] => Array
(
[LastName] =>
[FirstName] =>
[AdditionalNames] =>
[Prefixes] =>
[Suffixes] =>
)
)
Array
(
[0] => Array
(
[Value] => 1-800-MY-APPLE
[Type] => Array
(
[0] => main
[1] => pref
)
)
)
For example, it uses item1.ADR
instead of just ADR
item1.ADR;type=WORK;type=pref:;;1 Infinite Loop;Cupertino;CA;95014;United States
item1.X-ABADR:us
However, we don't even get the address out of it.
This pastebin contains the contents of the card: http://pastebin.com/mdsHFVv6
A declarative, efficient, and flexible JavaScript library for building user interfaces.
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google ❤️ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.