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