You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

enigma_mime_message.php 9.6KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294
  1. <?php
  2. /**
  3. +-------------------------------------------------------------------------+
  4. | Mail_mime wrapper for the Enigma Plugin |
  5. | |
  6. | Copyright (C) 2010-2015 The Roundcube Dev Team |
  7. | |
  8. | Licensed under the GNU General Public License version 3 or |
  9. | any later version with exceptions for skins & plugins. |
  10. | See the README file for a full license statement. |
  11. | |
  12. +-------------------------------------------------------------------------+
  13. | Author: Aleksander Machniak <alec@alec.pl> |
  14. +-------------------------------------------------------------------------+
  15. */
  16. class enigma_mime_message extends Mail_mime
  17. {
  18. const PGP_SIGNED = 1;
  19. const PGP_ENCRYPTED = 2;
  20. protected $type;
  21. protected $message;
  22. protected $body;
  23. protected $signature;
  24. protected $encrypted;
  25. /**
  26. * Object constructor
  27. *
  28. * @param Mail_mime Original message
  29. * @param int Output message type
  30. */
  31. function __construct($message, $type)
  32. {
  33. $this->message = $message;
  34. $this->type = $type;
  35. // clone parameters
  36. foreach (array_keys($this->build_params) as $param) {
  37. $this->build_params[$param] = $message->getParam($param);
  38. }
  39. // clone headers
  40. $this->headers = $message->headers();
  41. // \r\n is must-have here
  42. $this->body = $message->get() . "\r\n";
  43. }
  44. /**
  45. * Check if the message is multipart (requires PGP/MIME)
  46. *
  47. * @return bool True if it is multipart, otherwise False
  48. */
  49. public function isMultipart()
  50. {
  51. return $this->message instanceof enigma_mime_message
  52. || $this->message->isMultipart() || $this->message->getHTMLBody();
  53. }
  54. /**
  55. * Get e-mail address of message sender
  56. *
  57. * @return string Sender address
  58. */
  59. public function getFromAddress()
  60. {
  61. // get sender address
  62. $headers = $this->message->headers();
  63. $from = rcube_mime::decode_address_list($headers['From'], 1, false, null, true);
  64. $from = $from[1];
  65. return $from;
  66. }
  67. /**
  68. * Get recipients' e-mail addresses
  69. *
  70. * @return array Recipients' addresses
  71. */
  72. public function getRecipients()
  73. {
  74. // get sender address
  75. $headers = $this->message->headers();
  76. $to = rcube_mime::decode_address_list($headers['To'], null, false, null, true);
  77. $cc = rcube_mime::decode_address_list($headers['Cc'], null, false, null, true);
  78. $bcc = rcube_mime::decode_address_list($headers['Bcc'], null, false, null, true);
  79. $recipients = array_unique(array_merge($to, $cc, $bcc));
  80. $recipients = array_diff($recipients, array('undisclosed-recipients:'));
  81. return $recipients;
  82. }
  83. /**
  84. * Get original message body, to be encrypted/signed
  85. *
  86. * @return string Message body
  87. */
  88. public function getOrigBody()
  89. {
  90. $_headers = $this->message->headers();
  91. $headers = array();
  92. if ($_headers['Content-Transfer-Encoding']
  93. && stripos($_headers['Content-Type'], 'multipart') === false
  94. ) {
  95. $headers[] = 'Content-Transfer-Encoding: ' . $_headers['Content-Transfer-Encoding'];
  96. }
  97. $headers[] = 'Content-Type: ' . $_headers['Content-Type'];
  98. return implode("\r\n", $headers) . "\r\n\r\n" . $this->body;
  99. }
  100. /**
  101. * Register signature attachment
  102. *
  103. * @param string Signature body
  104. */
  105. public function addPGPSignature($body)
  106. {
  107. $this->signature = $body;
  108. // Reset Content-Type to be overwritten with valid boundary
  109. unset($this->headers['Content-Type']);
  110. unset($this->headers['Content-Transfer-Encoding']);
  111. }
  112. /**
  113. * Register encrypted body
  114. *
  115. * @param string Encrypted body
  116. */
  117. public function setPGPEncryptedBody($body)
  118. {
  119. $this->encrypted = $body;
  120. // Reset Content-Type to be overwritten with valid boundary
  121. unset($this->headers['Content-Type']);
  122. unset($this->headers['Content-Transfer-Encoding']);
  123. }
  124. /**
  125. * Builds the multipart message.
  126. *
  127. * @param array $params Build parameters that change the way the email
  128. * is built. Should be associative. See $_build_params.
  129. * @param resource $filename Output file where to save the message instead of
  130. * returning it
  131. * @param boolean $skip_head True if you want to return/save only the message
  132. * without headers
  133. *
  134. * @return mixed The MIME message content string, null or PEAR error object
  135. */
  136. public function get($params = null, $filename = null, $skip_head = false)
  137. {
  138. if (isset($params)) {
  139. while (list($key, $value) = each($params)) {
  140. $this->build_params[$key] = $value;
  141. }
  142. }
  143. $this->checkParams();
  144. if ($this->type == self::PGP_SIGNED) {
  145. $params = array(
  146. 'preamble' => "This is an OpenPGP/MIME signed message (RFC 4880 and 3156)",
  147. 'content_type' => "multipart/signed; micalg=pgp-sha1; protocol=\"application/pgp-signature\"",
  148. 'eol' => $this->build_params['eol'],
  149. );
  150. $message = new Mail_mimePart('', $params);
  151. if (!empty($this->body)) {
  152. $headers = $this->message->headers();
  153. $params = array('content_type' => $headers['Content-Type']);
  154. if ($headers['Content-Transfer-Encoding']
  155. && stripos($headers['Content-Type'], 'multipart') === false
  156. ) {
  157. $params['encoding'] = $headers['Content-Transfer-Encoding'];
  158. }
  159. $message->addSubpart($this->body, $params);
  160. }
  161. if (!empty($this->signature)) {
  162. $message->addSubpart($this->signature, array(
  163. 'filename' => 'signature.asc',
  164. 'content_type' => 'application/pgp-signature',
  165. 'disposition' => 'attachment',
  166. 'description' => 'OpenPGP digital signature',
  167. ));
  168. }
  169. }
  170. else if ($this->type == self::PGP_ENCRYPTED) {
  171. $params = array(
  172. 'preamble' => "This is an OpenPGP/MIME encrypted message (RFC 4880 and 3156)",
  173. 'content_type' => "multipart/encrypted; protocol=\"application/pgp-encrypted\"",
  174. 'eol' => $this->build_params['eol'],
  175. );
  176. $message = new Mail_mimePart('', $params);
  177. $message->addSubpart('Version: 1', array(
  178. 'content_type' => 'application/pgp-encrypted',
  179. 'description' => 'PGP/MIME version identification',
  180. ));
  181. $message->addSubpart($this->encrypted, array(
  182. 'content_type' => 'application/octet-stream',
  183. 'description' => 'PGP/MIME encrypted message',
  184. 'disposition' => 'inline',
  185. 'filename' => 'encrypted.asc',
  186. ));
  187. }
  188. // Use saved boundary
  189. if (!empty($this->build_params['boundary'])) {
  190. $boundary = $this->build_params['boundary'];
  191. }
  192. else {
  193. $boundary = null;
  194. }
  195. // Write output to file
  196. if ($filename) {
  197. // Append mimePart message headers and body into file
  198. $headers = $message->encodeToFile($filename, $boundary, $skip_head);
  199. if ($this->isError($headers)) {
  200. return $headers;
  201. }
  202. $this->headers = array_merge($this->headers, $headers);
  203. return;
  204. }
  205. else {
  206. $output = $message->encode($boundary, $skip_head);
  207. if ($this->isError($output)) {
  208. return $output;
  209. }
  210. $this->headers = array_merge($this->headers, $output['headers']);
  211. return $output['body'];
  212. }
  213. }
  214. /**
  215. * Get Content-Type and Content-Transfer-Encoding headers of the message
  216. *
  217. * @return array Headers array
  218. */
  219. protected function contentHeaders()
  220. {
  221. $this->checkParams();
  222. $eol = $this->build_params['eol'] ?: "\r\n";
  223. // multipart message: and boundary
  224. if (!empty($this->build_params['boundary'])) {
  225. $boundary = $this->build_params['boundary'];
  226. }
  227. else if (!empty($this->headers['Content-Type'])
  228. && preg_match('/boundary="([^"]+)"/', $this->headers['Content-Type'], $m)
  229. ) {
  230. $boundary = $m[1];
  231. }
  232. else {
  233. $boundary = '=_' . md5(rand() . microtime());
  234. }
  235. $this->build_params['boundary'] = $boundary;
  236. if ($this->type == self::PGP_SIGNED) {
  237. $headers['Content-Type'] = "multipart/signed; micalg=pgp-sha1;$eol"
  238. ." protocol=\"application/pgp-signature\";$eol"
  239. ." boundary=\"$boundary\"";
  240. }
  241. else if ($this->type == self::PGP_ENCRYPTED) {
  242. $headers['Content-Type'] = "multipart/encrypted;$eol"
  243. ." protocol=\"application/pgp-encrypted\";$eol"
  244. ." boundary=\"$boundary\"";
  245. }
  246. return $headers;
  247. }
  248. }