aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--src/m_email/EMailModule.cpp197
1 files changed, 182 insertions, 15 deletions
diff --git a/src/m_email/EMailModule.cpp b/src/m_email/EMailModule.cpp
index f2747c7..582542a 100644
--- a/src/m_email/EMailModule.cpp
+++ b/src/m_email/EMailModule.cpp
@@ -61,6 +61,7 @@ auto GFRegisterModule() -> int {
MLogDebug("email module registering...");
LISTEN("EMAIL_VERIFY_EML_DATA");
+ LISTEN("EMAIL_DECRYPT_EML_DATA");
return 0;
}
@@ -78,7 +79,7 @@ REGISTER_EVENT_HANDLER(EMAIL_VERIFY_EML_DATA, [](const MEvent& event) -> int {
message->parse(vmime_data);
} catch (const vmime::exception& e) {
FLOG_DEBUG("error when parsing vmime data: %1", e.what());
- CB_ERR(event, -2, "error when parsing vmime data");
+ CB_ERR(event, -2, "Error when parsing eml raw data");
}
auto header = message->getHeader();
@@ -86,7 +87,7 @@ REGISTER_EVENT_HANDLER(EMAIL_VERIFY_EML_DATA, [](const MEvent& event) -> int {
auto content_type_field =
header->getField<vmime::contentTypeField>(vmime::fields::CONTENT_TYPE);
if (!content_type_field) {
- CB_ERR(event, -2, "cannot get 'Content-Type' Field from header");
+ CB_ERR(event, -2, "Cannot get 'Content-Type' Field from header");
}
auto content_type_value =
@@ -94,7 +95,7 @@ REGISTER_EVENT_HANDLER(EMAIL_VERIFY_EML_DATA, [](const MEvent& event) -> int {
auto prm_protocol = content_type_field->getParameter("protocol");
if (!prm_protocol) {
- CB_ERR(event, -2, "cannot get 'protocol' from 'Content-Type'");
+ CB_ERR(event, -2, "Cannot get 'protocol' from 'Content-Type'");
}
/*
@@ -113,7 +114,7 @@ REGISTER_EVENT_HANDLER(EMAIL_VERIFY_EML_DATA, [](const MEvent& event) -> int {
auto prm_protocol_value = Q_SC(prm_protocol->getValue().generate());
if (prm_protocol_value != "application/pgp-signature")
CB_ERR(event, -2,
- "'protocol' parameter which MUST have a value of "
+ "The 'protocol' parameter which MUST have a value of "
"'application/pgp-signature' (MUST be quoted)");
auto prm_micalg = content_type_field->getParameter("micalg");
@@ -131,8 +132,8 @@ REGISTER_EVENT_HANDLER(EMAIL_VERIFY_EML_DATA, [](const MEvent& event) -> int {
FLOG_DEBUG("micalg value: %1", prm_micalg_value);
if (!IsValidMicalgFormat(prm_micalg_value)) {
CB_ERR(event, -2,
- "'micalg' MUST contain exactly one hash-symbol of the format "
- "'pgp-<hash-identifier>'");
+ "The 'micalg' parameter MUST contain exactly one hash-symbol of the "
+ "format 'pgp-<hash-identifier>'");
}
auto from_field_value_text =
@@ -176,21 +177,39 @@ REGISTER_EVENT_HANDLER(EMAIL_VERIFY_EML_DATA, [](const MEvent& event) -> int {
auto part_mime_content_text = QByteArray::fromStdString(
vmime_data.substr(part_mime_parse_offset, part_mime_parse_length));
- FLOG_DEBUG("body part of raw offset: %1, length: %2", part_mime_parse_offset,
- part_mime_parse_length);
- FLOG_DEBUG("body part of raw content left: %1",
- part_mime_content_text.left(64));
- FLOG_DEBUG("body part of raw content right: %1",
- part_mime_content_text.right(64));
+ FLOG_DEBUG("mime part info, raw offset: %1, length: %2",
+ part_mime_parse_offset, part_mime_parse_length);
auto part_mime_content_hash = QCryptographicHash::hash(
part_mime_content_text, QCryptographicHash::Sha1);
- FLOG_DEBUG("body part of raw content hash: %1",
+ FLOG_DEBUG("mime part of raw content hash: %1",
part_mime_content_hash.toHex());
if (part_mime_content_text.isEmpty())
CB_ERR(event, -2, "mime raw data part is empty");
+ auto attachments =
+ vmime::attachmentHelper::findAttachmentsInBodyPart(part_mime);
+ FLOG_DEBUG("mime part info, attachment count: %1", attachments.size());
+
+ QStringList public_keys_buffer;
+
+ for (const auto& att : attachments) {
+ auto att_type = Q_SC(att->getType().generate()).trimmed();
+ FLOG_DEBUG("mime part info, attachment type: %1", att_type);
+
+ if (att_type != "application/pgp-keys") continue;
+
+ std::ostringstream oss;
+ vmime::utility::outputStreamAdapter osa(oss);
+ att->getData()->extract(osa);
+
+ public_keys_buffer.append(Q_SC(oss.str()));
+ }
+
+ FLOG_DEBUG("mime part info, attached public keys: ",
+ public_keys_buffer.join("\n"));
+
/*
* The second body MUST contain the OpenPGP digital signature. It MUST
* be labeled with a content type of "application/pgp-signature"
@@ -209,7 +228,7 @@ REGISTER_EVENT_HANDLER(EMAIL_VERIFY_EML_DATA, [](const MEvent& event) -> int {
auto part_sign_body_content =
QByteArray::fromStdString(part_sign->getBody()->generate());
if (part_sign_body_content.trimmed().isEmpty())
- CB_ERR(event, -2, "signature part is empty");
+ CB_ERR(event, -2, "The signature part is empty");
FLOG_DEBUG("body part of signature content: %1", part_sign_body_content);
@@ -218,7 +237,7 @@ REGISTER_EVENT_HANDLER(EMAIL_VERIFY_EML_DATA, [](const MEvent& event) -> int {
{
{"ret", QString::number(0)},
{"mime", QString::fromLatin1(part_mime_content_text.toBase64())},
- {"mime_hash", part_mime_content_hash},
+ {"mime_hash", part_mime_content_hash.toHex()},
{"signature", QString::fromLatin1(part_sign_body_content.toBase64())},
{"from", from_field_value_text},
{"to", to_field_value_text},
@@ -227,6 +246,154 @@ REGISTER_EVENT_HANDLER(EMAIL_VERIFY_EML_DATA, [](const MEvent& event) -> int {
{"subject", subject_field_value_text},
{"datetime", QString::number(date_field_value.toMSecsSinceEpoch())},
{"micalg", prm_micalg_value},
+ {"public_keys", public_keys_buffer.join("\n")},
+ });
+
+ return 0;
+});
+
+REGISTER_EVENT_HANDLER(EMAIL_DECRYPT_EML_DATA, [](const MEvent& event) -> int {
+ if (event["eml_data"].isEmpty()) CB_ERR(event, -1, "eml_data is empty");
+
+ auto data = QByteArray::fromBase64(QString(event["eml_data"]).toLatin1());
+ vmime::string vmime_data(data.constData(), data.size());
+
+ auto message = vmime::make_shared<vmime::message>();
+ try {
+ message->parse(vmime_data);
+ } catch (const vmime::exception& e) {
+ FLOG_DEBUG("error when parsing vmime data: %1", e.what());
+ CB_ERR(event, -2, "error when parsing vmime data");
+ }
+
+ auto header = message->getHeader();
+
+ auto content_type_field =
+ header->getField<vmime::contentTypeField>(vmime::fields::CONTENT_TYPE);
+ if (!content_type_field) {
+ CB_ERR(event, -2, "cannot get 'Content-Type' Field from header");
+ }
+
+ auto content_type_value =
+ Q_SC(content_type_field->getValue()->generate()).trimmed();
+
+ auto prm_protocol = content_type_field->getParameter("protocol");
+ if (!prm_protocol) {
+ CB_ERR(event, -2, "cannot get 'protocol' from 'Content-Type'");
+ }
+
+ /*
+ * OpenPGP encrypted data is denoted by the "multipart/encrypted"
+ * content type
+ */
+ if (content_type_value != "multipart/encrypted")
+ CB_ERR(event, -2,
+ "OpenPGP encrypted data is denoted by the 'multipart/encrypted' "
+ "content type");
+
+ /*
+ * MUST have a "protocol" parameter value of "application/pgp-encrypted"
+ */
+ auto prm_protocol_value = Q_SC(prm_protocol->getValue().generate());
+ if (prm_protocol_value != "application/pgp-encrypted")
+ CB_ERR(event, -2,
+ "'protocol' parameter which MUST have a value of "
+ "'application/pgp-encrypted' (MUST be quoted)");
+
+ auto from_field_value_text =
+ ExtractFieldValueMailBox(header, vmime::fields::FROM);
+ auto to_field_value_text =
+ ExtractFieldValueAddressList(header, vmime::fields::TO);
+ auto cc_field_value_text =
+ ExtractFieldValueAddressList(header, vmime::fields::CC);
+ auto bcc_field_value_text =
+ ExtractFieldValueAddressList(header, vmime::fields::BCC);
+ auto date_field_value =
+ ExtractFieldValueDateTime(header, vmime::fields::DATE);
+ auto subject_field_value_text =
+ ExtractFieldValueText(header, vmime::fields::SUBJECT);
+ auto reply_to_field_value_text =
+ ExtractFieldValueMailBox(header, vmime::fields::REPLY_TO);
+ auto organization_text =
+ ExtractFieldValueText(header, vmime::fields::ORGANIZATION);
+
+ auto body = message->getBody();
+ auto content_type = body->getContentType();
+ auto part_count = body->getPartCount();
+
+ FLOG_DEBUG("body page count: %1", part_count);
+
+ /*
+ * The multipart/encrypted body MUST consist of exactly two parts.
+ */
+ if (part_count != 2)
+ CB_ERR(event, -2,
+ "The multipart/signed body MUST consist of exactly two parts");
+
+ /*
+ * The multipart/encrypted MIME body MUST consist of exactly two body
+ * parts, the first with content type "application/pgp-encrypted". This
+ * body contains the control information.
+ */
+ auto part_mime = body->getPartAt(0);
+
+ std::ostringstream oss;
+ vmime::utility::outputStreamAdapter osa(oss);
+
+ auto part_mime_body = part_mime->getBody();
+ auto part_mime_body_content = part_mime_body->getContents();
+ if (!part_mime_body_content) {
+ CB_ERR(event, -2, "Cannot get the content of the first part's body");
+ }
+
+ part_mime_body_content->extractRaw(osa);
+ osa.flush();
+
+ auto part_mime_body_content_text = Q_SC(oss.str());
+ FLOG_DEBUG("body part of raw content text: %1", part_mime_body_content_text);
+
+ /*
+ * A message complying with this
+ * standard MUST contain a "Version: 1" field in this body.
+ */
+ if (!part_mime_body_content_text.contains("Version: 1")) {
+ CB_ERR(event, -2,
+ "The first part MUST contain a 'Version: 1' field in this body.");
+ }
+
+ /*
+ * The second MIME body part MUST contain the actual encrypted data. It
+ * MUST be labeled with a content type of "application/octet-stream".
+ */
+ auto part_sign = body->getPartAt(1);
+ auto part_sign_header = part_sign->getHeader();
+ auto part_sign_content_type = part_sign_header->ContentType();
+ auto part_sign_content_type_value =
+ Q_SC(part_sign_content_type->getValue()->generate());
+
+ if (part_sign_content_type_value != "application/octet-stream")
+ CB_ERR(event, -2,
+ "The second part MUST be labeled with a content type of "
+ "'application/octet-stream'");
+
+ auto part_encr_body_content =
+ QByteArray::fromStdString(part_sign->getBody()->generate());
+ if (part_encr_body_content.trimmed().isEmpty())
+ CB_ERR(event, -2, "The second part is empty");
+
+ FLOG_DEBUG("body part of encrypt content: %1", part_encr_body_content);
+
+ // callback
+ CB(event, GFGetModuleID(),
+ {
+ {"ret", QString::number(0)},
+ {"encrypted", QString::fromLatin1(part_encr_body_content.toBase64())},
+ {"from", from_field_value_text},
+ {"to", to_field_value_text},
+ {"cc", cc_field_value_text},
+ {"bcc", bcc_field_value_text},
+ {"subject", subject_field_value_text},
+ {"datetime", QString::number(date_field_value.toMSecsSinceEpoch())},
});
return 0;