From fcb7d4fc034335d960917abd37254bd3997cf2f3 Mon Sep 17 00:00:00 2001
From: Aleksander Machniak <alec@alec.pl>
Date: Fri, 01 Aug 2014 06:49:37 -0400
Subject: [PATCH] Fix various iCloud vCard issues, added fallback for external photos (#1489993)

---
 CHANGELOG                             |    1 +
 program/lib/Roundcube/rcube_vcard.php |   10 +++++-----
 program/steps/addressbook/photo.inc   |    8 ++++++--
 tests/Framework/VCard.php             |   19 +++++++++++++++++++
 program/steps/addressbook/func.inc    |   10 +++++++---
 program/steps/mail/show.inc           |    4 +++-
 program/lib/Roundcube/html.php        |    2 +-
 7 files changed, 42 insertions(+), 12 deletions(-)

diff --git a/CHANGELOG b/CHANGELOG
index a6ade5b..a2de91f 100644
--- a/CHANGELOG
+++ b/CHANGELOG
@@ -41,6 +41,7 @@
 - Fix some mime-type to extension mapping checks in Installer (#1489983)
 - Fix errors when using localStorage in Safari's private browsing mode (#1489996)
 - Fix bug where $Forwarded flag was being set even if server didn't support it (#1490000)
+- Fix various iCloud vCard issues, added fallback for external photos (#1489993)
 
 RELEASE 1.0.2
 -------------
diff --git a/program/lib/Roundcube/html.php b/program/lib/Roundcube/html.php
index bcf89d7..f18cad0 100644
--- a/program/lib/Roundcube/html.php
+++ b/program/lib/Roundcube/html.php
@@ -153,7 +153,7 @@
             $attr = array('src' => $attr);
         }
         return self::tag('img', $attr + array('alt' => ''), null, array_merge(self::$common_attrib,
-            array('src','alt','width','height','border','usemap','onclick')));
+            array('src','alt','width','height','border','usemap','onclick','onerror')));
     }
 
     /**
diff --git a/program/lib/Roundcube/rcube_vcard.php b/program/lib/Roundcube/rcube_vcard.php
index 4a2684f..96add11 100644
--- a/program/lib/Roundcube/rcube_vcard.php
+++ b/program/lib/Roundcube/rcube_vcard.php
@@ -110,7 +110,7 @@
     public function load($vcard, $charset = RCUBE_CHARSET, $detect = false)
     {
         self::$values_decoded = false;
-        $this->raw = self::vcard_decode($vcard);
+        $this->raw = self::vcard_decode(self::cleanup($vcard));
 
         // resolve charset parameters
         if ($charset == null) {
@@ -496,7 +496,7 @@
 
             if (preg_match('/^END:VCARD$/i', $line)) {
                 // parse vcard
-                $obj = new rcube_vcard(self::cleanup($vcard_block), $charset, true, self::$fieldmap);
+                $obj = new rcube_vcard($vcard_block, $charset, true, self::$fieldmap);
                 // FN and N is required by vCard format (RFC 2426)
                 // on import we can be less restrictive, let's addressbook decide
                 if (!empty($obj->displayname) || !empty($obj->surname) || !empty($obj->firstname) || !empty($obj->email)) {
@@ -532,9 +532,9 @@
         // Cleanup
         $vcard = preg_replace(array(
                 // convert special types (like Skype) to normal type='skype' classes with this simple regex ;)
-                '/item(\d+)\.(TEL|EMAIL|URL)([^:]*?):(.*?)item\1.X-ABLabel:(?:_\$!<)?([\w-() ]*)(?:>!\$_)?./s',
-                '/^item\d*\.X-AB.*$/m',  // remove cruft like item1.X-AB*
-                '/^item\d*\./m',         // remove item1.ADR instead of ADR
+                '/item(\d+)\.(TEL|EMAIL|URL)([^:]*?):(.*?)item\1.X-ABLabel:(?:_\$!<)?([\w-() ]*)(?:>!\$_)?./si',
+                '/^item\d*\.X-AB.*$/mi',  // remove cruft like item1.X-AB*
+                '/^item\d*\./mi',         // remove item1.ADR instead of ADR
                 '/\n+/',                 // remove empty lines
                 '/^(N:[^;\R]*)$/m',      // if N doesn't have any semicolons, add some
             ),
diff --git a/program/steps/addressbook/func.inc b/program/steps/addressbook/func.inc
index d4c57cc..38de93d 100644
--- a/program/steps/addressbook/func.inc
+++ b/program/steps/addressbook/func.inc
@@ -816,11 +816,15 @@
         }
         $photo_img = $RCMAIL->url($url);
     }
-    else
+    else {
         $ff_value = '-del-'; // will disable delete-photo action
+    }
 
-    $img = html::img(array('src' => $photo_img, 'border' => 1, 'alt' => $RCMAIL->gettext('contactphoto')));
-    $content = html::div($attrib, $img);
+    $content = html::div($attrib, html::img(array(
+            'src'     => $photo_img,
+            'alt'     => $RCMAIL->gettext('contactphoto'),
+            'onerror' => 'this.src = rcmail.env.photo_placeholder',
+    )));
 
     if ($CONTACT_COLTYPES['photo'] && ($RCMAIL->action == 'edit' || $RCMAIL->action == 'add')) {
         $RCMAIL->output->add_gui_object('contactphoto', $attrib['id']);
diff --git a/program/steps/addressbook/photo.inc b/program/steps/addressbook/photo.inc
index 4821857..30d09ff 100644
--- a/program/steps/addressbook/photo.inc
+++ b/program/steps/addressbook/photo.inc
@@ -72,8 +72,12 @@
 if ($plugin['url']) {
     $RCMAIL->output->redirect($plugin['url']);
 }
-else {
-    $data = $plugin['data'];
+
+$data = $plugin['data'];
+
+// detect if photo data is an URL
+if (strlen($data) < 1024 && filter_var($data, FILTER_VALIDATE_URL)) {
+    $RCMAIL->output->redirect($data);
 }
 
 // deliver alt image
diff --git a/program/steps/mail/show.inc b/program/steps/mail/show.inc
index 4b2d78d..d4121fd 100644
--- a/program/steps/mail/show.inc
+++ b/program/steps/mail/show.inc
@@ -349,8 +349,10 @@
             '_task'   => 'addressbook',
             '_action' => 'photo',
             '_email'  => $MESSAGE->sender['mailto'],
-            '_alt'    => $placeholder
+            '_alt'    => $placeholder,
         ));
+
+        $attrib['onerror'] = "this.src = '" . ($placeholder ? $placeholder : 'program/resources/blank.gif') . "'";
     }
     else {
         $photo_img = $placeholder ? $placeholder : 'program/resources/blank.gif';
diff --git a/tests/Framework/VCard.php b/tests/Framework/VCard.php
index 0a34fc5..c23dba8 100644
--- a/tests/Framework/VCard.php
+++ b/tests/Framework/VCard.php
@@ -79,6 +79,25 @@
         $this->assertEquals("http://domain.tld", $vcard['website:other'][0], "Decode dummy backslash character");
     }
 
+    /**
+     * Some Apple vCard quirks (#1489993)
+     */
+    function test_parse_six()
+    {
+        $vcard = new rcube_vcard("BEGIN:VCARD\n"
+            . "VERSION:3.0\n"
+            . "N:;;;;\n"
+            . "FN:Apple Computer AG\n"
+            . "ITEM1.ADR;type=WORK;type=pref:;;Birgistrasse 4a;Wallisellen-Zürich;;8304;Switzerland\n"
+            . "PHOTO;ENCODING=B:aHR0cDovL3Rlc3QuY29t\n"
+            . "END:VCARD"
+        );
+
+        $result = $vcard->get_assoc();
+
+        $this->assertCount(1, $result['address:work'], "ITEM1.-prefixed entry");
+    }
+
     function test_import()
     {
         $input = file_get_contents($this->_srcpath('apple.vcf'));

--
Gitblit v1.9.1