Improve vcard import: map more fields, support photo urls, better UTF-16 charset detection
1 files added
4 files modified
| | |
| | | 'notes' => 'NOTE', |
| | | 'email' => 'EMAIL', |
| | | 'address' => 'ADR', |
| | | 'jobtitle' => 'TITLE', |
| | | 'gender' => 'X-GENDER', |
| | | 'maidenname' => 'X-MAIDENNAME', |
| | | 'anniversary' => 'X-ANNIVERSARY', |
| | |
| | | } |
| | | } |
| | | } |
| | | |
| | | // force subtype if none set |
| | | if (preg_match('/^(email|phone|address|website)/', $key) && !$subtype) |
| | | $subtype = 'other'; |
| | | |
| | | if ($subtype) |
| | | $key .= ':' . $subtype; |
| | |
| | | break; |
| | | |
| | | case 'photo': |
| | | $encoded = !preg_match('![^a-z0-9/=+-]!i', $value); |
| | | $this->raw['PHOTO'][0] = array(0 => $encoded ? $value : base64_encode($value), 'BASE64' => true); |
| | | if (strpos($value, 'http:') === 0) { |
| | | // TODO: fetch file from URL and save it locally? |
| | | $this->raw['PHOTO'][0] = array(0 => $value, 'URL' => true); |
| | | } |
| | | else { |
| | | $encoded = !preg_match('![^a-z0-9/=+-]!i', $value); |
| | | $this->raw['PHOTO'][0] = array(0 => $encoded ? $value : base64_encode($value), 'BASE64' => true); |
| | | } |
| | | break; |
| | | |
| | | case 'email': |
| | |
| | | { |
| | | // Convert special types (like Skype) to normal type='skype' classes with this simple regex ;) |
| | | $vcard = preg_replace( |
| | | '/item(\d+)\.(TEL|URL)([^:]*?):(.*?)item\1.X-ABLabel:(?:_\$!<)?([\w-() ]*)(?:>!\$_)?./s', |
| | | '/item(\d+)\.(TEL|EMAIL|URL)([^:]*?):(.*?)item\1.X-ABLabel:(?:_\$!<)?([\w-() ]*)(?:>!\$_)?./s', |
| | | '\2;type=\5\3:\4', |
| | | $vcard); |
| | | |
| | | // convert Apple X-ABRELATEDNAMES into X-* fields for better compatibility |
| | | $vcard = preg_replace_callback( |
| | | '/item(\d+)\.(X-ABRELATEDNAMES)([^:]*?):(.*?)item\1.X-ABLabel:(?:_\$!<)?([\w-() ]*)(?:>!\$_)?./s', |
| | | array('self', 'x_abrelatednames_callback'), |
| | | $vcard); |
| | | |
| | | // Remove cruft like item1.X-AB*, item1.ADR instead of ADR, and empty lines |
| | |
| | | $vcard = preg_replace('/^(N:[^;\R]*)$/m', '\1;;;;', $vcard); |
| | | |
| | | return $vcard; |
| | | } |
| | | |
| | | private static function x_abrelatednames_callback($matches) |
| | | { |
| | | return 'X-' . strtoupper($matches[5]) . $matches[3] . ':'. $matches[4]; |
| | | } |
| | | |
| | | private static function rfc2425_fold_callback($matches) |
| | |
| | | if (substr($string, 0, 2) == "\xFF\xFE") return 'UTF-16LE'; // Little Endian |
| | | if (substr($string, 0, 3) == "\xEF\xBB\xBF") return 'UTF-8'; |
| | | |
| | | // heuristics |
| | | if ($string[0] == "\0" && $string[1] == "\0" && $string[2] == "\0" && $string[3] != "\0") return 'UTF-32BE'; |
| | | if ($string[0] != "\0" && $string[1] == "\0" && $string[2] == "\0" && $string[3] == "\0") return 'UTF-32LE'; |
| | | if ($string[0] == "\0" && $string[1] != "\0" && $string[2] == "\0" && $string[3] != "\0") return 'UTF-16BE'; |
| | | if ($string[0] != "\0" && $string[1] == "\0" && $string[2] != "\0" && $string[3] == "\0") return 'UTF-16LE'; |
| | | |
| | | // use mb_detect_encoding() |
| | | $encodings = array('UTF-8', 'ISO-8859-1', 'ISO-8859-2', 'ISO-8859-3', |
| | | 'ISO-8859-4', 'ISO-8859-5', 'ISO-8859-6', 'ISO-8859-7', 'ISO-8859-8', 'ISO-8859-9', |
| | |
| | | // skip cols unknown to the backend |
| | | if (!$coltypes[$col]) |
| | | continue; |
| | | |
| | | // only string values are expected here |
| | | if (is_array($record[$col])) |
| | | $record[$col] = join(' ', $record[$col]); |
| | | |
| | | if ($RCMAIL->action == 'show') { |
| | | if (!empty($record[$col])) |
| | |
| | | if ($CONTACT_COLTYPES['photo']) { |
| | | $RCMAIL->output->set_env('photo_placeholder', $photo_img); |
| | | |
| | | if ($record['photo']) |
| | | if (strpos($record['photo'], 'http:') === 0) |
| | | $photo_img = $record['photo']; |
| | | else if ($record['photo']) |
| | | $photo_img = $RCMAIL->url(array('_action' => 'photo', '_cid' => $record['ID'], '_source' => $_REQUEST['_source'])); |
| | | $img = html::img(array('src' => $photo_img, 'border' => 1, 'alt' => '')); |
| | | $content = html::div($attrib, $img); |
| | |
| | | // We're using UTF8 internally |
| | | $email = rcube_idn_to_utf8($email); |
| | | |
| | | if (!$replace) { |
| | | if (!$replace && $email) { |
| | | // compare e-mail address |
| | | $existing = $CONTACTS->search('email', $email, false, false); |
| | | if (!$existing->count) { // compare display name |
| | |
| | | $this->assertEqual("Iksiñski", $vcards2[0]->surname, "Detect charset in encoded values"); |
| | | } |
| | | |
| | | function test_encodings() |
| | | { |
| | | $input = file_get_contents($this->_srcpath('utf-16_sample.vcf')); |
| | | |
| | | $vcards = rcube_vcard::import($input); |
| | | $this->assertEqual("Ǽgean ĽdaMonté", $vcards[0]->displayname, "Decoded from UTF-16"); |
| | | } |
| | | |
| | | } |