Differences Between: [Versions 311 and 401] [Versions 311 and 402] [Versions 311 and 403]
1 <?php 2 /** 3 * Copyright 2014-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 2014-2017 Horde LLC 10 * @license http://www.horde.org/licenses/lgpl21 LGPL 2.1 11 * @package Mime 12 */ 13 14 /** 15 * This class represents a header element that contains MIME content 16 * parameters (RFCs 2045, 2183, 2231). 17 * 18 * @author Michael Slusarz <slusarz@horde.org> 19 * @category Horde 20 * @copyright 2014-2017 Horde LLC 21 * @license http://www.horde.org/licenses/lgpl21 LGPL 2.1 22 * @package Mime 23 * @since 2.5.0 24 * 25 * @property-read array $params Content parameters. 26 */ 27 class Horde_Mime_Headers_ContentParam 28 extends Horde_Mime_Headers_Element_Single 29 implements ArrayAccess, Horde_Mime_Headers_Extension_Mime, Serializable 30 { 31 /** 32 * Content parameters. 33 * 34 * @var Horde_Support_CaseInsensitiveArray 35 */ 36 protected $_params; 37 38 /** 39 */ 40 public function __clone() 41 { 42 $this->_params = new Horde_Support_CaseInsensitiveArray( 43 $this->_params->getArrayCopy() 44 ); 45 } 46 47 /** 48 */ 49 public function __get($name) 50 { 51 switch ($name) { 52 case 'full_value': 53 $tmp = $this->value; 54 foreach ($this->_escapeParams($this->params) as $key => $val) { 55 $tmp .= '; ' . $key . '=' . $val; 56 } 57 return $tmp; 58 59 case 'params': 60 return $this->_params->getArrayCopy(); 61 } 62 63 return parent::__get($name); 64 } 65 66 /** 67 * @param mixed $data Either an array (interpreted as a list of 68 * parameters), a string (interpreted as a RFC 69 * encoded parameter list), an object with two 70 * properties: value and params, or a 71 * Horde_Mime_Headers_ContentParam object. 72 */ 73 protected function _setValue($data) 74 { 75 if (!$this->_params) { 76 $this->_params = new Horde_Support_CaseInsensitiveArray(); 77 } 78 79 if ($data instanceof Horde_Mime_Headers_ContentParam) { 80 if (empty($this->_values)) { 81 $this->setContentParamValue($data->value); 82 } 83 foreach ($data->params as $key => $val) { 84 $this[$key] = $val; 85 } 86 } elseif (is_object($data)) { 87 if (!empty($data->value)) { 88 $this->setContentParamValue($data->value); 89 } 90 if (!empty($data->params)) { 91 $this->decode($data->params); 92 } 93 } else { 94 $this->decode($data); 95 } 96 } 97 98 /** 99 * @param array $opts See encode(). 100 */ 101 protected function _sendEncode($opts) 102 { 103 $out = $this->value; 104 105 foreach ($this->encode($opts) as $key => $val) { 106 $out .= '; ' . $key . '=' . $val; 107 } 108 109 return array($out); 110 } 111 112 /** 113 */ 114 public static function getHandles() 115 { 116 return array(); 117 } 118 119 /** 120 * Encodes a MIME content parameter string pursuant to RFC 2183 & 2231 121 * (Content-Type and Content-Disposition headers). 122 * 123 * @param array $opts Options: 124 * - broken_rfc2231: (boolean) Attempt to work around non-RFC 125 * 2231-compliant MUAs by generating both a RFC 126 * 2047-like parameter name and also the correct RFC 127 * 2231 parameter 128 * DEFAULT: false 129 * - charset: (string) The charset to encode to. 130 * DEFAULT: UTF-8 131 * - lang: (string) The language to use when encoding. 132 * DEFAULT: None specified 133 * 134 * @return array The encoded parameter string (US-ASCII). 135 */ 136 public function encode(array $opts = array()) 137 { 138 $opts = array_merge(array( 139 'charset' => 'UTF-8', 140 ), $opts); 141 142 $out = array(); 143 144 foreach ($this->params as $key => $val) { 145 $out = array_merge($out, $this->_encode($key, $val, $opts)); 146 } 147 148 return $out; 149 } 150 151 /** 152 * @see encode() 153 */ 154 protected function _encode($name, $val, $opts) 155 { 156 $curr = 0; 157 $encode = $wrap = false; 158 $out = array(); 159 160 // 2 = '=', ';' 161 $pre_len = strlen($name) + 2; 162 163 /* Several possibilities: 164 * - String is ASCII. Output as ASCII (duh). 165 * - Language information has been provided. We MUST encode output 166 * to include this information. 167 * - String is non-ASCII, but can losslessly translate to ASCII. 168 * Output as ASCII (most efficient). 169 * - String is in non-ASCII, but doesn't losslessly translate to 170 * ASCII. MUST encode output (duh). */ 171 if (empty($opts['lang']) && !Horde_Mime::is8bit($val, 'UTF-8')) { 172 $string = $val; 173 } else { 174 $cval = Horde_String::convertCharset($val, 'UTF-8', $opts['charset']); 175 $string = Horde_String::lower($opts['charset']) . '\'' . (empty($opts['lang']) ? '' : Horde_String::lower($opts['lang'])) . '\'' . rawurlencode($cval); 176 $encode = true; 177 /* Account for trailing '*'. */ 178 ++$pre_len; 179 } 180 181 if (($pre_len + strlen($string)) > 75) { 182 /* Account for continuation '*'. */ 183 ++$pre_len; 184 $wrap = true; 185 186 while ($string) { 187 $chunk = 75 - $pre_len - strlen($curr); 188 $pos = min($chunk, strlen($string) - 1); 189 190 /* Don't split in the middle of an encoded char. */ 191 if (($chunk == $pos) && ($pos > 2)) { 192 for ($i = 0; $i <= 2; ++$i) { 193 if ($string[$pos - $i] == '%') { 194 $pos -= $i + 1; 195 break; 196 } 197 } 198 } 199 200 $lines[] = substr($string, 0, $pos + 1); 201 $string = substr($string, $pos + 1); 202 ++$curr; 203 } 204 } else { 205 $lines = array($string); 206 } 207 208 foreach ($lines as $i => $line) { 209 $out[$name . (($wrap) ? ('*' . $i) : '') . (($encode) ? '*' : '')] = $line; 210 } 211 212 if (!empty($opts['broken_rfc2231']) && !isset($out[$name])) { 213 $out = array_merge(array( 214 $name => Horde_Mime::encode($val, $opts['charset']) 215 ), $out); 216 } 217 218 /* Escape characters in params (See RFC 2045 [Appendix A]). 219 * Must be quoted-string if one of these exists. */ 220 return $this->_escapeParams($out); 221 } 222 223 /** 224 * Escape the parameter array. 225 * 226 * @param array $params Parameter array. 227 * 228 * @return array Escaped parameter array. 229 */ 230 protected function _escapeParams($params) 231 { 232 foreach ($params as $k => $v) { 233 foreach (str_split($v) as $c) { 234 if (!Horde_Mime_ContentParam_Decode::isAtextNonTspecial($c)) { 235 $params[$k] = '"' . addcslashes($v, '\\"') . '"'; 236 break; 237 } 238 } 239 } 240 241 return $params; 242 } 243 244 /** 245 * Set the content-parameter base value. 246 * 247 * @since 2.8.0 248 * 249 * @param string $data Value. 250 */ 251 public function setContentParamValue($data) 252 { 253 $data = $this->_sanityCheck(trim($data)); 254 if (($pos = strpos($data, ';')) !== false) { 255 $data = substr($data, 0, $pos); 256 } 257 258 $this->_values = array($data); 259 } 260 261 /** 262 * Decodes a MIME content parameter string pursuant to RFC 2183 & 2231 263 * (Content-Type and Content-Disposition headers). 264 * 265 * Stores value/parameter data in the current object. 266 * 267 * @param mixed $data Parameter data. Either an array or a string. 268 */ 269 public function decode($data) 270 { 271 $add = $convert = array(); 272 273 if (is_array($data)) { 274 $params = $data; 275 } else { 276 $parts = explode(';', $data, 2); 277 if (isset($parts[0]) && (strpos($parts[0], '=') === false)) { 278 $this->setContentParamValue($parts[0]); 279 $param = isset($parts[1]) ? $parts[1] : null; 280 } else { 281 $param = $data; 282 } 283 284 if (empty($param)) { 285 $params = array(); 286 } else { 287 $decode = new Horde_Mime_ContentParam_Decode(); 288 $params = $decode->decode($param); 289 } 290 } 291 292 $to_add = array(); 293 294 foreach ($params as $name => $val) { 295 /* Asterisk at end indicates encoded value. */ 296 if (substr($name, -1) == '*') { 297 $name = substr($name, 0, -1); 298 $encoded = true; 299 } else { 300 $encoded = false; 301 } 302 303 /* This asterisk indicates continuation parameter. */ 304 if ((($pos = strrpos($name, '*')) !== false) && 305 is_numeric($order = substr($name, $pos + 1))) { 306 $name = substr($name, 0, $pos); 307 $to_add[Horde_String::lower($name)][$order] = $val; 308 } else { 309 $to_add[$name] = array($val); 310 } 311 312 if ($encoded) { 313 $convert[$name] = true; 314 } 315 } 316 317 foreach ($to_add as $key => $val) { 318 ksort($val); 319 $add[$key] = implode('', $val); 320 } 321 322 foreach (array_keys($convert) as $name) { 323 $val = $add[$name]; 324 $quote = strpos($val, "'"); 325 326 if ($quote === false) { 327 $add[$name] = urldecode($val); 328 } else { 329 $orig_charset = substr($val, 0, $quote); 330 if (Horde_String::lower($orig_charset) == 'iso-8859-1') { 331 $orig_charset = 'windows-1252'; 332 } 333 334 /* Ignore language. */ 335 $quote = strpos($val, "'", $quote + 1); 336 substr($val, $quote + 1); 337 $add[$name] = Horde_String::convertCharset( 338 urldecode(substr($val, $quote + 1)), 339 $orig_charset, 340 'UTF-8' 341 ); 342 } 343 } 344 345 /* MIME parameters are supposed to be encoded via RFC 2231, but many 346 * mailers do RFC 2045 encoding instead. However, if we see at least 347 * one RFC 2231 encoding, then assume the sending mailer knew what 348 * it was doing and didn't send any parameters RFC 2045 encoded. */ 349 if (empty($convert)) { 350 foreach ($add as $key => $val) { 351 $add[$key] = Horde_Mime::decode($val); 352 } 353 } 354 355 if (count($add)) { 356 foreach ($add as $key => $val) { 357 /* When parsing a content-param string, lowercase all 358 * parameter names to normalize. Only maintain case of 359 * parameters explicitly added by calling code. */ 360 $this[Horde_String::lower($key)] = $val; 361 } 362 } elseif (is_string($data)) { 363 $this->setContentParamValue($parts[0]); 364 } 365 } 366 367 /* ArrayAccess methods */ 368 369 /** 370 */ 371 public function offsetExists($offset) 372 { 373 return isset($this->_params[$offset]); 374 } 375 376 /** 377 */ 378 public function offsetGet($offset) 379 { 380 return $this->_params[$offset]; 381 } 382 383 /** 384 */ 385 public function offsetSet($offset, $value) 386 { 387 $this->_params[$offset] = $this->_sanityCheck($value); 388 } 389 390 /** 391 */ 392 public function offsetUnset($offset) 393 { 394 unset($this->_params[$offset]); 395 } 396 397 /* Serializable methods */ 398 399 /** 400 */ 401 public function serialize() 402 { 403 $vars = array_filter(get_object_vars($this)); 404 if (isset($vars['_params'])) { 405 $vars['_params'] = $vars['_params']->getArrayCopy(); 406 } 407 return serialize($vars); 408 } 409 410 /** 411 */ 412 public function unserialize($data) 413 { 414 $data = unserialize($data); 415 416 foreach ($data as $key => $val) { 417 switch ($key) { 418 case '_params': 419 $this->_params = new Horde_Support_CaseInsensitiveArray($val); 420 break; 421 422 default: 423 $this->$key = $val; 424 break; 425 } 426 } 427 } 428 429 }
title
Description
Body
title
Description
Body
title
Description
Body
title
Body