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