Do charset detection in vcards with encoded values (#1485542)

release-0.6
thomascube 14 years ago
parent 636328483f
commit 6bdb6102c0

@ -1,6 +1,7 @@
CHANGELOG Roundcube Webmail CHANGELOG Roundcube Webmail
=========================== ===========================
- Fix charset detection in vcards with encoded values (#1485542)
- Better CSS cursors for splitters (#1486874) - Better CSS cursors for splitters (#1486874)
- Show the same message only once (#1487641) - Show the same message only once (#1487641)
- Fix namespaces handling (#1487649) - Fix namespaces handling (#1487649)

@ -28,6 +28,7 @@
*/ */
class rcube_vcard class rcube_vcard
{ {
private static $values_decoded = false;
private $raw = array( private $raw = array(
'FN' => array(), 'FN' => array(),
'N' => array(array('','','','','')), 'N' => array(array('','','','','')),
@ -47,10 +48,10 @@ class rcube_vcard
/** /**
* Constructor * Constructor
*/ */
public function __construct($vcard = null, $charset = RCMAIL_CHARSET) public function __construct($vcard = null, $charset = RCMAIL_CHARSET, $detect = false)
{ {
if (!empty($vcard)) if (!empty($vcard))
$this->load($vcard, $charset); $this->load($vcard, $charset, $detect);
} }
@ -58,14 +59,23 @@ class rcube_vcard
* Load record from (internal, unfolded) vcard 3.0 format * Load record from (internal, unfolded) vcard 3.0 format
* *
* @param string vCard string to parse * @param string vCard string to parse
* @param string Charset of string values
* @param boolean True if loading a 'foreign' vcard and extra heuristics for charset detection is required
*/ */
public function load($vcard, $charset = RCMAIL_CHARSET) public function load($vcard, $charset = RCMAIL_CHARSET, $detect = false)
{ {
self::$values_decoded = false;
$this->raw = self::vcard_decode($vcard); $this->raw = self::vcard_decode($vcard);
// resolve charset parameters // resolve charset parameters
if ($charset == null) if ($charset == null) {
$this->raw = $this->charset_convert($this->raw); $this->raw = self::charset_convert($this->raw);
}
// vcard has encoded values and charset should be detected
else if ($detect && self::$values_decoded &&
($detected_charset = self::detect_encoding(self::vcard_encode($this->raw))) && $detected_charset != RCMAIL_CHARSET) {
$this->raw = self::charset_convert($this->raw, $detected_charset);
}
// find well-known address fields // find well-known address fields
$this->displayname = $this->raw['FN'][0][0]; $this->displayname = $this->raw['FN'][0][0];
@ -171,13 +181,13 @@ class rcube_vcard
/** /**
* Convert a whole vcard (array) to UTF-8. * Convert a whole vcard (array) to UTF-8.
* Each member value that has a charset parameter will be converted. * If $force_charset is null, each member value that has a charset parameter will be converted
*/ */
private function charset_convert($card) private static function charset_convert($card, $force_charset = null)
{ {
foreach ($card as $key => $node) { foreach ($card as $key => $node) {
foreach ($node as $i => $subnode) { foreach ($node as $i => $subnode) {
if (is_array($subnode) && $subnode['charset'] && ($charset = $subnode['charset'][0])) { if (is_array($subnode) && (($charset = $force_charset) || ($subnode['charset'] && ($charset = $subnode['charset'][0])))) {
foreach ($subnode as $j => $value) { foreach ($subnode as $j => $value) {
if (is_numeric($j) && is_string($value)) if (is_numeric($j) && is_string($value))
$card[$key][$i][$j] = rcube_charset_convert($value, $charset); $card[$key][$i][$j] = rcube_charset_convert($value, $charset);
@ -222,7 +232,7 @@ class rcube_vcard
if (preg_match('/^END:VCARD$/i', $line)) { if (preg_match('/^END:VCARD$/i', $line)) {
// parse vcard // parse vcard
$obj = new rcube_vcard(self::cleanup($vcard_block), $charset); $obj = new rcube_vcard(self::cleanup($vcard_block), $charset, true);
if (!empty($obj->displayname)) if (!empty($obj->displayname))
$out[] = $obj; $out[] = $obj;
@ -363,9 +373,11 @@ class rcube_vcard
{ {
switch (strtolower($encoding)) { switch (strtolower($encoding)) {
case 'quoted-printable': case 'quoted-printable':
self::$values_decoded = true;
return quoted_printable_decode($value); return quoted_printable_decode($value);
case 'base64': case 'base64':
self::$values_decoded = true;
return base64_decode($value); return base64_decode($value);
default: default:

@ -0,0 +1,8 @@
BEGIN:VCARD
VERSION:2.1
N;ENCODING=QUOTED-PRINTABLE:Iksi=F1ski;Piotr
FN;ENCODING=QUOTED-PRINTABLE:Piotr Iksi=F1ski
EMAIL;PREF;INTERNET:piotr.iksinski@somedomain.com
X-GENDER:Male
REV:20080716T203548Z
END:VCARD

@ -48,6 +48,10 @@ class rcube_test_vcards extends UnitTestCase
$this->assertEqual(2, count($vcards), "Detected 2 vcards"); $this->assertEqual(2, count($vcards), "Detected 2 vcards");
$this->assertEqual("Apple Computer AG", $vcards[0]->displayname, "FN => displayname"); $this->assertEqual("Apple Computer AG", $vcards[0]->displayname, "FN => displayname");
$this->assertEqual("John Doë", $vcards[1]->displayname, "Displayname with correct charset"); $this->assertEqual("John Doë", $vcards[1]->displayname, "Displayname with correct charset");
// http://trac.roundcube.net/ticket/1485542
$vcards2 = rcube_vcard::import(file_get_contents($this->_srcpath('thebat.vcf')));
$this->assertEqual("Iksiñski", $vcards2[0]->surname, "Detect charset in encoded values");
} }
} }

Loading…
Cancel
Save