Page Menu
Home
Phabricator
Search
Configure Global Search
Log In
Files
F1819717
VCardConverter.php
No One
Temporary
Actions
Download File
Edit File
Delete File
View Transforms
Subscribe
Mute Notifications
Size
15 KB
Subscribers
None
VCardConverter.php
View Options
<?php
namespace
Sabre\VObject
;
/**
* This utility converts vcards from one version to another.
*
* @copyright Copyright (C) 2011-2015 fruux GmbH (https://fruux.com/).
* @author Evert Pot (http://evertpot.com/)
* @license http://sabre.io/license/ Modified BSD License
*/
class
VCardConverter
{
/**
* Converts a vCard object to a new version.
*
* targetVersion must be one of:
* Document::VCARD21
* Document::VCARD30
* Document::VCARD40
*
* Currently only 3.0 and 4.0 as input and output versions.
*
* 2.1 has some minor support for the input version, it's incomplete at the
* moment though.
*
* If input and output version are identical, a clone is returned.
*
* @param Component\VCard $input
* @param int $targetVersion
*/
public
function
convert
(
Component\VCard
$input
,
$targetVersion
)
{
$inputVersion
=
$input
->
getDocumentType
();
if
(
$inputVersion
===
$targetVersion
)
{
return
clone
$input
;
}
if
(!
in_array
(
$inputVersion
,
array
(
Document
::
VCARD21
,
Document
::
VCARD30
,
Document
::
VCARD40
)))
{
throw
new
\InvalidArgumentException
(
'Only vCard 2.1, 3.0 and 4.0 are supported for the input data'
);
}
if
(!
in_array
(
$targetVersion
,
array
(
Document
::
VCARD30
,
Document
::
VCARD40
)))
{
throw
new
\InvalidArgumentException
(
'You can only use vCard 3.0 or 4.0 for the target version'
);
}
$newVersion
=
$targetVersion
===
Document
::
VCARD40
?
'4.0'
:
'3.0'
;
$output
=
new
Component\VCard
(
array
(
'VERSION'
=>
$newVersion
,
));
foreach
(
$input
->
children
as
$property
)
{
$this
->
convertProperty
(
$input
,
$output
,
$property
,
$targetVersion
);
}
return
$output
;
}
/**
* Handles conversion of a single property.
*
* @param Component\VCard $input
* @param Component\VCard $output
* @param Property $property
* @param int $targetVersion
* @return void
*/
protected
function
convertProperty
(
Component\VCard
$input
,
Component\VCard
$output
,
Property
$property
,
$targetVersion
)
{
// Skipping these, those are automatically added.
if
(
in_array
(
$property
->
name
,
array
(
'VERSION'
,
'PRODID'
)))
{
return
;
}
$parameters
=
$property
->
parameters
();
$valueType
=
null
;
if
(
isset
(
$parameters
[
'VALUE'
]))
{
$valueType
=
$parameters
[
'VALUE'
]->
getValue
();
unset
(
$parameters
[
'VALUE'
]);
}
if
(!
$valueType
)
{
$valueType
=
$property
->
getValueType
();
}
$newProperty
=
$output
->
createProperty
(
$property
->
name
,
$property
->
getParts
(),
array
(),
// parameters will get added a bit later.
$valueType
);
if
(
$targetVersion
===
Document
::
VCARD30
)
{
if
(
$property
instanceof
Property\Uri
&&
in_array
(
$property
->
name
,
array
(
'PHOTO'
,
'LOGO'
,
'SOUND'
)))
{
$newProperty
=
$this
->
convertUriToBinary
(
$output
,
$newProperty
);
}
elseif
(
$property
instanceof
Property\VCard\DateAndOrTime
)
{
// In vCard 4, the birth year may be optional. This is not the
// case for vCard 3. Apple has a workaround for this that
// allows applications that support Apple's extension still
// omit birthyears in vCard 3, but applications that do not
// support this, will just use a random birthyear. We're
// choosing 1604 for the birthyear, because that's what apple
// uses.
$parts
=
DateTimeParser
::
parseVCardDateTime
(
$property
->
getValue
());
if
(
is_null
(
$parts
[
'year'
]))
{
$newValue
=
'1604-'
.
$parts
[
'month'
]
.
'-'
.
$parts
[
'date'
];
$newProperty
->
setValue
(
$newValue
);
$newProperty
[
'X-APPLE-OMIT-YEAR'
]
=
'1604'
;
}
if
(
$newProperty
->
name
==
'ANNIVERSARY'
)
{
// Microsoft non-standard anniversary
$newProperty
->
name
=
'X-ANNIVERSARY'
;
// We also need to add a new apple property for the same
// purpose. This apple property needs a 'label' in the same
// group, so we first need to find a groupname that doesn't
// exist yet.
$x
=
1
;
while
(
$output
->
select
(
'ITEM'
.
$x
.
'.'
))
{
$x
++;
}
$output
->
add
(
'ITEM'
.
$x
.
'.X-ABDATE'
,
$newProperty
->
getValue
(),
array
(
'VALUE'
=>
'DATE-AND-OR-TIME'
));
$output
->
add
(
'ITEM'
.
$x
.
'.X-ABLABEL'
,
'_$!<Anniversary>!$_'
);
}
}
elseif
(
$property
->
name
===
'KIND'
)
{
switch
(
strtolower
(
$property
->
getValue
()))
{
case
'org'
:
// vCard 3.0 does not have an equivalent to KIND:ORG,
// but apple has an extension that means the same
// thing.
$newProperty
=
$output
->
createProperty
(
'X-ABSHOWAS'
,
'COMPANY'
);
break
;
case
'individual'
:
// Individual is implicit, so we skip it.
return
;
case
'group'
:
// OS X addressbook property
$newProperty
=
$output
->
createProperty
(
'X-ADDRESSBOOKSERVER-KIND'
,
'GROUP'
);
break
;
}
}
}
elseif
(
$targetVersion
===
Document
::
VCARD40
)
{
// These properties were removed in vCard 4.0
if
(
in_array
(
$property
->
name
,
array
(
'NAME'
,
'MAILER'
,
'LABEL'
,
'CLASS'
)))
{
return
;
}
if
(
$property
instanceof
Property\Binary
)
{
$newProperty
=
$this
->
convertBinaryToUri
(
$output
,
$newProperty
,
$parameters
);
}
elseif
(
$property
instanceof
Property\VCard\DateAndOrTime
&&
isset
(
$parameters
[
'X-APPLE-OMIT-YEAR'
]))
{
// If a property such as BDAY contained 'X-APPLE-OMIT-YEAR',
// then we're stripping the year from the vcard 4 value.
$parts
=
DateTimeParser
::
parseVCardDateTime
(
$property
->
getValue
());
if
(
$parts
[
'year'
]===
$property
[
'X-APPLE-OMIT-YEAR'
]->
getValue
())
{
$newValue
=
'--'
.
$parts
[
'month'
]
.
'-'
.
$parts
[
'date'
];
$newProperty
->
setValue
(
$newValue
);
}
// Regardless if the year matched or not, we do need to strip
// X-APPLE-OMIT-YEAR.
unset
(
$parameters
[
'X-APPLE-OMIT-YEAR'
]);
}
switch
(
$property
->
name
)
{
case
'X-ABSHOWAS'
:
if
(
strtoupper
(
$property
->
getValue
())
===
'COMPANY'
)
{
$newProperty
=
$output
->
createProperty
(
'KIND'
,
'ORG'
);
}
break
;
case
'X-ADDRESSBOOKSERVER-KIND'
:
if
(
strtoupper
(
$property
->
getValue
())
===
'GROUP'
)
{
$newProperty
=
$output
->
createProperty
(
'KIND'
,
'GROUP'
);
}
break
;
case
'X-ANNIVERSARY'
:
$newProperty
->
name
=
'ANNIVERSARY'
;
// If we already have an anniversary property with the same
// value, ignore.
foreach
(
$output
->
select
(
'ANNIVERSARY'
)
as
$anniversary
)
{
if
(
$anniversary
->
getValue
()
===
$newProperty
->
getValue
())
{
return
;
}
}
break
;
case
'X-ABDATE'
:
// Find out what the label was, if it exists.
if
(!
$property
->
group
)
{
break
;
}
$label
=
$input
->{
$property
->
group
.
'.X-ABLABEL'
};
// We only support converting anniversaries.
if
(!
$label
||
$label
->
getValue
()!==
'_$!<Anniversary>!$_'
)
{
break
;
}
// If we already have an anniversary property with the same
// value, ignore.
foreach
(
$output
->
select
(
'ANNIVERSARY'
)
as
$anniversary
)
{
if
(
$anniversary
->
getValue
()
===
$newProperty
->
getValue
())
{
return
;
}
}
$newProperty
->
name
=
'ANNIVERSARY'
;
break
;
// Apple's per-property label system.
case
'X-ABLABEL'
:
if
(
$newProperty
->
getValue
()
===
'_$!<Anniversary>!$_'
)
{
// We can safely remove these, as they are converted to
// ANNIVERSARY properties.
return
;
}
break
;
}
}
// set property group
$newProperty
->
group
=
$property
->
group
;
if
(
$targetVersion
===
Document
::
VCARD40
)
{
$this
->
convertParameters40
(
$newProperty
,
$parameters
);
}
else
{
$this
->
convertParameters30
(
$newProperty
,
$parameters
);
}
// Lastly, we need to see if there's a need for a VALUE parameter.
//
// We can do that by instantating a empty property with that name, and
// seeing if the default valueType is identical to the current one.
$tempProperty
=
$output
->
createProperty
(
$newProperty
->
name
);
if
(
$tempProperty
->
getValueType
()
!==
$newProperty
->
getValueType
())
{
$newProperty
[
'VALUE'
]
=
$newProperty
->
getValueType
();
}
$output
->
add
(
$newProperty
);
}
/**
* Converts a BINARY property to a URI property.
*
* vCard 4.0 no longer supports BINARY properties.
*
* @param Component\VCard $output
* @param Property\Uri $property The input property.
* @param $parameters List of parameters that will eventually be added to
* the new property.
* @return Property\Uri
*/
protected
function
convertBinaryToUri
(
Component\VCard
$output
,
Property\Binary
$newProperty
,
array
&
$parameters
)
{
$value
=
$newProperty
->
getValue
();
$newProperty
=
$output
->
createProperty
(
$newProperty
->
name
,
null
,
// no value
array
(),
// no parameters yet
'URI'
// Forcing the BINARY type
);
$mimeType
=
'application/octet-stream'
;
// See if we can find a better mimetype.
if
(
isset
(
$parameters
[
'TYPE'
]))
{
$newTypes
=
array
();
foreach
(
$parameters
[
'TYPE'
]->
getParts
()
as
$typePart
)
{
if
(
in_array
(
strtoupper
(
$typePart
),
array
(
'JPEG'
,
'PNG'
,
'GIF'
)
))
{
$mimeType
=
'image/'
.
strtolower
(
$typePart
);
}
else
{
$newTypes
[]
=
$typePart
;
}
}
// If there were any parameters we're not converting to a
// mime-type, we need to keep them.
if
(
$newTypes
)
{
$parameters
[
'TYPE'
]->
setParts
(
$newTypes
);
}
else
{
unset
(
$parameters
[
'TYPE'
]);
}
}
$newProperty
->
setValue
(
'data:'
.
$mimeType
.
';base64,'
.
base64_encode
(
$value
));
return
$newProperty
;
}
/**
* Converts a URI property to a BINARY property.
*
* In vCard 4.0 attachments are encoded as data: uri. Even though these may
* be valid in vCard 3.0 as well, we should convert those to BINARY if
* possible, to improve compatibility.
*
* @param Component\VCard $output
* @param Property\Uri $property The input property.
* @return Property\Binary|null
*/
protected
function
convertUriToBinary
(
Component\VCard
$output
,
Property\Uri
$newProperty
)
{
$value
=
$newProperty
->
getValue
();
// Only converting data: uris
if
(
substr
(
$value
,
0
,
5
)!==
'data:'
)
{
return
$newProperty
;
}
$newProperty
=
$output
->
createProperty
(
$newProperty
->
name
,
null
,
// no value
array
(),
// no parameters yet
'BINARY'
);
$mimeType
=
substr
(
$value
,
5
,
strpos
(
$value
,
','
)-
5
);
if
(
strpos
(
$mimeType
,
';'
))
{
$mimeType
=
substr
(
$mimeType
,
0
,
strpos
(
$mimeType
,
';'
));
$newProperty
->
setValue
(
base64_decode
(
substr
(
$value
,
strpos
(
$value
,
','
)+
1
)));
}
else
{
$newProperty
->
setValue
(
substr
(
$value
,
strpos
(
$value
,
','
)+
1
));
}
unset
(
$value
);
$newProperty
[
'ENCODING'
]
=
'b'
;
switch
(
$mimeType
)
{
case
'image/jpeg'
:
$newProperty
[
'TYPE'
]
=
'JPEG'
;
break
;
case
'image/png'
:
$newProperty
[
'TYPE'
]
=
'PNG'
;
break
;
case
'image/gif'
:
$newProperty
[
'TYPE'
]
=
'GIF'
;
break
;
}
return
$newProperty
;
}
/**
* Adds parameters to a new property for vCard 4.0
*
* @param Property $newProperty
* @param array $parameters
* @return void
*/
protected
function
convertParameters40
(
Property
$newProperty
,
array
$parameters
)
{
// Adding all parameters.
foreach
(
$parameters
as
$param
)
{
// vCard 2.1 allowed parameters with no name
if
(
$param
->
noName
)
$param
->
noName
=
false
;
switch
(
$param
->
name
)
{
// We need to see if there's any TYPE=PREF, because in vCard 4
// that's now PREF=1.
case
'TYPE'
:
foreach
(
$param
->
getParts
()
as
$paramPart
)
{
if
(
strtoupper
(
$paramPart
)===
'PREF'
)
{
$newProperty
->
add
(
'PREF'
,
'1'
);
}
else
{
$newProperty
->
add
(
$param
->
name
,
$paramPart
);
}
}
break
;
// These no longer exist in vCard 4
case
'ENCODING'
:
case
'CHARSET'
:
break
;
default
:
$newProperty
->
add
(
$param
->
name
,
$param
->
getParts
());
break
;
}
}
}
/**
* Adds parameters to a new property for vCard 3.0
*
* @param Property $newProperty
* @param array $parameters
* @return void
*/
protected
function
convertParameters30
(
Property
$newProperty
,
array
$parameters
)
{
// Adding all parameters.
foreach
(
$parameters
as
$param
)
{
// vCard 2.1 allowed parameters with no name
if
(
$param
->
noName
)
$param
->
noName
=
false
;
switch
(
$param
->
name
)
{
case
'ENCODING'
:
// This value only existed in vCard 2.1, and should be
// removed for anything else.
if
(
strtoupper
(
$param
->
getValue
())!==
'QUOTED-PRINTABLE'
)
{
$newProperty
->
add
(
$param
->
name
,
$param
->
getParts
());
}
break
;
/*
* Converting PREF=1 to TYPE=PREF.
*
* Any other PREF numbers we'll drop.
*/
case
'PREF'
:
if
(
$param
->
getValue
()==
'1'
)
{
$newProperty
->
add
(
'TYPE'
,
'PREF'
);
}
break
;
default
:
$newProperty
->
add
(
$param
->
name
,
$param
->
getParts
());
break
;
}
}
}
}
File Metadata
Details
Attached
Mime Type
text/x-php
Expires
Sat, Dec 21, 1:05 PM (2 w, 5 d ago)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
915388
Default Alt Text
VCardConverter.php (15 KB)
Attached To
rDAVCAL DokuWiki DAVCal PlugIn
Event Timeline
Log In to Comment