From eebf1b64bda27bee12f05b153842247b0ff1f26f Mon Sep 17 00:00:00 2001 From: Michael M Slusarz Date: Thu, 7 Nov 2013 00:48:32 -0700 Subject: [PATCH] [mms] Strip PGP armor text when replying to a message. --- imp/docs/CHANGES | 1 + imp/lib/Compose.php | 12 +++++ imp/lib/Crypt/Pgp.php | 92 ++++++++++++++++++++++++++++++++++ imp/lib/Mime/Viewer/Plain.php | 93 +++-------------------------------- imp/package.xml | 1 + 5 files changed, 112 insertions(+), 87 deletions(-) diff --git a/imp/docs/CHANGES b/imp/docs/CHANGES index d2503475e1f..3a9b6e0f462 100644 --- a/imp/docs/CHANGES +++ b/imp/docs/CHANGES @@ -2,6 +2,7 @@ v6.2.0-git ---------- +[mms] Strip PGP armor text when replying to a message. [mms] Move determination whether to scan plaintext messages for PGP data from preferences to MIME viewer configuration. [mms] Add copy email option to the address context menu in dynamic view. diff --git a/imp/lib/Compose.php b/imp/lib/Compose.php index 8dc17aea183..ec2e3cd62ea 100644 --- a/imp/lib/Compose.php +++ b/imp/lib/Compose.php @@ -2837,6 +2837,18 @@ protected function _getMessageText($contents, array $options = array()) $msg = rtrim(substr($msg, 0, $pos)); } + /* Remove PGP armored text. */ + $pgp = $injector->getInstance('IMP_Crypt_Pgp')->armoredTextToPart($msg); + if (!is_null($pgp)) { + $msg = ''; + $pgp->buildMimeIds(); + foreach ($pgp->contentTypeMap() as $key => $val) { + if (strpos($val, 'text/') === 0) { + $msg .= $pgp[$key]->getContents(); + } + } + } + if ($part->getContentTypeParameter('format') == 'flowed') { $flowed = new Horde_Text_Flowed($msg, 'UTF-8'); if (Horde_String::lower($part->getContentTypeParameter('delsp')) == 'yes') { diff --git a/imp/lib/Crypt/Pgp.php b/imp/lib/Crypt/Pgp.php index 6d471848b98..ef302aa2768 100644 --- a/imp/lib/Crypt/Pgp.php +++ b/imp/lib/Crypt/Pgp.php @@ -699,4 +699,96 @@ public function getKeys($data) return $out; } + /** + * TODO + */ + public function armoredTextToPart($data, $charset = 'UTF-8') + { + $parts = $this->parsePGPData($data); + + if (empty($parts) || + ((count($parts) == 1) && ($parts[0]['type'] == self::ARMOR_TEXT))) { + return null; + } + + $new_part = new Horde_Mime_Part(); + $new_part->setType('multipart/mixed'); + + foreach ($parts as $val) { + switch ($val['type']) { + case self::ARMOR_TEXT: + $part = new Horde_Mime_Part(); + $part->setType('text/plain'); + $part->setCharset($charset); + $part->setContents(implode("\n", $val['data'])); + $new_part->addPart($part); + break; + + case self::ARMOR_PUBLIC_KEY: + $part = new Horde_Mime_Part(); + $part->setType('application/pgp-keys'); + $part->setContents(implode("\n", $val['data'])); + $new_part->addPart($part); + break; + + case self::ARMOR_MESSAGE: + $part = new Horde_Mime_Part(); + $part->setType('multipart/encrypted'); + $part->setMetadata(IMP_Mime_Viewer_Pgp::PGP_ARMOR, true); + $part->setContentTypeParameter('protocol', 'application/pgp-encrypted'); + + $part1 = new Horde_Mime_Part(); + $part1->setType('application/pgp-encrypted'); + $part1->setContents("Version: 1\n"); + + $part2 = new Horde_Mime_Part(); + $part2->setType('application/octet-stream'); + $part2->setContents(implode("\n", $val['data'])); + $part2->setDisposition('inline'); + + $part->addPart($part1); + $part->addPart($part2); + + $new_part->addPart($part); + break; + + case self::ARMOR_SIGNED_MESSAGE: + if (($sig = current($parts)) && + ($sig['type'] == self::ARMOR_SIGNATURE)) { + $part = new Horde_Mime_Part(); + $part->setType('multipart/signed'); + // TODO: add micalg parameter + $part->setContentTypeParameter('protocol', 'application/pgp-signature'); + + $part1 = new Horde_Mime_Part(); + $part1->setType('text/plain'); + $part1->setCharset($charset); + + $part1_data = implode("\n", $val['data']); + $part1->setContents(substr($part1_data, strpos($part1_data, "\n\n") + 2)); + + $part2 = new Horde_Mime_Part(); + + $part2->setType('application/pgp-signature'); + $part2->setContents(implode("\n", $val['data']) . "\n" . implode("\n", $sig['data'])); + // A true pgp-signature part would only contain the + // detached signature. However, we need to carry around + // the entire armored text to verify correctly. Use a + // IMP-specific content-type parameter to clue the PGP + // driver into this fact. + $part2->setMetadata(IMP_Mime_Viewer_Pgp::PGP_SIG, true); + $part2->setMetadata(IMP_Mime_Viewer_Pgp::PGP_CHARSET, $charset); + + $part->addPart($part1); + $part->addPart($part2); + $new_part->addPart($part); + + next($parts); + } + } + } + + return $new_part; + } + } diff --git a/imp/lib/Mime/Viewer/Plain.php b/imp/lib/Mime/Viewer/Plain.php index b7831c835fe..3ca3b175cb3 100644 --- a/imp/lib/Mime/Viewer/Plain.php +++ b/imp/lib/Mime/Viewer/Plain.php @@ -244,99 +244,18 @@ protected function _getEmbeddedMimeParts() */ protected function _parsePGP() { - $parts = $GLOBALS['injector']->getInstance('IMP_Crypt_Pgp')->parsePGPData( + $part = $GLOBALS['injector']->getInstance('IMP_Crypt_Pgp')->armoredTextToPart( new Horde_Stream_Existing(array( 'stream' => $this->_mimepart->getContents(array('stream' => true)) - )) + )), + $this->_mimepart->getCharset() ); - if (empty($parts) || - ((count($parts) == 1) && - ($parts[0]['type'] == Horde_Crypt_Pgp::ARMOR_TEXT))) { - return null; - } - - $new_part = new Horde_Mime_Part(); - $new_part->setType('multipart/mixed'); - $charset = $this->_mimepart->getCharset(); - $mime_id = $this->_mimepart->getMimeId(); - - while (list(,$val) = each($parts)) { - switch ($val['type']) { - case Horde_Crypt_Pgp::ARMOR_TEXT: - $part = new Horde_Mime_Part(); - $part->setType('text/plain'); - $part->setCharset($charset); - $part->setContents(implode("\n", $val['data'])); - $new_part->addPart($part); - break; - - case Horde_Crypt_Pgp::ARMOR_PUBLIC_KEY: - $part = new Horde_Mime_Part(); - $part->setType('application/pgp-keys'); - $part->setContents(implode("\n", $val['data'])); - $new_part->addPart($part); - break; - - case Horde_Crypt_Pgp::ARMOR_MESSAGE: - $part = new Horde_Mime_Part(); - $part->setType('multipart/encrypted'); - $part->setMetadata(IMP_Mime_Viewer_Pgp::PGP_ARMOR, true); - $part->setContentTypeParameter('protocol', 'application/pgp-encrypted'); - - $part1 = new Horde_Mime_Part(); - $part1->setType('application/pgp-encrypted'); - $part1->setContents("Version: 1\n"); - - $part2 = new Horde_Mime_Part(); - $part2->setType('application/octet-stream'); - $part2->setContents(implode("\n", $val['data'])); - $part2->setDisposition('inline'); - - $part->addPart($part1); - $part->addPart($part2); - - $new_part->addPart($part); - break; - - case Horde_Crypt_Pgp::ARMOR_SIGNED_MESSAGE: - if (($sig = current($parts)) && - ($sig['type'] == Horde_Crypt_Pgp::ARMOR_SIGNATURE)) { - $part = new Horde_Mime_Part(); - $part->setType('multipart/signed'); - // TODO: add micalg parameter - $part->setContentTypeParameter('protocol', 'application/pgp-signature'); - - $part1 = new Horde_Mime_Part(); - $part1->setType('text/plain'); - $part1->setCharset($charset); - - $part1_data = implode("\n", $val['data']); - $part1->setContents(substr($part1_data, strpos($part1_data, "\n\n") + 2)); - - $part2 = new Horde_Mime_Part(); - $part2->setType('application/pgp-signature'); - $part2->setContents(implode("\n", $val['data']) . "\n" . implode("\n", $sig['data'])); - // A true pgp-signature part would only contain the - // detached signature. However, we need to carry around - // the entire armored text to verify correctly. Use a - // IMP-specific content-type parameter to clue the PGP - // driver into this fact. - $part2->setMetadata(IMP_Mime_Viewer_Pgp::PGP_SIG, true); - $part2->setMetadata(IMP_Mime_Viewer_Pgp::PGP_CHARSET, $charset); - - $part->addPart($part1); - $part->addPart($part2); - $new_part->addPart($part); - - next($parts); - } - } + if (!is_null($part)) { + self::$_cache[$this->_mimepart->getMimeId()] = true; } - self::$_cache[$mime_id] = true; - - return $new_part; + return $part; } /** diff --git a/imp/package.xml b/imp/package.xml index 22bbbaa917d..9b4cd3c3e62 100644 --- a/imp/package.xml +++ b/imp/package.xml @@ -33,6 +33,7 @@ GPL-2.0 +* [mms] Strip PGP armor text when replying to a message. * [mms] Move determination whether to scan plaintext messages for PGP data from preferences to MIME viewer configuration. * [mms] Add copy email option to the address context menu in dynamic view. * [mms] Support uploading multiple attachments at once in dynamic view.