summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--CHANGELOG3
-rw-r--r--program/include/rcube_imap.php125
-rw-r--r--program/include/rcube_imap_generic.php2
-rw-r--r--program/lib/html2text.php6
-rw-r--r--program/localization/fr_FR/labels.inc2
-rw-r--r--program/steps/mail/func.inc3
6 files changed, 82 insertions, 59 deletions
diff --git a/CHANGELOG b/CHANGELOG
index f02c50ac3..4882f4eb7 100644
--- a/CHANGELOG
+++ b/CHANGELOG
@@ -1,6 +1,9 @@
CHANGELOG Roundcube Webmail
===========================
+- Fix some emails are not shown using Cyrus IMAP (#1487820)
+- Fix handling of mime-encoded words with non-integral number of octets in a word (#1487801)
+- Fix parsing links with non-printable characters inside (#1487805)
- Fixed de_CH Localization bugs (#1487773)
- Add variable for 'Today' label in date_today option (#1486120)
- Applied plugin changes since 0.5-stable release
diff --git a/program/include/rcube_imap.php b/program/include/rcube_imap.php
index dd821bf37..adca4e3d4 100644
--- a/program/include/rcube_imap.php
+++ b/program/include/rcube_imap.php
@@ -4344,80 +4344,95 @@ class rcube_imap
*/
public static function decode_mime_string($input, $fallback=null)
{
- // Initialize variable
- $out = '';
+ if (!empty($fallback)) {
+ $default_charset = $fallback;
+ }
+ else {
+ $default_charset = rcmail::get_instance()->config->get('default_charset', 'ISO-8859-1');
+ }
- // Iterate instead of recursing, this way if there are too many values we don't have stack overflows
// rfc: all line breaks or other characters not found
// in the Base64 Alphabet must be ignored by decoding software
// delete all blanks between MIME-lines, differently we can
// receive unnecessary blanks and broken utf-8 symbols
$input = preg_replace("/\?=\s+=\?/", '?==?', $input);
- // Check if there is stuff to decode
- if (strpos($input, '=?') !== false) {
- // Loop through the string to decode all occurences of =? ?= into the variable $out
- while(($pos = strpos($input, '=?')) !== false) {
- // Append everything that is before the text to be decoded
- $out .= substr($input, 0, $pos);
-
- // Get the location of the text to decode
- $end_cs_pos = strpos($input, "?", $pos+2);
- $end_en_pos = strpos($input, "?", $end_cs_pos+1);
- $end_pos = strpos($input, "?=", $end_en_pos+1);
+ // encoded-word regexp
+ $re = '/=\?([^?]+)\?([BbQq])\?([^?\n]*)\?=/';
- // Extract the encoded string
- $encstr = substr($input, $pos+2, ($end_pos-$pos-2));
- // Extract the remaining string
- $input = substr($input, $end_pos+2);
+ // Find all RFC2047's encoded words
+ if (preg_match_all($re, $input, $matches, PREG_OFFSET_CAPTURE | PREG_SET_ORDER)) {
+ // Initialize variables
+ $tmp = array();
+ $out = '';
+ $start = 0;
- // Decode the string fragement
- $out .= rcube_imap::_decode_mime_string_part($encstr);
- }
+ foreach ($matches as $idx => $m) {
+ $pos = $m[0][1];
+ $charset = $m[1][0];
+ $encoding = $m[2][0];
+ $text = $m[3][0];
+ $length = strlen($m[0][0]);
- // Deocde the rest (if any)
- if (strlen($input) != 0)
- $out .= rcube_imap::decode_mime_string($input, $fallback);
+ // Append everything that is before the text to be decoded
+ if ($start != $pos) {
+ $substr = substr($input, $start, $pos-$start);
+ $out .= rcube_charset_convert($substr, $default_charset);
+ $start = $pos;
+ }
+ $start += $length;
+
+ // Per RFC2047, each string part "MUST represent an integral number
+ // of characters . A multi-octet character may not be split across
+ // adjacent encoded-words." However, some mailers break this, so we
+ // try to handle characters spanned across parts anyway by iterating
+ // through and aggregating sequential encoded parts with the same
+ // character set and encoding, then perform the decoding on the
+ // aggregation as a whole.
+
+ $tmp[] = $text;
+ if ($next_match = $matches[$idx+1]) {
+ if ($next_match[0][1] == $start
+ && $next_match[1][0] == $charset
+ && $next_match[2][0] == $encoding
+ ) {
+ continue;
+ }
+ }
- // return the results
- return $out;
- }
-
- // no encoding information, use fallback
- return rcube_charset_convert($input,
- !empty($fallback) ? $fallback : rcmail::get_instance()->config->get('default_charset', 'ISO-8859-1'));
- }
+ $count = count($tmp);
+ $text = '';
+ // Decode and join encoded-word's chunks
+ if ($encoding == 'B' || $encoding == 'b') {
+ // base64 must be decoded a segment at a time
+ for ($i=0; $i<$count; $i++)
+ $text .= base64_decode($tmp[$i]);
+ }
+ else { //if ($encoding == 'Q' || $encoding == 'q') {
+ // quoted printable can be combined and processed at once
+ for ($i=0; $i<$count; $i++)
+ $text .= $tmp[$i];
- /**
- * Decode a part of a mime-encoded string
- *
- * @param string $str String to decode
- * @return string Decoded string
- * @access private
- */
- private function _decode_mime_string_part($str)
- {
- $a = explode('?', $str);
- $count = count($a);
+ $text = str_replace('_', ' ', $text);
+ $text = quoted_printable_decode($text);
+ }
- // should be in format "charset?encoding?base64_string"
- if ($count >= 3) {
- for ($i=2; $i<$count; $i++)
- $rest .= $a[$i];
+ $out .= rcube_charset_convert($text, $charset);
+ $tmp = array();
+ }
- if (($a[1]=='B') || ($a[1]=='b'))
- $rest = base64_decode($rest);
- else if (($a[1]=='Q') || ($a[1]=='q')) {
- $rest = str_replace('_', ' ', $rest);
- $rest = quoted_printable_decode($rest);
+ // add the last part of the input string
+ if ($start != strlen($input)) {
+ $out .= rcube_charset_convert(substr($input, $start), $default_charset);
}
- return rcube_charset_convert($rest, $a[0]);
+ // return the results
+ return $out;
}
- // we dont' know what to do with this
- return $str;
+ // no encoding information, use fallback
+ return rcube_charset_convert($input, $default_charset);
}
diff --git a/program/include/rcube_imap_generic.php b/program/include/rcube_imap_generic.php
index f1b3dfda8..f3855892a 100644
--- a/program/include/rcube_imap_generic.php
+++ b/program/include/rcube_imap_generic.php
@@ -1351,7 +1351,7 @@ class rcube_imap_generic
$result[$id] = '';
}
} else if ($mode == 2) {
- if (preg_match('/\((UID|RFC822\.SIZE) ([0-9]+)/', $line, $matches)) {
+ if (preg_match('/(UID|RFC822\.SIZE) ([0-9]+)/', $line, $matches)) {
$result[$id] = trim($matches[2]);
} else {
$result[$id] = 0;
diff --git a/program/lib/html2text.php b/program/lib/html2text.php
index 3b98e8df7..325a1c941 100644
--- a/program/lib/html2text.php
+++ b/program/lib/html2text.php
@@ -652,10 +652,12 @@ class html2text
case 'h':
return $this->_strtoupper("\n\n". $matches[2] ."\n\n");
case 'a':
- return $this->_build_link_list($matches[3], $matches[4]);
+ // Remove spaces in URL (#1487805)
+ $url = str_replace(' ', '', $matches[3]);
+ return $this->_build_link_list($url, $matches[4]);
}
}
-
+
/**
* Strtoupper multibyte wrapper function
*
diff --git a/program/localization/fr_FR/labels.inc b/program/localization/fr_FR/labels.inc
index 4430de619..96c78cc56 100644
--- a/program/localization/fr_FR/labels.inc
+++ b/program/localization/fr_FR/labels.inc
@@ -103,7 +103,7 @@ $labels['replytoallmessage'] = 'Répondre à tous';
$labels['replyall'] = 'Répondre à tous';
$labels['replylist'] = 'Répondre à la liste';
$labels['forwardmessage'] = 'Transmettre le message';
-$labels['deletemessage'] = 'Déplacer le message dans la corbeille';
+$labels['deletemessage'] = 'Supprimer le message';
$labels['movemessagetotrash'] = 'Déplacer le message dans la corbeille';
$labels['printmessage'] = 'Imprimer ce message';
$labels['previousmessage'] = 'Voir le message précédent';
diff --git a/program/steps/mail/func.inc b/program/steps/mail/func.inc
index a4eb13175..f700a6cdb 100644
--- a/program/steps/mail/func.inc
+++ b/program/steps/mail/func.inc
@@ -1200,6 +1200,9 @@ function rcmail_alter_html_link($matches)
$attrib = parse_attrib_string($matches[2]);
$end = '>';
+ // Remove non-printable characters in URL (#1487805)
+ $attrib['href'] = preg_replace('/[\x00-\x1F]/', '', $attrib['href']);
+
if ($tag == 'link' && preg_match('/^https?:\/\//i', $attrib['href'])) {
$tempurl = 'tmp-' . md5($attrib['href']) . '.css';
$_SESSION['modcssurls'][$tempurl] = $attrib['href'];