From b231c8f6accb7c04461ab8364016d0abbe81f82e Mon Sep 17 00:00:00 2001
From: Aleksander Machniak <alec@alec.pl>
Date: Mon, 07 Jul 2014 13:06:10 -0400
Subject: [PATCH] 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 4a58d9e..aa22fb0 100644
--- a/CHANGELOG
+++ b/CHANGELOG
@@ -56,6 +56,7 @@
 - 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 fb8fdd5..4a2684f 100644
--- a/program/lib/Roundcube/rcube_vcard.php
+++ b/program/lib/Roundcube/rcube_vcard.php
@@ -594,29 +594,34 @@
     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;
+            }
 
-            if (preg_match('/^(BEGIN|END)$/i', $line[1]))
+            $prefix = substr($lines[$i], 0, $pos);
+            $data   = substr($lines[$i], $pos+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 @@
                             // 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 @@
                         // 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 3353b5b..0a34fc5 100644
--- a/tests/Framework/VCard.php
+++ b/tests/Framework/VCard.php
@@ -107,6 +107,22 @@
         // 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()

--
Gitblit v1.9.1