TYPO3  7.6
AbstractHeader.php
Go to the documentation of this file.
1 <?php
2 
3 /*
4  * This file is part of SwiftMailer.
5  * (c) 2004-2009 Chris Corbyn
6  *
7  * For the full copyright and license information, please view the LICENSE
8  * file that was distributed with this source code.
9  */
10 
17 {
23  private $_name;
24 
30  private $_grammar;
31 
37  private $_encoder;
38 
44  private $_lineLength = 78;
45 
51  private $_lang;
52 
58  private $_charset = 'utf-8';
59 
65  private $_cachedValue = null;
66 
72  public function __construct(Swift_Mime_Grammar $grammar)
73  {
74  $this->setGrammar($grammar);
75  }
76 
82  public function setCharset($charset)
83  {
84  $this->clearCachedValueIf($charset != $this->_charset);
85  $this->_charset = $charset;
86  if (isset($this->_encoder)) {
87  $this->_encoder->charsetChanged($charset);
88  }
89  }
90 
96  public function getCharset()
97  {
98  return $this->_charset;
99  }
100 
109  public function setLanguage($lang)
110  {
111  $this->clearCachedValueIf($this->_lang != $lang);
112  $this->_lang = $lang;
113  }
114 
120  public function getLanguage()
121  {
122  return $this->_lang;
123  }
124 
130  public function setEncoder(Swift_Mime_HeaderEncoder $encoder)
131  {
132  $this->_encoder = $encoder;
133  $this->setCachedValue(null);
134  }
135 
141  public function getEncoder()
142  {
143  return $this->_encoder;
144  }
145 
151  public function setGrammar(Swift_Mime_Grammar $grammar)
152  {
153  $this->_grammar = $grammar;
154  $this->setCachedValue(null);
155  }
156 
162  public function getGrammar()
163  {
164  return $this->_grammar;
165  }
166 
172  public function getFieldName()
173  {
174  return $this->_name;
175  }
176 
182  public function setMaxLineLength($lineLength)
183  {
184  $this->clearCachedValueIf($this->_lineLength != $lineLength);
185  $this->_lineLength = $lineLength;
186  }
187 
193  public function getMaxLineLength()
194  {
195  return $this->_lineLength;
196  }
197 
205  public function toString()
206  {
207  return $this->_tokensToString($this->toTokens());
208  }
209 
217  public function __toString()
218  {
219  return $this->toString();
220  }
221 
222  // -- Points of extension
223 
229  protected function setFieldName($name)
230  {
231  $this->_name = $name;
232  }
233 
245  protected function createPhrase(Swift_Mime_Header $header, $string, $charset, Swift_Mime_HeaderEncoder $encoder = null, $shorten = false)
246  {
247  // Treat token as exactly what was given
248  $phraseStr = $string;
249  // If it's not valid
250  if (!preg_match('/^'.$this->getGrammar()->getDefinition('phrase').'$/D', $phraseStr)) {
251  // .. but it is just ascii text, try escaping some characters
252  // and make it a quoted-string
253  if (preg_match('/^'.$this->getGrammar()->getDefinition('text').'*$/D', $phraseStr)) {
254  $phraseStr = $this->getGrammar()->escapeSpecials(
255  $phraseStr, array('"'), $this->getGrammar()->getSpecials()
256  );
257  $phraseStr = '"'.$phraseStr.'"';
258  } else {
259  // ... otherwise it needs encoding
260  // Determine space remaining on line if first line
261  if ($shorten) {
262  $usedLength = strlen($header->getFieldName().': ');
263  } else {
264  $usedLength = 0;
265  }
266  $phraseStr = $this->encodeWords($header, $string, $usedLength);
267  }
268  }
269 
270  return $phraseStr;
271  }
272 
282  protected function encodeWords(Swift_Mime_Header $header, $input, $usedLength = -1)
283  {
284  $value = '';
285 
286  $tokens = $this->getEncodableWordTokens($input);
287 
288  foreach ($tokens as $token) {
289  // See RFC 2822, Sect 2.2 (really 2.2 ??)
290  if ($this->tokenNeedsEncoding($token)) {
291  // Don't encode starting WSP
292  $firstChar = substr($token, 0, 1);
293  switch ($firstChar) {
294  case ' ':
295  case "\t":
296  $value .= $firstChar;
297  $token = substr($token, 1);
298  }
299 
300  if (-1 == $usedLength) {
301  $usedLength = strlen($header->getFieldName().': ') + strlen($value);
302  }
303  $value .= $this->getTokenAsEncodedWord($token, $usedLength);
304 
305  $header->setMaxLineLength(76); // Forcefully override
306  } else {
307  $value .= $token;
308  }
309  }
310 
311  return $value;
312  }
313 
321  protected function tokenNeedsEncoding($token)
322  {
323  return preg_match('~[\x00-\x08\x10-\x19\x7F-\xFF\r\n]~', $token);
324  }
325 
333  protected function getEncodableWordTokens($string)
334  {
335  $tokens = array();
336 
337  $encodedToken = '';
338  // Split at all whitespace boundaries
339  foreach (preg_split('~(?=[\t ])~', $string) as $token) {
340  if ($this->tokenNeedsEncoding($token)) {
341  $encodedToken .= $token;
342  } else {
343  if (strlen($encodedToken) > 0) {
344  $tokens[] = $encodedToken;
345  $encodedToken = '';
346  }
347  $tokens[] = $token;
348  }
349  }
350  if (strlen($encodedToken)) {
351  $tokens[] = $encodedToken;
352  }
353 
354  return $tokens;
355  }
356 
365  protected function getTokenAsEncodedWord($token, $firstLineOffset = 0)
366  {
367  // Adjust $firstLineOffset to account for space needed for syntax
368  $charsetDecl = $this->_charset;
369  if (isset($this->_lang)) {
370  $charsetDecl .= '*'.$this->_lang;
371  }
372  $encodingWrapperLength = strlen(
373  '=?'.$charsetDecl.'?'.$this->_encoder->getName().'??='
374  );
375 
376  if ($firstLineOffset >= 75) {
377  //Does this logic need to be here?
378  $firstLineOffset = 0;
379  }
380 
381  $encodedTextLines = explode("\r\n",
382  $this->_encoder->encodeString(
383  $token, $firstLineOffset, 75 - $encodingWrapperLength, $this->_charset
384  )
385  );
386 
387  if (strtolower($this->_charset) !== 'iso-2022-jp') {
388  // special encoding for iso-2022-jp using mb_encode_mimeheader
389  foreach ($encodedTextLines as $lineNum => $line) {
390  $encodedTextLines[$lineNum] = '=?'.$charsetDecl.
391  '?'.$this->_encoder->getName().
392  '?'.$line.'?=';
393  }
394  }
395 
396  return implode("\r\n ", $encodedTextLines);
397  }
398 
406  protected function generateTokenLines($token)
407  {
408  return preg_split('~(\r\n)~', $token, -1, PREG_SPLIT_DELIM_CAPTURE);
409  }
410 
416  protected function setCachedValue($value)
417  {
418  $this->_cachedValue = $value;
419  }
420 
426  protected function getCachedValue()
427  {
428  return $this->_cachedValue;
429  }
430 
436  protected function clearCachedValueIf($condition)
437  {
438  if ($condition) {
439  $this->setCachedValue(null);
440  }
441  }
442 
450  protected function toTokens($string = null)
451  {
452  if (is_null($string)) {
453  $string = $this->getFieldBody();
454  }
455 
456  $tokens = array();
457 
458  // Generate atoms; split at all invisible boundaries followed by WSP
459  foreach (preg_split('~(?=[ \t])~', $string) as $token) {
460  $newTokens = $this->generateTokenLines($token);
461  foreach ($newTokens as $newToken) {
462  $tokens[] = $newToken;
463  }
464  }
465 
466  return $tokens;
467  }
468 
477  private function _tokensToString(array $tokens)
478  {
479  $lineCount = 0;
480  $headerLines = array();
481  $headerLines[] = $this->_name.': ';
482  $currentLine = &$headerLines[$lineCount++];
483 
484  // Build all tokens back into compliant header
485  foreach ($tokens as $i => $token) {
486  // Line longer than specified maximum or token was just a new line
487  if (("\r\n" == $token) ||
488  ($i > 0 && strlen($currentLine.$token) > $this->_lineLength)
489  && 0 < strlen($currentLine)) {
490  $headerLines[] = '';
491  $currentLine = &$headerLines[$lineCount++];
492  }
493 
494  // Append token to the line
495  if ("\r\n" != $token) {
496  $currentLine .= $token;
497  }
498  }
499 
500  // Implode with FWS (RFC 2822, 2.2.3)
501  return implode("\r\n", $headerLines)."\r\n";
502  }
503 }