ParameterizedHeader.php 6.6KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270
  1. <?php
  2. /*
  3. * This file is part of SwiftMailer.
  4. * (c) 2004-2009 Chris Corbyn
  5. *
  6. * For the full copyright and license information, please view the LICENSE
  7. * file that was distributed with this source code.
  8. */
  9. /**
  10. * An abstract base MIME Header.
  11. * @package Swift
  12. * @subpackage Mime
  13. * @author Chris Corbyn
  14. */
  15. class Swift_Mime_Headers_ParameterizedHeader
  16. extends Swift_Mime_Headers_UnstructuredHeader
  17. implements Swift_Mime_ParameterizedHeader
  18. {
  19. /**
  20. * The Encoder used to encode the parameters.
  21. * @var Swift_Encoder
  22. * @access private
  23. */
  24. private $_paramEncoder;
  25. /**
  26. * The parameters as an associative array.
  27. * @var string[]
  28. * @access private
  29. */
  30. private $_params = array();
  31. /**
  32. * RFC 2231's definition of a token.
  33. * @var string
  34. * @access private
  35. */
  36. private $_tokenRe;
  37. /**
  38. * Creates a new ParameterizedHeader with $name.
  39. * @param string $name
  40. * @param Swift_Mime_HeaderEncoder $encoder
  41. * @param Swift_Encoder $paramEncoder, optional
  42. * @param Swift_Mime_Grammar $grammar
  43. */
  44. public function __construct($name, Swift_Mime_HeaderEncoder $encoder,
  45. Swift_Encoder $paramEncoder = null, Swift_Mime_Grammar $grammar)
  46. {
  47. parent::__construct($name, $encoder, $grammar);
  48. $this->_paramEncoder = $paramEncoder;
  49. $this->_tokenRe = '(?:[\x21\x23-\x27\x2A\x2B\x2D\x2E\x30-\x39\x41-\x5A\x5E-\x7E]+)';
  50. }
  51. /**
  52. * Get the type of Header that this instance represents.
  53. * @return int
  54. * @see TYPE_TEXT, TYPE_PARAMETERIZED, TYPE_MAILBOX
  55. * @see TYPE_DATE, TYPE_ID, TYPE_PATH
  56. */
  57. public function getFieldType()
  58. {
  59. return self::TYPE_PARAMETERIZED;
  60. }
  61. /**
  62. * Set the character set used in this Header.
  63. * @param string $charset
  64. */
  65. public function setCharset($charset)
  66. {
  67. parent::setCharset($charset);
  68. if (isset($this->_paramEncoder))
  69. {
  70. $this->_paramEncoder->charsetChanged($charset);
  71. }
  72. }
  73. /**
  74. * Set the value of $parameter.
  75. * @param string $parameter
  76. * @param string $value
  77. */
  78. public function setParameter($parameter, $value)
  79. {
  80. $this->setParameters(array_merge($this->getParameters(), array($parameter => $value)));
  81. }
  82. /**
  83. * Get the value of $parameter.
  84. * @return string
  85. */
  86. public function getParameter($parameter)
  87. {
  88. $params = $this->getParameters();
  89. return array_key_exists($parameter, $params)
  90. ? $params[$parameter]
  91. : null;
  92. }
  93. /**
  94. * Set an associative array of parameter names mapped to values.
  95. * @param string[]
  96. */
  97. public function setParameters(array $parameters)
  98. {
  99. $this->clearCachedValueIf($this->_params != $parameters);
  100. $this->_params = $parameters;
  101. }
  102. /**
  103. * Returns an associative array of parameter names mapped to values.
  104. * @return string[]
  105. */
  106. public function getParameters()
  107. {
  108. return $this->_params;
  109. }
  110. /**
  111. * Get the value of this header prepared for rendering.
  112. * @return string
  113. */
  114. public function getFieldBody() //TODO: Check caching here
  115. {
  116. $body = parent::getFieldBody();
  117. foreach ($this->_params as $name => $value)
  118. {
  119. if (!is_null($value))
  120. {
  121. //Add the parameter
  122. $body .= '; ' . $this->_createParameter($name, $value);
  123. }
  124. }
  125. return $body;
  126. }
  127. // -- Protected methods
  128. /**
  129. * Generate a list of all tokens in the final header.
  130. * This doesn't need to be overridden in theory, but it is for implementation
  131. * reasons to prevent potential breakage of attributes.
  132. * @return string[]
  133. * @access protected
  134. */
  135. protected function toTokens($string = null)
  136. {
  137. $tokens = parent::toTokens(parent::getFieldBody());
  138. //Try creating any parameters
  139. foreach ($this->_params as $name => $value)
  140. {
  141. if (!is_null($value))
  142. {
  143. //Add the semi-colon separator
  144. $tokens[count($tokens)-1] .= ';';
  145. $tokens = array_merge($tokens, $this->generateTokenLines(
  146. ' ' . $this->_createParameter($name, $value)
  147. ));
  148. }
  149. }
  150. return $tokens;
  151. }
  152. // -- Private methods
  153. /**
  154. * Render a RFC 2047 compliant header parameter from the $name and $value.
  155. * @param string $name
  156. * @param string $value
  157. * @return string
  158. * @access private
  159. */
  160. private function _createParameter($name, $value)
  161. {
  162. $origValue = $value;
  163. $encoded = false;
  164. //Allow room for parameter name, indices, "=" and DQUOTEs
  165. $maxValueLength = $this->getMaxLineLength() - strlen($name . '=*N"";') - 1;
  166. $firstLineOffset = 0;
  167. //If it's not already a valid parameter value...
  168. if (!preg_match('/^' . $this->_tokenRe . '$/D', $value))
  169. {
  170. //TODO: text, or something else??
  171. //... and it's not ascii
  172. if (!preg_match('/^' . $this->getGrammar()->getDefinition('text') . '*$/D', $value))
  173. {
  174. $encoded = true;
  175. //Allow space for the indices, charset and language
  176. $maxValueLength = $this->getMaxLineLength() - strlen($name . '*N*="";') - 1;
  177. $firstLineOffset = strlen(
  178. $this->getCharset() . "'" . $this->getLanguage() . "'"
  179. );
  180. }
  181. }
  182. //Encode if we need to
  183. if ($encoded || strlen($value) > $maxValueLength)
  184. {
  185. if (isset($this->_paramEncoder))
  186. {
  187. $value = $this->_paramEncoder->encodeString(
  188. $origValue, $firstLineOffset, $maxValueLength
  189. );
  190. }
  191. else //We have to go against RFC 2183/2231 in some areas for interoperability
  192. {
  193. $value = $this->getTokenAsEncodedWord($origValue);
  194. $encoded = false;
  195. }
  196. }
  197. $valueLines = isset($this->_paramEncoder) ? explode("\r\n", $value) : array($value);
  198. //Need to add indices
  199. if (count($valueLines) > 1)
  200. {
  201. $paramLines = array();
  202. foreach ($valueLines as $i => $line)
  203. {
  204. $paramLines[] = $name . '*' . $i .
  205. $this->_getEndOfParameterValue($line, $encoded, $i == 0);
  206. }
  207. return implode(";\r\n ", $paramLines);
  208. }
  209. else
  210. {
  211. return $name . $this->_getEndOfParameterValue(
  212. $valueLines[0], $encoded, true
  213. );
  214. }
  215. }
  216. /**
  217. * Returns the parameter value from the "=" and beyond.
  218. * @param string $value to append
  219. * @param boolean $encoded
  220. * @param boolean $firstLine
  221. * @return string
  222. * @access private
  223. */
  224. private function _getEndOfParameterValue($value, $encoded = false, $firstLine = false)
  225. {
  226. if (!preg_match('/^' . $this->_tokenRe . '$/D', $value))
  227. {
  228. $value = '"' . $value . '"';
  229. }
  230. $prepend = '=';
  231. if ($encoded)
  232. {
  233. $prepend = '*=';
  234. if ($firstLine)
  235. {
  236. $prepend = '*=' . $this->getCharset() . "'" . $this->getLanguage() .
  237. "'";
  238. }
  239. }
  240. return $prepend . $value;
  241. }
  242. }