From 118a170dbca9f55badb3f05abb231c1c98eb3539 Mon Sep 17 00:00:00 2001 From: Aleksander Machniak Date: Mon, 7 Jul 2014 19:06:10 +0200 Subject: Fix images import from various vCard formats (#1489977) --- CHANGELOG | 1 + program/lib/Roundcube/rcube_vcard.php | 51 ++++++++++++++++++++++------------- tests/Framework/VCard.php | 16 +++++++++++ 3 files changed, 50 insertions(+), 18 deletions(-) diff --git a/CHANGELOG b/CHANGELOG index ab0b98279..7553b68d6 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -21,6 +21,7 @@ CHANGELOG Roundcube Webmail - Fix security issue in delete-response action - allow only ajax request - Fix Delete button state after deleting identity/response (#1489972) - Fix bug where contacts with no email address were listed on compose addressbook (#1489970) +- Fix images import from various vCard formats (#1489977) RELEASE 1.0.1 ------------- diff --git a/program/lib/Roundcube/rcube_vcard.php b/program/lib/Roundcube/rcube_vcard.php index fb8fdd525..4a2684f10 100644 --- a/program/lib/Roundcube/rcube_vcard.php +++ b/program/lib/Roundcube/rcube_vcard.php @@ -594,29 +594,34 @@ class rcube_vcard private static function vcard_decode($vcard) { // Perform RFC2425 line unfolding and split lines - $vcard = preg_replace(array("/\r/", "/\n\s+/"), '', $vcard); - $lines = explode("\n", $vcard); - $data = array(); + $vcard = preg_replace(array("/\r/", "/\n\s+/"), '', $vcard); + $lines = explode("\n", $vcard); + $result = array(); for ($i=0; $i < count($lines); $i++) { - if (!preg_match('/^([^:]+):(.+)$/', $lines[$i], $line)) + if (!($pos = strpos($lines[$i], ':'))) { continue; + } + + $prefix = substr($lines[$i], 0, $pos); + $data = substr($lines[$i], $pos+1); - if (preg_match('/^(BEGIN|END)$/i', $line[1])) + if (preg_match('/^(BEGIN|END)$/i', $prefix)) { continue; + } // convert 2.1-style "EMAIL;internet;home:" to 3.0-style "EMAIL;TYPE=internet;TYPE=home:" - if ($data['VERSION'][0] == "2.1" - && preg_match('/^([^;]+);([^:]+)/', $line[1], $regs2) + if ($result['VERSION'][0] == "2.1" + && preg_match('/^([^;]+);([^:]+)/', $prefix, $regs2) && !preg_match('/^TYPE=/i', $regs2[2]) ) { - $line[1] = $regs2[1]; + $prefix = $regs2[1]; foreach (explode(';', $regs2[2]) as $prop) { - $line[1] .= ';' . (strpos($prop, '=') ? $prop : 'TYPE='.$prop); + $prefix .= ';' . (strpos($prop, '=') ? $prop : 'TYPE='.$prop); } } - if (preg_match_all('/([^\\;]+);?/', $line[1], $regs2)) { + if (preg_match_all('/([^\\;]+);?/', $prefix, $regs2)) { $entry = array(); $field = strtoupper($regs2[1][0]); $enc = null; @@ -629,10 +634,10 @@ class rcube_vcard // add next line(s) to value string if QP line end detected if ($value == 'QUOTED-PRINTABLE') { while (preg_match('/=$/', $lines[$i])) { - $line[2] .= "\n" . $lines[++$i]; + $data .= "\n" . $lines[++$i]; } } - $enc = $value; + $enc = $value == 'BASE64' ? 'B' : $value; } else { $lc_key = strtolower($key); @@ -652,20 +657,30 @@ class rcube_vcard // should we use vCard 3.0 instead? // $entry['base64'] = true; } - $line[2] = self::decode_value($line[2], $enc ? $enc : 'base64'); + + $data = self::decode_value($data, $enc ? $enc : 'base64'); + } + else if ($field == 'PHOTO') { + // vCard 4.0 data URI, "PHOTO:data:image/jpeg;base64,..." + if (preg_match('/^data:[a-z\/_-]+;base64,/i', $data, $m)) { + $entry['encoding'] = $enc = 'B'; + $data = substr($data, strlen($m[0])); + $data = self::decode_value($data, 'base64'); + } } if ($enc != 'B' && empty($entry['base64'])) { - $line[2] = self::vcard_unquote($line[2]); + $data = self::vcard_unquote($data); } - $entry = array_merge($entry, (array) $line[2]); - $data[$field][] = $entry; + $entry = array_merge($entry, (array) $data); + $result[$field][] = $entry; } } - unset($data['VERSION']); - return $data; + unset($result['VERSION']); + + return $result; } /** diff --git a/tests/Framework/VCard.php b/tests/Framework/VCard.php index 3353b5b13..0a34fc51f 100644 --- a/tests/Framework/VCard.php +++ b/tests/Framework/VCard.php @@ -107,6 +107,22 @@ class Framework_VCard extends PHPUnit_Framework_TestCase // ENCODING=b case (#1488683) $this->assertEquals("/9j/4AAQSkZJRgABAQA", substr(base64_encode($vcard['photo']), 0, 19), "Photo decoding"); $this->assertEquals("Müller", $vcard['surname'], "Unicode characters"); + + $input = str_replace('ENCODING=b:', 'ENCODING=base64;jpeg:', $input); + + $vcards = rcube_vcard::import($input); + $vcard = $vcards[0]->get_assoc(); + + // ENCODING=base64 case (#1489977) + $this->assertEquals("/9j/4AAQSkZJRgABAQA", substr(base64_encode($vcard['photo']), 0, 19), "Photo decoding"); + + $input = str_replace('PHOTO;ENCODING=base64;jpeg:', 'PHOTO:data:image/jpeg;base64,', $input); + + $vcards = rcube_vcard::import($input); + $vcard = $vcards[0]->get_assoc(); + + // vcard4.0 "PHOTO:data:image/jpeg;base64," case (#1489977) + $this->assertEquals("/9j/4AAQSkZJRgABAQA", substr(base64_encode($vcard['photo']), 0, 19), "Photo decoding"); } function test_encodings() -- cgit v1.2.3