See Release Notes
Long Term Support Release
1 <?php 2 /** 3 * Copyright 1999-2017 Horde LLC (http://www.horde.org/) 4 * 5 * See the enclosed file LICENSE for license information (LGPL). If you 6 * did not receive this file, see http://www.horde.org/licenses/lgpl21. 7 * 8 * @category Horde 9 * @copyright 1999-2017 Horde LLC 10 * @license http://www.horde.org/licenses/lgpl21 LGPL 2.1 11 * @package Mime 12 */ 13 14 /** 15 * Provide methods for dealing with MIME encoding (RFC 2045-2049); 16 * 17 * @author Chuck Hagenbuch <chuck@horde.org> 18 * @author Michael Slusarz <slusarz@horde.org> 19 * @category Horde 20 * @copyright 1999-2017 Horde LLC 21 * @license http://www.horde.org/licenses/lgpl21 LGPL 2.1 22 * @package Mime 23 */ 24 class Horde_Mime 25 { 26 /** 27 * The RFC defined EOL string. 28 * 29 * @var string 30 */ 31 const EOL = "\r\n"; 32 33 /** 34 * Use windows-1252 charset when decoding ISO-8859-1 data? 35 * HTML 5 requires this behavior, so it is the default. 36 * 37 * @var boolean 38 */ 39 public static $decodeWindows1252 = true; 40 41 /** 42 * Determines if a string contains 8-bit (non US-ASCII) characters. 43 * 44 * @param string $string The string to check. 45 * @param string $charset The charset of the string. Defaults to 46 * US-ASCII. (@deprecated) 47 * 48 * @return boolean True if string contains non 7-bit characters. 49 */ 50 public static function is8bit($string, $charset = null) 51 { 52 $string = strval($string); 53 for ($i = 0, $len = strlen($string); $i < $len; ++$i) { 54 if (ord($string[$i]) > 127) { 55 return true; 56 } 57 } 58 59 return false; 60 } 61 62 /** 63 * MIME encodes a string (RFC 2047). 64 * 65 * @param string $text The text to encode (UTF-8). 66 * @param string $charset The character set to encode to. 67 * 68 * @return string The MIME encoded string (US-ASCII). 69 */ 70 public static function encode($text, $charset = 'UTF-8') 71 { 72 $charset = Horde_String::lower($charset); 73 $text = Horde_String::convertCharset($text, 'UTF-8', $charset); 74 75 $encoded = $is_encoded = false; 76 $lwsp = $word = null; 77 $out = ''; 78 79 /* 0 = word unencoded 80 * 1 = word encoded 81 * 2 = spaces */ 82 $parts = array(); 83 84 /* Tokenize string. */ 85 for ($i = 0, $len = strlen($text); $i < $len; ++$i) { 86 switch ($text[$i]) { 87 case "\t": 88 case "\r": 89 case "\n": 90 if (!is_null($word)) { 91 $parts[] = array(intval($encoded), $word, $i - $word); 92 $word = null; 93 } elseif (!is_null($lwsp)) { 94 $parts[] = array(2, $lwsp, $i - $lwsp); 95 $lwsp = null; 96 } 97 98 $parts[] = array(0, $i, 1); 99 break; 100 101 case ' ': 102 if (!is_null($word)) { 103 $parts[] = array(intval($encoded), $word, $i - $word); 104 $word = null; 105 } 106 if (is_null($lwsp)) { 107 $lwsp = $i; 108 } 109 break; 110 111 default: 112 if (is_null($word)) { 113 $encoded = false; 114 $word = $i; 115 if (!is_null($lwsp)) { 116 $parts[] = array(2, $lwsp, $i - $lwsp); 117 $lwsp = null; 118 } 119 120 /* Check for MIME encoding delimiter. Encode it if 121 * found. */ 122 if (($text[$i] === '=') && 123 (($i + 1) < $len) && 124 ($text[$i +1] === '?')) { 125 ++$i; 126 $encoded = $is_encoded = true; 127 } 128 } 129 130 /* Check for 8-bit characters or control characters. */ 131 if (!$encoded) { 132 $c = ord($text[$i]); 133 if ($encoded = (($c & 0x80) || ($c < 32))) { 134 $is_encoded = true; 135 } 136 } 137 break; 138 } 139 } 140 141 if (!$is_encoded) { 142 return $text; 143 } 144 145 if (is_null($lwsp)) { 146 $parts[] = array(intval($encoded), $word, $len); 147 } else { 148 $parts[] = array(2, $lwsp, $len); 149 } 150 151 /* Combine parts into MIME encoded string. */ 152 for ($i = 0, $cnt = count($parts); $i < $cnt; ++$i) { 153 $val = $parts[$i]; 154 155 switch ($val[0]) { 156 case 0: 157 case 2: 158 $out .= substr($text, $val[1], $val[2]); 159 break; 160 161 case 1: 162 $j = $i; 163 for ($k = $i + 1; $k < $cnt; ++$k) { 164 switch ($parts[$k][0]) { 165 case 0: 166 break 2; 167 168 case 1: 169 $i = $k; 170 break; 171 } 172 } 173 174 $encode = ''; 175 for (; $j <= $i; ++$j) { 176 $encode .= substr($text, $parts[$j][1], $parts[$j][2]); 177 } 178 179 $delim = '=?' . $charset . '?b?'; 180 $e_parts = explode( 181 self::EOL, 182 rtrim( 183 chunk_split( 184 base64_encode($encode), 185 /* strlen($delim) + 2 = space taken by MIME 186 * delimiter */ 187 intval((75 - strlen($delim) + 2) / 4) * 4 188 ) 189 ) 190 ); 191 192 $tmp = array(); 193 foreach ($e_parts as $val) { 194 $tmp[] = $delim . $val . '?='; 195 } 196 197 $out .= implode(' ', $tmp); 198 break; 199 } 200 } 201 202 return rtrim($out); 203 } 204 205 /** 206 * Decodes a MIME encoded (RFC 2047) string. 207 * 208 * @param string $string The MIME encoded text. 209 * 210 * @return string The decoded text. 211 */ 212 public static function decode($string) 213 { 214 $old_pos = 0; 215 $out = ''; 216 217 while (($pos = strpos($string, '=?', $old_pos)) !== false) { 218 /* Save any preceding text, if it is not LWSP between two 219 * encoded words. */ 220 $pre = substr($string, $old_pos, $pos - $old_pos); 221 if (!$old_pos || 222 (strspn($pre, " \t\n\r") != strlen($pre))) { 223 $out .= $pre; 224 } 225 226 /* Search for first delimiting question mark (charset). */ 227 if (($d1 = strpos($string, '?', $pos + 2)) === false) { 228 break; 229 } 230 231 $orig_charset = substr($string, $pos + 2, $d1 - $pos - 2); 232 if (self::$decodeWindows1252 && 233 (Horde_String::lower($orig_charset) == 'iso-8859-1')) { 234 $orig_charset = 'windows-1252'; 235 } 236 237 /* Search for second delimiting question mark (encoding). */ 238 if (($d2 = strpos($string, '?', $d1 + 1)) === false) { 239 break; 240 } 241 242 $encoding = substr($string, $d1 + 1, $d2 - $d1 - 1); 243 244 /* Search for end of encoded data. */ 245 if (($end = strpos($string, '?=', $d2 + 1)) === false) { 246 break; 247 } 248 249 $encoded_text = substr($string, $d2 + 1, $end - $d2 - 1); 250 251 switch ($encoding) { 252 case 'Q': 253 case 'q': 254 $out .= Horde_String::convertCharset( 255 quoted_printable_decode( 256 str_replace('_', ' ', $encoded_text) 257 ), 258 $orig_charset, 259 'UTF-8' 260 ); 261 break; 262 263 case 'B': 264 case 'b': 265 $out .= Horde_String::convertCharset( 266 base64_decode($encoded_text), 267 $orig_charset, 268 'UTF-8' 269 ); 270 break; 271 272 default: 273 // Ignore unknown encoding. 274 break; 275 } 276 277 $old_pos = $end + 2; 278 } 279 280 return $out . substr($string, $old_pos); 281 } 282 283 /* Deprecated methods. */ 284 285 /** 286 * @deprecated Use Horde_Mime_Headers_MessageId::create() instead. 287 */ 288 public static function generateMessageId() 289 { 290 return Horde_Mime_Headers_MessageId::create()->value; 291 } 292 293 /** 294 * @deprecated Use Horde_Mime_Uudecode instead. 295 */ 296 public static function uudecode($input) 297 { 298 $uudecode = new Horde_Mime_Uudecode($input); 299 return iterator_to_array($uudecode); 300 } 301 302 /** 303 * @deprecated 304 */ 305 public static $brokenRFC2231 = false; 306 307 /** 308 * @deprecated 309 */ 310 const MIME_PARAM_QUOTED = '/[\x01-\x20\x22\x28\x29\x2c\x2f\x3a-\x40\x5b-\x5d]/'; 311 312 /** 313 * @deprecated Use Horde_Mime_Headers_ContentParam#encode() instead. 314 */ 315 public static function encodeParam($name, $val, array $opts = array()) 316 { 317 $cp = new Horde_Mime_Headers_ContentParam( 318 'UNUSED', 319 array($name => $val) 320 ); 321 322 return $cp->encode(array_merge(array( 323 'broken_rfc2231' => self::$brokenRFC2231 324 ), $opts)); 325 } 326 327 /** 328 * @deprecated Use Horde_Mime_Headers_ELement_ContentParam instead. 329 */ 330 public static function decodeParam($type, $data) 331 { 332 $cp = new Horde_Mime_Headers_ContentParam( 333 'UNUSED', 334 $data 335 ); 336 337 if (strlen($cp->value)) { 338 $val = $cp->value; 339 } else { 340 $val = (Horde_String::lower($type) == 'content-type') 341 ? 'text/plain' 342 : 'attachment'; 343 } 344 345 return array( 346 'params' => $cp->params, 347 'val' => $val 348 ); 349 } 350 351 /** 352 * @deprecated Use Horde_Mime_Id instead. 353 */ 354 public static function mimeIdArithmetic($id, $action, $options = array()) 355 { 356 $id_ob = new Horde_Mime_Id($id); 357 358 switch ($action) { 359 case 'down': 360 $action = $id_ob::ID_DOWN; 361 break; 362 363 case 'next': 364 $action = $id_ob::ID_NEXT; 365 break; 366 367 case 'prev': 368 $action = $id_ob::ID_PREV; 369 break; 370 371 case 'up': 372 $action = $id_ob::ID_UP; 373 break; 374 } 375 376 return $id_ob->idArithmetic($action, $options); 377 } 378 379 /** 380 * @deprecated Use Horde_Mime_Id instead. 381 */ 382 public static function isChild($base, $id) 383 { 384 $id_ob = new Horde_Mime_Id($base); 385 return $id_ob->isChild($id); 386 } 387 388 /** 389 * @deprecated Use Horde_Mime_QuotedPrintable instead. 390 */ 391 public static function quotedPrintableEncode($text, $eol = self::EOL, 392 $wrap = 76) 393 { 394 return Horde_Mime_QuotedPrintable::encode($text, $eol, $wrap); 395 } 396 397 }
title
Description
Body
title
Description
Body
title
Description
Body
title
Body