From 6bdb6102c06c605d18f5d2cfba8ac806875ae13a Mon Sep 17 00:00:00 2001 From: thomascube Date: Tue, 4 Jan 2011 22:00:35 +0000 Subject: [PATCH] Do charset detection in vcards with encoded values (#1485542) --- CHANGELOG | 1 + program/include/rcube_vcard.php | 32 ++++++++++++++++++++++---------- tests/src/thebat.vcf | 8 ++++++++ tests/vcards.php | 4 ++++ 4 files changed, 35 insertions(+), 10 deletions(-) create mode 100644 tests/src/thebat.vcf diff --git a/CHANGELOG b/CHANGELOG index 2e20b3ec6..17a6563f3 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -1,6 +1,7 @@ CHANGELOG Roundcube Webmail =========================== +- Fix charset detection in vcards with encoded values (#1485542) - Better CSS cursors for splitters (#1486874) - Show the same message only once (#1487641) - Fix namespaces handling (#1487649) diff --git a/program/include/rcube_vcard.php b/program/include/rcube_vcard.php index 9bbc32b3c..641d61e15 100644 --- a/program/include/rcube_vcard.php +++ b/program/include/rcube_vcard.php @@ -28,6 +28,7 @@ */ class rcube_vcard { + private static $values_decoded = false; private $raw = array( 'FN' => array(), 'N' => array(array('','','','','')), @@ -47,10 +48,10 @@ class rcube_vcard /** * Constructor */ - public function __construct($vcard = null, $charset = RCMAIL_CHARSET) + public function __construct($vcard = null, $charset = RCMAIL_CHARSET, $detect = false) { 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 * * @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); - + // resolve charset parameters - if ($charset == null) - $this->raw = $this->charset_convert($this->raw); + if ($charset == null) { + $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 $this->displayname = $this->raw['FN'][0][0]; @@ -171,13 +181,13 @@ class rcube_vcard /** * 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 ($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) { if (is_numeric($j) && is_string($value)) $card[$key][$i][$j] = rcube_charset_convert($value, $charset); @@ -222,7 +232,7 @@ class rcube_vcard if (preg_match('/^END:VCARD$/i', $line)) { // 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)) $out[] = $obj; @@ -363,9 +373,11 @@ class rcube_vcard { switch (strtolower($encoding)) { case 'quoted-printable': + self::$values_decoded = true; return quoted_printable_decode($value); case 'base64': + self::$values_decoded = true; return base64_decode($value); default: diff --git a/tests/src/thebat.vcf b/tests/src/thebat.vcf new file mode 100644 index 000000000..8179f788d --- /dev/null +++ b/tests/src/thebat.vcf @@ -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 diff --git a/tests/vcards.php b/tests/vcards.php index 72c687484..3b8f260c4 100644 --- a/tests/vcards.php +++ b/tests/vcards.php @@ -48,6 +48,10 @@ class rcube_test_vcards extends UnitTestCase $this->assertEqual(2, count($vcards), "Detected 2 vcards"); $this->assertEqual("Apple Computer AG", $vcards[0]->displayname, "FN => displayname"); $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"); } }