From a99c34159d03f2b5b525d6d8cc38509c4ac2f0a1 Mon Sep 17 00:00:00 2001 From: Aleksander Machniak Date: Tue, 31 Mar 2015 18:56:32 +0200 Subject: Enigma: Implemented messages signing and encrypting --- plugins/enigma/lib/enigma_mime_message.php | 299 +++++++++++++++++++++++++++++ 1 file changed, 299 insertions(+) create mode 100644 plugins/enigma/lib/enigma_mime_message.php (limited to 'plugins/enigma/lib/enigma_mime_message.php') diff --git a/plugins/enigma/lib/enigma_mime_message.php b/plugins/enigma/lib/enigma_mime_message.php new file mode 100644 index 000000000..feed78e03 --- /dev/null +++ b/plugins/enigma/lib/enigma_mime_message.php @@ -0,0 +1,299 @@ + | + +-------------------------------------------------------------------------+ +*/ + +class enigma_mime_message extends Mail_mime +{ + const PGP_SIGNED = 1; + const PGP_ENCRYPTED = 2; + + protected $_type; + protected $_message; + protected $_body; + protected $_signature; + protected $_encrypted; + + + /** + * Object constructor + * + * @param Mail_mime Original message + * @param int Output message type + */ + function __construct($message, $type) + { + $this->_message = $message; + $this->_type = $type; + + // clone parameters + foreach (array_keys($this->_build_params) as $param) { + $this->_build_params[$param] = $message->getParam($param); + } + + // clone headers + $this->_headers = $message->_headers; + +/* + if ($message->getParam('delay_file_io')) { + // use common temp dir + $temp_dir = $this->config->get('temp_dir'); + $body_file = tempnam($temp_dir, 'rcmMsg'); + $mime_result = $message->saveMessageBody($body_file); + + if (is_a($mime_result, 'PEAR_Error')) { + self::raise_error(array('code' => 650, 'type' => 'php', + 'file' => __FILE__, 'line' => __LINE__, + 'message' => "Could not create message: ".$mime_result->getMessage()), + true, false); + return false; + } + + $msg_body = fopen($body_file, 'r'); + } + else { +*/ + // \r\n is must-have here + $this->_body = $message->get() . "\r\n"; +/* + } +*/ + } + + /** + * Check if the message is multipart (requires PGP/MIME) + * + * @return bool True if it is multipart, otherwise False + */ + function isMultipart() + { + return $this->_message instanceof enigma_mime_message + || !empty($this->_message->_parts) || $this->_message->getHTMLBody(); + } + + /** + * Get e-mail address of message sender + * + * @return string Sender address + */ + function getFromAddress() + { + // get sender address + $headers = $this->_message->headers(); + $from = rcube_mime::decode_address_list($headers['From'], 1, false, null, true); + $from = $from[1]; + + return $from; + } + + /** + * Get recipients' e-mail addresses + * + * @return array Recipients' addresses + */ + function getRecipients() + { + // get sender address + $headers = $this->_message->headers(); + $to = rcube_mime::decode_address_list($headers['To'], null, false, null, true); + $cc = rcube_mime::decode_address_list($headers['Cc'], null, false, null, true); + $bcc = rcube_mime::decode_address_list($headers['Bcc'], null, false, null, true); + + $recipients = array_unique(array_merge($to, $cc, $bcc)); + $recipients = array_diff($recipients, array('undisclosed-recipients:')); + + return $recipients; + } + + /** + * Get original message body, to be encrypted/signed + * + * @return string Message body + */ + function getOrigBody() + { + $_headers = $this->_message->headers(); + $headers = array(); + + if ($_headers['Content-Transfer-Encoding']) { + $headers[] = 'Content-Transfer-Encoding: ' . $_headers['Content-Transfer-Encoding']; + } + $headers[] = 'Content-Type: ' . $_headers['Content-Type']; + + return implode("\r\n", $headers) . "\r\n\r\n" . $this->_body; + } + + /** + * Register signature attachment + * + * @param string Signature body + */ + function addPGPSignature($body) + { + $this->_signature = $body; + } + + /** + * Register encrypted body + * + * @param string Encrypted body + */ + function setPGPEncryptedBody($body) + { + $this->_encrypted = $body; + } + + /** + * Builds the multipart message. + * + * @param array $params Build parameters that change the way the email + * is built. Should be associative. See $_build_params. + * @param resource $filename Output file where to save the message instead of + * returning it + * @param boolean $skip_head True if you want to return/save only the message + * without headers + * + * @return mixed The MIME message content string, null or PEAR error object + * @access public + */ + function get($params = null, $filename = null, $skip_head = false) + { + if (isset($params)) { + while (list($key, $value) = each($params)) { + $this->_build_params[$key] = $value; + } + } + + $this->_checkParams(); + + if ($this->_type == self::PGP_SIGNED) { + $body = "This is an OpenPGP/MIME signed message (RFC 4880 and 3156)"; + $params = array( + 'content_type' => "multipart/signed; micalg=pgp-sha1; protocol=\"application/pgp-signature\"", + 'eol' => $this->_build_params['eol'], + ); + + $message = new Mail_mimePart($body, $params); + + if (!empty($this->_body)) { + $headers = $this->_message->headers(); + $params = array('content_type' => $headers['Content-Type']); + + if ($headers['Content-Transfer-Encoding']) { + $params['encoding'] = $headers['Content-Transfer-Encoding']; + } + + $message->addSubpart($this->_body, $params); + } + + if (!empty($this->_signature)) { + $message->addSubpart($this->_signature, array( + 'filename' => 'signature.asc', + 'content_type' => 'application/pgp-signature', + 'disposition' => 'attachment', + 'description' => 'OpenPGP digital signature', + )); + } + } + else if ($this->_type == self::PGP_ENCRYPTED) { + $body = "This is an OpenPGP/MIME encrypted message (RFC 4880 and 3156)"; + $params = array( + 'content_type' => "multipart/encrypted; protocol=\"application/pgp-encrypted\"", + 'eol' => $this->_build_params['eol'], + ); + + $message = new Mail_mimePart($body, $params); + + $message->addSubpart('Version: 1', array( + 'content_type' => 'application/pgp-encrypted', + 'description' => 'PGP/MIME version identification', + )); + + $message->addSubpart($this->_encrypted, array( + 'content_type' => 'application/octet-stream', + 'description' => 'PGP/MIME encrypted message', + 'disposition' => 'inline', + 'filename' => 'encrypted.asc', + )); + } + + // Use saved boundary + if (!empty($this->_build_params['boundary'])) { + $boundary = $this->_build_params['boundary']; + } + else { + $boundary = null; + } + + // Write output to file + if ($filename) { + // Append mimePart message headers and body into file + $headers = $message->encodeToFile($filename, $boundary, $skip_head); + if ($this->_isError($headers)) { + return $headers; + } + $this->_headers = array_merge($this->_headers, $headers); + return null; + } + else { + $output = $message->encode($boundary, $skip_head); + if ($this->_isError($output)) { + return $output; + } + $this->_headers = array_merge($this->_headers, $output['headers']); + return $output['body']; + } + } + + /** + * Get Content-Type and Content-Transfer-Encoding headers of the message + * + * @return array Headers array + * @access private + */ + function _contentHeaders() + { + $this->_checkParams(); + + $eol = !empty($this->_build_params['eol']) ? $this->_build_params['eol'] : "\r\n"; + + // multipart message: and boundary + if (!empty($this->_build_params['boundary'])) { + $boundary = $this->_build_params['boundary']; + } + else if (!empty($this->_headers['Content-Type']) + && preg_match('/boundary="([^"]+)"/', $this->_headers['Content-Type'], $m) + ) { + $boundary = $m[1]; + } + else { + $boundary = '=_' . md5(rand() . microtime()); + } + + $this->_build_params['boundary'] = $boundary; + + if ($this->_type == self::PGP_SIGNED) { + $headers['Content-Type'] = "multipart/signed; micalg=pgp-sha1;$eol" + ." protocol=\"application/pgp-signature\";$eol" + ." boundary=\"$boundary\""; + } + else if ($this->_type == self::PGP_ENCRYPTED) { + $headers['Content-Type'] = "multipart/encrypted;$eol" + ." protocol=\"application/pgp-encrypted\";$eol" + ." boundary=\"$boundary\""; + } + + return $headers; + } +} -- cgit v1.2.3