PageRenderTime 32ms CodeModel.GetById 21ms RepoModel.GetById 0ms app.codeStats 0ms

/libs/Nette/Mail/MailMimePart.php

https://github.com/vohnicky/treeview
PHP | 394 lines | 285 code | 45 blank | 64 comment | 27 complexity | 44e4d9ef596fce72d2acd11507d0de9b MD5 | raw file
  1. <?php
  2. /**
  3. * Nette Framework
  4. *
  5. * @copyright Copyright (c) 2004, 2010 David Grudl
  6. * @license http://nettephp.com/license Nette license
  7. * @link http://nettephp.com
  8. * @category Nette
  9. * @package Nette\Mail
  10. */
  11. /**
  12. * MIME message part.
  13. *
  14. * @copyright Copyright (c) 2004, 2010 David Grudl
  15. * @package Nette\Mail
  16. *
  17. * @property string $encoding
  18. * @property string $body
  19. * @property-read array $headers
  20. */
  21. class MailMimePart extends Object
  22. {
  23. /**#@+ encoding */
  24. const ENCODING_BASE64 = 'base64';
  25. const ENCODING_7BIT = '7bit';
  26. const ENCODING_8BIT = '8bit';
  27. const ENCODING_QUOTED_PRINTABLE = 'quoted-printable';
  28. /**#@-*/
  29. /**#@+ @ignore internal */
  30. const EOL = "\r\n";
  31. const LINE_LENGTH = 76;
  32. /**#@-*/
  33. /** @var array */
  34. private $headers = array();
  35. /** @var array */
  36. private $parts = array();
  37. /** @var string */
  38. private $body;
  39. /**
  40. * Sets a header.
  41. * @param string
  42. * @param string|array value or pair email => name
  43. * @param bool
  44. * @return MailMimePart provides a fluent interface
  45. */
  46. public function setHeader($name, $value, $append = FALSE)
  47. {
  48. if (!$name || preg_match('#[^a-z0-9-]#i', $name)) {
  49. throw new InvalidArgumentException("Header name must be non-empty alphanumeric string, '$name' given.");
  50. }
  51. if ($value == NULL) { // intentionally ==
  52. if (!$append) {
  53. unset($this->headers[$name]);
  54. }
  55. } elseif (is_array($value)) { // email
  56. $tmp = & $this->headers[$name];
  57. if (!$append || !is_array($tmp)) {
  58. $tmp = array();
  59. }
  60. foreach ($value as $email => $name) {
  61. if (!preg_match('#^[^@",\s]+@[^@",\s]+\.[a-z]{2,10}$#i', $email)) {
  62. throw new InvalidArgumentException("Email address '$email' is not valid.");
  63. }
  64. if (preg_match('#[\r\n]#', $name)) {
  65. throw new InvalidArgumentException("Name cannot contain the line separator.");
  66. }
  67. $tmp[$email] = $name;
  68. }
  69. } else {
  70. $this->headers[$name] = preg_replace('#[\r\n]+#', ' ', $value);
  71. }
  72. return $this;
  73. }
  74. /**
  75. * Returns a header.
  76. * @param string
  77. * @return mixed
  78. */
  79. public function getHeader($name)
  80. {
  81. return isset($this->headers[$name]) ? $this->headers[$name] : NULL;
  82. }
  83. /**
  84. * Removes a header.
  85. * @param string
  86. * @return MailMimePart provides a fluent interface
  87. */
  88. public function clearHeader($name)
  89. {
  90. unset($this->headers[$name]);
  91. return $this;
  92. }
  93. /**
  94. * Returns an encoded header.
  95. * @param string
  96. * @param string
  97. * @return string
  98. */
  99. public function getEncodedHeader($name, $charset = 'UTF-8')
  100. {
  101. $len = strlen($name) + 2;
  102. if (!isset($this->headers[$name])) {
  103. return NULL;
  104. } elseif (is_array($this->headers[$name])) {
  105. $s = '';
  106. foreach ($this->headers[$name] as $email => $name) {
  107. if ($name != NULL) { // intentionally ==
  108. $s .= self::encodeQuotedPrintableHeader(
  109. strspn($name, '.,;<@>()[]"=?') ? '"' . addcslashes($name, '"\\') . '"' : $name,
  110. $charset, $len
  111. );
  112. $email = " <$email>";
  113. }
  114. if ($len + strlen($email) + 1 > self::LINE_LENGTH) {
  115. $s .= self::EOL . "\t";
  116. $len = 1;
  117. }
  118. $s .= "$email,";
  119. $len += strlen($email) + 1;
  120. }
  121. return substr($s, 0, -1);
  122. } else {
  123. return self::encodeQuotedPrintableHeader($this->headers[$name], $charset, $len);
  124. }
  125. }
  126. /**
  127. * Returns all headers.
  128. * @return array
  129. */
  130. public function getHeaders()
  131. {
  132. return $this->headers;
  133. }
  134. /**
  135. * Sets Content-Type header.
  136. * @param string
  137. * @param string
  138. * @return MailMimePart provides a fluent interface
  139. */
  140. public function setContentType($contentType, $charset = NULL)
  141. {
  142. $this->setHeader('Content-Type', $contentType . ($charset ? "; charset=$charset" : ''));
  143. return $this;
  144. }
  145. /**
  146. * Sets Content-Transfer-Encoding header.
  147. * @param string
  148. * @return MailMimePart provides a fluent interface
  149. */
  150. public function setEncoding($encoding)
  151. {
  152. $this->setHeader('Content-Transfer-Encoding', $encoding);
  153. return $this;
  154. }
  155. /**
  156. * Returns Content-Transfer-Encoding header.
  157. * @return string
  158. */
  159. public function getEncoding()
  160. {
  161. return $this->getHeader('Content-Transfer-Encoding');
  162. }
  163. /**
  164. * Adds or creates new multipart.
  165. * @param MailMimePart
  166. * @return MailMimePart
  167. */
  168. public function addPart(MailMimePart $part = NULL)
  169. {
  170. return $this->parts[] = $part === NULL ? new self : $part;
  171. }
  172. /**
  173. * Sets textual body.
  174. * @param mixed
  175. * @return MailMimePart provides a fluent interface
  176. */
  177. public function setBody($body)
  178. {
  179. $this->body = $body;
  180. return $this;
  181. }
  182. /**
  183. * Gets textual body.
  184. * @return mixed
  185. */
  186. public function getBody()
  187. {
  188. return $this->body;
  189. }
  190. /********************* building ****************d*g**/
  191. /**
  192. * Returns encoded message.
  193. * @return string
  194. */
  195. public function generateMessage()
  196. {
  197. $output = '';
  198. $boundary = '--------' . md5(uniqid('', TRUE));
  199. foreach ($this->headers as $name => $value) {
  200. $output .= $name . ': ' . $this->getEncodedHeader($name);
  201. if ($this->parts && $name === 'Content-Type') {
  202. $output .= ';' . self::EOL . "\tboundary=\"$boundary\"";
  203. }
  204. $output .= self::EOL;
  205. }
  206. $output .= self::EOL;
  207. $body = (string) $this->body;
  208. if ($body !== '') {
  209. switch ($this->getEncoding()) {
  210. case self::ENCODING_QUOTED_PRINTABLE:
  211. $output .= function_exists('quoted_printable_encode') ? quoted_printable_encode($body) : self::encodeQuotedPrintable($body);
  212. break;
  213. case self::ENCODING_BASE64:
  214. $output .= rtrim(chunk_split(base64_encode($body), self::LINE_LENGTH, self::EOL));
  215. break;
  216. case self::ENCODING_7BIT:
  217. $body = preg_replace('#[\x80-\xFF]+#', '', $body);
  218. // break intentionally omitted
  219. case self::ENCODING_8BIT:
  220. $body = str_replace(array("\x00", "\r"), '', $body);
  221. $body = str_replace("\n", self::EOL, $body);
  222. $output .= $body;
  223. break;
  224. default:
  225. throw new InvalidStateException('Unknown encoding');
  226. }
  227. }
  228. if ($this->parts) {
  229. if (substr($output, -strlen(self::EOL)) !== self::EOL) $output .= self::EOL;
  230. foreach ($this->parts as $part) {
  231. $output .= '--' . $boundary . self::EOL . $part->generateMessage() . self::EOL;
  232. }
  233. $output .= '--' . $boundary.'--';
  234. }
  235. return $output;
  236. }
  237. /********************* QuotedPrintable helpers ****************d*g**/
  238. /**
  239. * Converts a 8 bit header to a quoted-printable string.
  240. * @param string
  241. * @param string
  242. * @param int
  243. * @return string
  244. */
  245. private static function encodeQuotedPrintableHeader($s, $charset = 'UTF-8', & $len = 0)
  246. {
  247. $range = '!"#$%&\'()*+,-./0123456789:;<>@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^`abcdefghijklmnopqrstuvwxyz{|}'; // \x21-\x7E without \x3D \x3F \x5F
  248. if (strspn($s, $range . "=? _\r\n\t") === strlen($s)) {
  249. return $s;
  250. }
  251. $prefix = "=?$charset?Q?";
  252. $pos = 0;
  253. $len += strlen($prefix);
  254. $o = $prefix;
  255. $size = strlen($s);
  256. while ($pos < $size) {
  257. if ($l = strspn($s, $range, $pos)) {
  258. while ($len + $l > self::LINE_LENGTH - 2) { // 2 = length of suffix ?=
  259. $lx = self::LINE_LENGTH - $len - 2;
  260. $o .= substr($s, $pos, $lx) . '?=' . self::EOL . "\t" . $prefix;
  261. $pos += $lx;
  262. $l -= $lx;
  263. $len = strlen($prefix) + 1;
  264. }
  265. $o .= substr($s, $pos, $l);
  266. $len += $l;
  267. $pos += $l;
  268. } else {
  269. $len += 3;
  270. // \xC0 tests UTF-8 character boudnary; 9 is reserved space for 4bytes UTF-8 character
  271. if (($s[$pos] & "\xC0") !== "\x80" && $len > self::LINE_LENGTH - 2 - 9) {
  272. $o .= '?=' . self::EOL . "\t" . $prefix;
  273. $len = strlen($prefix) + 1 + 3;
  274. }
  275. $o .= '=' . strtoupper(bin2hex($s[$pos]));
  276. $pos++;
  277. }
  278. }
  279. return $o . '?=';
  280. }
  281. /**
  282. * Converts a 8 bit string to a quoted-printable string.
  283. * @param string
  284. * @return string
  285. */
  286. public static function encodeQuotedPrintable($s)
  287. {
  288. $range = '!"#$%&\'()*+,-./0123456789:;<>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}'; // \x21-\x7E without \x3D
  289. $pos = 0;
  290. $len = 0;
  291. $o = '';
  292. $size = strlen($s);
  293. while ($pos < $size) {
  294. if ($l = strspn($s, $range, $pos)) {
  295. while ($len + $l > self::LINE_LENGTH - 1) { // 1 = length of suffix =
  296. $lx = self::LINE_LENGTH - $len - 1;
  297. $o .= substr($s, $pos, $lx) . '=' . self::EOL;
  298. $pos += $lx;
  299. $l -= $lx;
  300. $len = 0;
  301. }
  302. $o .= substr($s, $pos, $l);
  303. $len += $l;
  304. $pos += $l;
  305. } else {
  306. $len += 3;
  307. if ($len > self::LINE_LENGTH - 1) {
  308. $o .= '=' . self::EOL;
  309. $len = 3;
  310. }
  311. $o .= '=' . strtoupper(bin2hex($s[$pos]));
  312. $pos++;
  313. }
  314. }
  315. return rtrim($o, '=' . self::EOL);
  316. }
  317. }