From 2de49dbeb5cb6c8ab0c6b1e10e8f8d33089868eb Mon Sep 17 00:00:00 2001 From: Daniel Wendt Date: Fri, 5 Jul 2024 10:06:30 +0200 Subject: [PATCH 01/25] MimePart: fix nullpointer exception in copy constructor --- src/mimepart.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/mimepart.cpp b/src/mimepart.cpp index 71a732c..f1faadf 100644 --- a/src/mimepart.cpp +++ b/src/mimepart.cpp @@ -32,6 +32,7 @@ MimePart::MimePart() } MimePart::MimePart(const MimePart &other) + : d_ptr(new MimePartPrivate) { Q_D(MimePart); d->contentCharset = other.charset(); From 2a6d2de715e05d9c10552aae0a22eabfbd6b9d28 Mon Sep 17 00:00:00 2001 From: Daniel Wendt Date: Thu, 11 Jul 2024 08:14:42 +0200 Subject: [PATCH 02/25] add flag for already Base64 encoded content --- src/mimepart.cpp | 24 ++++++++++++++++++++++-- src/mimepart.h | 4 ++++ src/mimepart_p.h | 1 + 3 files changed, 27 insertions(+), 2 deletions(-) diff --git a/src/mimepart.cpp b/src/mimepart.cpp index f1faadf..f891563 100644 --- a/src/mimepart.cpp +++ b/src/mimepart.cpp @@ -46,6 +46,7 @@ MimePart::MimePart(const MimePart &other) d->contentType = other.contentType(); d->contentEncoding = other.encoding(); d->header = other.header(); + d->contentIsBase64 = other.contentIsBase64(); } MimePart::~MimePart() @@ -66,6 +67,7 @@ MimePart &MimePart::operator=(const MimePart &other) d->contentType = other.contentType(); d->contentEncoding = other.encoding(); d->header = other.header(); + d->contentIsBase64 = other.contentIsBase64(); return *this; } @@ -275,6 +277,18 @@ MimePart::MimePart(MimePartPrivate *d) { } +void MimePart::setContentIsBase64(bool isBase64) +{ + Q_D(MimePart); + d->contentIsBase64 = isBase64; +} + +bool MimePart::contentIsBase64() const +{ + Q_D(const MimePart); + return d->contentIsBase64; +} + bool MimePart::writeData(QIODevice *device) { Q_D(MimePart); @@ -297,8 +311,14 @@ bool MimePart::writeData(QIODevice *device) } break; case MimePart::Base64: - if (!d->writeBase64(input, device)) { - return false; + if (!d->contentIsBase64) { + if (!d->writeBase64(input, device)) { + return false; + } + } else { + if (!d->writeRaw(input, device)) { + return false; + } } break; case MimePart::QuotedPrintable: diff --git a/src/mimepart.h b/src/mimepart.h index 8d8c8b8..a8c1b02 100644 --- a/src/mimepart.h +++ b/src/mimepart.h @@ -67,6 +67,10 @@ class SMTP_EXPORT MimePart bool write(QIODevice *device); + void setContentIsBase64(bool isBase64); + + bool contentIsBase64() const; + protected: MimePart(MimePartPrivate *d); virtual bool writeData(QIODevice *device); diff --git a/src/mimepart_p.h b/src/mimepart_p.h index fea6a0b..e9eb723 100644 --- a/src/mimepart_p.h +++ b/src/mimepart_p.h @@ -44,6 +44,7 @@ class MimePartPrivate : public QSharedData QByteArray contentType; QByteArray contentCharset; QByteArray contentBoundary; + bool contentIsBase64 = false; MimeContentFormatter formatter; MimePart::Encoding contentEncoding = MimePart::_7Bit; From 81eff589391b3b1ddad18adf28e46a8b7710d24b Mon Sep 17 00:00:00 2001 From: Daniel Wendt Date: Fri, 12 Jul 2024 10:45:45 +0200 Subject: [PATCH 03/25] MimeMessage: return content in a shared pointer --- src/mimemessage.cpp | 4 ++-- src/mimemessage.h | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/mimemessage.cpp b/src/mimemessage.cpp index a9579b3..ffc0eab 100644 --- a/src/mimemessage.cpp +++ b/src/mimemessage.cpp @@ -55,9 +55,9 @@ MimeMessage &MimeMessage::operator=(const MimeMessage &other) return *this; } -MimePart &MimeMessage::getContent() +std::shared_ptr MimeMessage::getContent() { - return *d->content; + return d->content; } void MimeMessage::setContent(const std::shared_ptr &content) diff --git a/src/mimemessage.h b/src/mimemessage.h index c0b79f3..1bf89da 100644 --- a/src/mimemessage.h +++ b/src/mimemessage.h @@ -66,7 +66,7 @@ class SMTP_EXPORT MimeMessage QString subject() const; QList> parts() const; - MimePart &getContent(); + std::shared_ptr getContent(); void setContent(const std::shared_ptr &content); bool write(QIODevice *device) const; From 46a5eb6c957699b040faf0fa4958b5f814f723a2 Mon Sep 17 00:00:00 2001 From: Daniel Wendt Date: Tue, 16 Jul 2024 08:50:24 +0200 Subject: [PATCH 04/25] add class smime for signing and/or encrypting emails --- src/smime.cpp | 355 ++++++++++++++++++++++++++++++++++++++++++++++++++ src/smime.h | 60 +++++++++ 2 files changed, 415 insertions(+) create mode 100644 src/smime.cpp create mode 100644 src/smime.h diff --git a/src/smime.cpp b/src/smime.cpp new file mode 100644 index 0000000..36b8d2e --- /dev/null +++ b/src/smime.cpp @@ -0,0 +1,355 @@ +#include "smime.h" + +#include "mimemultipart.h" +#include "mimepart_p.h" + +#include +#include +#include +#include + +#include +#include +#include + +Q_LOGGING_CATEGORY(SIMPLEMAIL_SMIME, "simplemail.smime", QtInfoMsg) + +using namespace SimpleMail; + +SMime::SMime(MimeMessage *message) + : _privateKey(nullptr) + , _certificate(nullptr) + , _certificateCA(nullptr) + , _recipsReceiver(nullptr) + , _input(nullptr) +{ + initOpenSSL(); + _mimeMessage = message; + writeMimeMessageBuffer(); +} + +SMime::~SMime() +{ + BIO_free(_input); + EVP_PKEY_free(_privateKey); + X509_free(_certificate); + sk_X509_pop_free(_certificateCA, X509_free); + sk_X509_pop_free(_recipsReceiver, X509_free); + _mimeMessage = nullptr; +} + +void SMime::setKeyFile(const QString &filename, const QString &password) +{ + _keyfile = filename; + _password = password; + loadPKCS12PrivateKey(); +} + +void SMime::setPublicKey(const QString &filename) +{ + _publicKeyfile = filename; + loadPKCS12PublicKey(); +} + +bool SMime::sign() +{ + bool ret = false; + PKCS7 *p7 = nullptr; + + if (!_certificate || !_privateKey) { + qCDebug(SIMPLEMAIL_SMIME) << "no certificate or private key"; + return ret; + } + + wrapMimeMultiPart(); + setSignedHeader(); + + int flags = PKCS7_DETACHED | PKCS7_STREAM | PKCS7_BINARY; + + BIO *out = NULL; + + if (!writeInputBuffer()) + goto err; + + /* Sign content */ + p7 = PKCS7_sign(_certificate, _privateKey, NULL, _input, flags); + if (!p7) + goto err; + + out = BIO_new(BIO_s_mem()); + if (!out) + goto err; + + /* Write out S/MIME message */ + if (!SMIME_write_PKCS7(out, + p7, + _input, + flags | + PKCS7_CRLFEOL)) // needed for intializing/finalizing SMIME structure + goto err; + + if (!handleData(p7, nullptr, 0)) + goto err; + + _mimeMessage->addPart(std::shared_ptr(this)); + + ret = true; +err: + if (!ret) { + qCDebug(SIMPLEMAIL_SMIME) << "Error Signing Data"; + } + PKCS7_free(p7); + BIO_free(out); + return ret; +} + +bool SMime::encrypt() +{ + bool ret = false; + PKCS7 *p7 = nullptr; + + if (!_recipsReceiver) { + qCDebug(SIMPLEMAIL_SMIME) << "no public key"; + return ret; + } + + setEncryptionHeader(); + + int flags = PKCS7_STREAM; + + if (!writeInputBuffer()) + goto err; + + /* encrypt content */ + p7 = PKCS7_encrypt(_recipsReceiver, _input, EVP_des_ede3_cbc(), flags); + + if (!p7) + goto err; + + if (!handleData(p7, _input, flags)) + goto err; + + _mimeMessage->setContent(std::shared_ptr(this)); + + ret = true; +err: + if (!ret) { + qCDebug(SIMPLEMAIL_SMIME) << "Error Encrypting Data"; + } + PKCS7_free(p7); + return ret; +} + +bool SMime::signAndEncrypt() +{ + bool ret = false; + PKCS7 *p7 = nullptr; + BIO *signedContent = nullptr; + if (!_certificate || !_privateKey) { + qCDebug(SIMPLEMAIL_SMIME) << "no certificate or private key"; + return ret; + } + if (!_recipsReceiver) { + qCDebug(SIMPLEMAIL_SMIME) << "no public key"; + return ret; + } + + setEncryptionHeader(); + + int flags = PKCS7_STREAM; + + if (!writeInputBuffer()) + goto err; + + /* Sign content */ + p7 = PKCS7_sign(_certificate, _privateKey, NULL, _input, flags); + if (!p7) + goto err; + + signedContent = BIO_new(BIO_s_mem()); + if (!SMIME_write_PKCS7(signedContent, p7, _input, flags | PKCS7_CRLFEOL)) + goto err; + + PKCS7_free(p7); + p7 = NULL; + p7 = PKCS7_encrypt(_recipsReceiver, signedContent, EVP_des_ede3_cbc(), flags); + + if (!p7) + goto err; + + if (!handleData(p7, signedContent, flags)) + goto err; + + _mimeMessage->setContent(std::shared_ptr(this)); + + ret = true; +err: + if (!ret) { + qCDebug(SIMPLEMAIL_SMIME) << "Error Signing/Encrypting Data"; + } + PKCS7_free(p7); + BIO_free(signedContent); + return ret; +} + +void SMime::setSignedHeader() +{ + Q_D(MimePart); + d->contentType = QByteArrayLiteral("application/pkcs7-signature; name=\"smime.p7s\""); + d->contentCharset.clear(); + d->contentEncoding = Base64; + d->contentIsBase64 = true; + + d->header.append("Content-Disposition: attachment; filename=\"smime.p7s\"\r\n"); +} + +void SMime::setEncryptionHeader() +{ + Q_D(MimePart); + d->contentType = QByteArrayLiteral( + "application/x-pkcs7-mime; smime-type=enveloped-data; name=\"smime.p7m\""); + d->contentCharset.clear(); + d->contentEncoding = Base64; + d->contentIsBase64 = true; + + d->header.append("Content-Disposition: attachment; filename=\"smime.p7m\"\r\n"); +} + +void SMime::loadPKCS12PrivateKey() +{ + QFile file(QString::fromLatin1(_keyfile.toStdString().c_str())); + if (!file.exists()) + return; + file.open(QFile::ReadOnly); + QByteArray buffer = file.readAll(); + file.close(); + + BIO *keyBuffer = BIO_new(BIO_s_mem()); + if (keyBuffer == NULL) { + qCDebug(SIMPLEMAIL_SMIME) << "Error opening file " << _keyfile; + return; + } + + BIO_write(keyBuffer, (void *) buffer.data(), buffer.length()); + PKCS12 *p12 = d2i_PKCS12_bio(keyBuffer, NULL); + BIO_free(keyBuffer); + + if (p12 == NULL) { + qCDebug(SIMPLEMAIL_SMIME) << "Error reading PKCS#12 file"; + } + + if (!PKCS12_parse( + p12, _password.toStdString().c_str(), &_privateKey, &_certificate, &_certificateCA)) { + OSSL_PROVIDER_try_load(nullptr, "legacy", 1); + + if (!PKCS12_parse(p12, + _password.toStdString().c_str(), + &_privateKey, + &_certificate, + &_certificateCA)) { + qCDebug(SIMPLEMAIL_SMIME) << "Error parsing PKCS#12 file"; + } + } + + PKCS12_free(p12); +} + +void SMime::loadPKCS12PublicKey() +{ + QFile file(QString::fromLatin1(_publicKeyfile.toStdString().c_str())); + if (!file.exists()) + return; + file.open(QFile::ReadOnly); + QByteArray buffer = file.readAll(); + file.close(); + + BIO *keyBuffer = BIO_new(BIO_s_mem()); + if (keyBuffer == NULL) { + qCDebug(SIMPLEMAIL_SMIME) << "Error opening file " << _publicKeyfile; + return; + } + BIO_write(keyBuffer, (void *) buffer.data(), buffer.length()); + + X509 *publicrcert = PEM_read_bio_X509(keyBuffer, NULL, 0, NULL); + BIO_free(keyBuffer); + + _recipsReceiver = sk_X509_new_null(); + + if (!_recipsReceiver || !sk_X509_push(_recipsReceiver, publicrcert)) { + return; + } + publicrcert = nullptr; +} + +void SMime::initOpenSSL() +{ + OpenSSL_add_all_algorithms(); + ERR_load_crypto_strings(); +} + +void SMime::wrapMimeMultiPart() +{ + auto &mimeMultiPart = *_mimeMessage->getContent(); + + if (typeid(mimeMultiPart) == typeid(MimeMultiPart)) { + MimeMultiPart *multiPartSigned = new MimeMultiPart(MimeMultiPart::Signed); + multiPartSigned->addPart(_mimeMessage->getContent()); + _mimeMessage->setContent(std::shared_ptr(multiPartSigned)); + } +} + +bool SMime::writeInputBuffer() +{ + _input = BIO_new(BIO_s_mem()); + if (!_input) + return false; + if (!BIO_write(_input, (void *) _message.data(), _message.length())) + return false; + return true; +} + +bool SMime::writeMimeMessageBuffer() +{ + QBuffer *buffer = new QBuffer; + buffer->open(QBuffer::ReadWrite); + + _mimeMessage->getContent()->write(buffer); + buffer->close(); + + _message = buffer->data(); + delete buffer; + + return true; +} + +bool SMime::handleData(PKCS7 *p7, BIO *dataIn, int flags) +{ + QByteArray contentBuffer; + + // Buffer for signed and/or encrypted data; + BIO *encryptedData = BIO_new(BIO_s_mem()); + if (!encryptedData) + return false; + + BIO *b64 = BIO_new(BIO_f_base64()); + BIO *tmp = BIO_push(b64, encryptedData); + + i2d_ASN1_bio_stream(tmp, (ASN1_VALUE *) p7, dataIn, flags, ASN1_ITEM_rptr(PKCS7)); + + BIO_flush(tmp); + BIO_pop(tmp); + BIO_free(b64); + + char buffer[1024]; + while (BIO_get_line(encryptedData, &buffer[0], sizeof(buffer))) { + qCDebug(SIMPLEMAIL_SMIME) << QString::fromLatin1(buffer); + if (strncmp(&buffer[0], "-----", strlen("-----")) != 0) { + contentBuffer += QByteArray(&buffer[0], strlen(buffer) - 1); + contentBuffer += QByteArray("\r\n"); + } + } + + setContent(contentBuffer); + BIO_free(encryptedData); + return true; +} diff --git a/src/smime.h b/src/smime.h new file mode 100644 index 0000000..f391273 --- /dev/null +++ b/src/smime.h @@ -0,0 +1,60 @@ +#ifndef SMIME_H +#define SMIME_H + +#include "mimemessage.h" +#include "mimepart.h" + +#include + +#include + +namespace SimpleMail { +class SMTP_EXPORT SMime : public MimePart +{ +public: + SMime(MimeMessage *message); + virtual ~SMime(); + + void setKeyFile(const QString &filename, const QString &password); + + void setPublicKey(const QString &filename); + + bool sign(); + bool encrypt(); + bool signAndEncrypt(); + +private: + void setSignedHeader(); + void setEncryptionHeader(); + + void loadPKCS12PrivateKey(); + void loadPKCS12PublicKey(); + + void initOpenSSL(); + + void wrapMimeMultiPart(); + bool writeInputBuffer(); + bool writeMimeMessageBuffer(); + bool handleData(PKCS7 *p7, BIO *dataIn, int flags); + + MimeMessage *_mimeMessage; + + QByteArray _message; + QString _keyfile; + QString _publicKeyfile; + QString _password; + + // private key for signing + EVP_PKEY *_privateKey; + + // certificate from private Key + X509 *_certificate; + STACK_OF(X509) * _certificateCA; + // certificate from public key, used for encrypting + STACK_OF(X509) * _recipsReceiver; + // buffer to be signed/encrypted + BIO *_input; +}; +} // namespace SimpleMail + +#endif // SMIME_H From a5d933e135c75f0ec92e2ddcc97133958ec30ed8 Mon Sep 17 00:00:00 2001 From: Daniel Wendt Date: Mon, 1 Jul 2024 15:45:16 +0200 Subject: [PATCH 05/25] extends the header for signed emails --- src/mimepart.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/mimepart.cpp b/src/mimepart.cpp index f891563..dc29d09 100644 --- a/src/mimepart.cpp +++ b/src/mimepart.cpp @@ -231,6 +231,10 @@ bool MimePart::write(QIODevice *device) headers.append("; name=\"?UTF-8?B?" + d->contentName.toBase64(QByteArray::Base64Encoding) + "?=\""); } + if (headers.contains("signed")) { + headers.append("; protocol=\"application/pkcs7-signature\";"); + headers.append("; micalg=sha1"); + } if (!d->contentCharset.isEmpty()) { headers.append("; charset=" + d->contentCharset); } From 07728744d5da16777db2b0c96401afb0079013ff Mon Sep 17 00:00:00 2001 From: Daniel Wendt Date: Mon, 1 Jul 2024 15:51:54 +0200 Subject: [PATCH 06/25] add missing newline for signed/encrypted mails --- src/mimemultipart.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/mimemultipart.cpp b/src/mimemultipart.cpp index 78efe3f..c4df53a 100644 --- a/src/mimemultipart.cpp +++ b/src/mimemultipart.cpp @@ -69,6 +69,7 @@ bool MimeMultiPart::writeData(QIODevice *device) if (!part->write(device)) { return false; } + device->write("\r\n"); } device->write("--" + d->contentBoundary + "--\r\n"); From 04eb9eb8c5a5bf7d344e5f66f0c34e60f430955b Mon Sep 17 00:00:00 2001 From: Daniel Wendt Date: Thu, 11 Jul 2024 08:20:27 +0200 Subject: [PATCH 07/25] cmake: add option for building lib with SMIME support --- CMakeLists.txt | 5 +++++ src/CMakeLists.txt | 16 ++++++++++++++++ 2 files changed, 21 insertions(+) diff --git a/CMakeLists.txt b/CMakeLists.txt index 36ccf42..81b5916 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -21,6 +21,7 @@ set(CMAKE_MODULE_PATH ${CMAKE_CURRENT_SOURCE_DIR}/cmake/modules) set(CMAKE_INCLUDE_CURRENT_DIR ON) option(BUILD_SHARED_LIBS "Build in shared lib mode" ON) +option(BUILD_WITH_SMIME "Build with SMIME Support" OFF) set(CMAKE_CXX_STANDARD 14) set(CMAKE_CXX_STANDARD_REQUIRED ON) @@ -78,6 +79,10 @@ install(EXPORT SimpleMailTargets COMPONENT Devel ) +if(BUILD_WITH_SMIME) + find_package(OpenSSL REQUIRED) +endif() + add_subdirectory(src) if (BUILD_DEMOS) add_subdirectory(demos) diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 7ce6535..953c197 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -43,6 +43,21 @@ set(simplemailqt_HEADERS_PRIVATE # common.h ) +if(BUILD_WITH_SMIME) + list(APPEND simplemailqt_SRC + smime.cpp + ) + list(APPEND simplemailqt_HEADERS + smime.h + ) + set(OPENSSL_LIBS + OpenSSL::SSL + OpenSSL::Crypto + ) +else() + set(OPENSSL_LIBS) +endif() + add_library(SimpleMail${PROJECT_VERSION_MAJOR}Qt${QT_VERSION_MAJOR} ${simplemailqt_SRC} ${simplemailqt_HEADERS} @@ -93,6 +108,7 @@ target_link_libraries(SimpleMail${PROJECT_VERSION_MAJOR}Qt${QT_VERSION_MAJOR} PUBLIC Qt::Core Qt::Network + ${OPENSSL_LIBS} ) set_property(TARGET SimpleMail${PROJECT_VERSION_MAJOR}Qt${QT_VERSION_MAJOR} PROPERTY PUBLIC_HEADER ${simplemailqt_HEADERS}) From 3c36435957039aaacaea5c78e539267d9ef1bef7 Mon Sep 17 00:00:00 2001 From: Daniel Wendt Date: Thu, 11 Jul 2024 08:21:01 +0200 Subject: [PATCH 08/25] add smime header when building with OpenSSL support --- src/CMakeLists.txt | 1 + src/SimpleMail | 3 +++ 2 files changed, 4 insertions(+) diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 953c197..0e0cf68 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -44,6 +44,7 @@ set(simplemailqt_HEADERS_PRIVATE ) if(BUILD_WITH_SMIME) + add_definitions(-DWITH_SMIME) list(APPEND simplemailqt_SRC smime.cpp ) diff --git a/src/SimpleMail b/src/SimpleMail index 0d2526f..efcaf39 100644 --- a/src/SimpleMail +++ b/src/SimpleMail @@ -26,3 +26,6 @@ #include "mimefile.h" #include "server.h" #include "serverreply.h" +#ifdef WITH_SMIME +#include "smime.h" +#endif From 328a551517c611699ecb2234c8193092a1144617 Mon Sep 17 00:00:00 2001 From: Daniel Wendt Date: Wed, 3 Jul 2024 16:08:01 +0200 Subject: [PATCH 09/25] add smime demo --- demos/CMakeLists.txt | 3 ++ demos/demo5/CMakeLists.txt | 20 ++++++++ demos/demo5/demo5.cpp | 95 ++++++++++++++++++++++++++++++++++++++ 3 files changed, 118 insertions(+) create mode 100644 demos/demo5/CMakeLists.txt create mode 100644 demos/demo5/demo5.cpp diff --git a/demos/CMakeLists.txt b/demos/CMakeLists.txt index cac7c2f..4c93284 100644 --- a/demos/CMakeLists.txt +++ b/demos/CMakeLists.txt @@ -2,4 +2,7 @@ add_subdirectory(demo1) add_subdirectory(demo2) add_subdirectory(demo3) add_subdirectory(demo4) +if(BUILD_WITH_SMIME) +add_subdirectory(demo5) +endif() add_subdirectory(async1) diff --git a/demos/demo5/CMakeLists.txt b/demos/demo5/CMakeLists.txt new file mode 100644 index 0000000..785fb85 --- /dev/null +++ b/demos/demo5/CMakeLists.txt @@ -0,0 +1,20 @@ +include_directories( + +) + +set(demo_SRCS + demo5.cpp +) + +add_definitions(-DWITH_SMIME) + +add_executable(demo5 + ${demo_SRCS} +) + +target_link_libraries(demo5 + SimpleMail::Core + Qt${QT_VERSION_MAJOR}::Core + OpenSSL::SSL + OpenSSL::Crypto +) diff --git a/demos/demo5/demo5.cpp b/demos/demo5/demo5.cpp new file mode 100644 index 0000000..9389aca --- /dev/null +++ b/demos/demo5/demo5.cpp @@ -0,0 +1,95 @@ +#include "../../src/SimpleMail" + +#include + +using namespace SimpleMail; + +int main(int argc, char *argv[]) +{ + QCoreApplication app(argc, argv); + + // This is a demo that shows you how to sign and/or encrypt an email + + // First we need to create an SmtpClient object + // We will use the Gmail's smtp server (smtp.gmail.com, port 465, ssl) + + Server server; + server.setHost(QLatin1String("smtp.gmail.com")); + server.setPort(465); + server.setConnectionType(Server::SslConnection); + + // We need to set the username (your email address) and password + // for smtp authentification. + + server.setUsername(QLatin1String("your_email_address@host.com")); + server.setPassword(QLatin1String("your_password")); + + // Now we create a MimeMessage object. This is the email. + + MimeMessage message; + + EmailAddress sender(QLatin1String("your_email_address@host.com"), QLatin1String("Your Name")); + message.setSender(sender); + + EmailAddress to(QLatin1String("recipient@host.com"), QLatin1String("Recipient's Name")); + message.addTo(to); + + message.setSubject(QLatin1String("SmtpClient for Qt - Demo")); + + // Now add some text to the email. + // First we create a MimeText object. + + auto text = std::make_shared(); + + text->setText(QLatin1String("Hi,\nThis is a simple email message.\n")); + + // Now add it to the mail + + message.addPart(text); + + // Add an attachment + + auto document = + std::make_shared(std::make_shared(QLatin1String("document.pdf"))); + message.addPart(document); + + // Now create an SMime object + + auto smime = new SimpleMail::SMime(&message); + + // Setup private and public key/certificate in PKCS#12 format + + smime->setKeyFile(QLatin1String("your_private_key.p12"), QLatin1String("your_private_key_password")); + smime->setPublicKey(QLatin1String("recipient_public_key.cert")); + + // Sign the message. Only your private key is required. + // if(!smime->sign()) { + // qDebug() << "Failed to create signed email"; + // delete smime; + // return -3; + // } + + // Encrypt the message. Only the recipient's public key/certificate is required. + // if(!smime->encrypt()) { + // qDebug() << "Failed to create encrypted email"; + // delete smime; + // return -3; + // } + + // Sign and encrypt the message + if (!smime->signAndEncrypt()) { + qDebug() << "Failed to create signed and encrypted email"; + delete smime; + return -3; + } + + // Now we can send the mail + ServerReply *reply = server.sendMail(message); + QObject::connect(reply, &ServerReply::finished, [=] { + qDebug() << "ServerReply finished" << reply->error() << reply->responseText(); + reply->deleteLater(); + qApp->exit(reply->error() ? -3 : 0); + }); + + app.exec(); +} From 7e4a7c7777c1453729b678839c8ce6799b991387 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Tue, 23 Jul 2024 06:40:54 +0000 Subject: [PATCH 10/25] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- demos/demo5/demo5.cpp | 3 ++- src/smime.cpp | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/demos/demo5/demo5.cpp b/demos/demo5/demo5.cpp index 9389aca..ff74b56 100644 --- a/demos/demo5/demo5.cpp +++ b/demos/demo5/demo5.cpp @@ -59,7 +59,8 @@ int main(int argc, char *argv[]) // Setup private and public key/certificate in PKCS#12 format - smime->setKeyFile(QLatin1String("your_private_key.p12"), QLatin1String("your_private_key_password")); + smime->setKeyFile(QLatin1String("your_private_key.p12"), + QLatin1String("your_private_key_password")); smime->setPublicKey(QLatin1String("recipient_public_key.cert")); // Sign the message. Only your private key is required. diff --git a/src/smime.cpp b/src/smime.cpp index 36b8d2e..9961c98 100644 --- a/src/smime.cpp +++ b/src/smime.cpp @@ -195,7 +195,7 @@ bool SMime::signAndEncrypt() void SMime::setSignedHeader() { Q_D(MimePart); - d->contentType = QByteArrayLiteral("application/pkcs7-signature; name=\"smime.p7s\""); + d->contentType = QByteArrayLiteral("application/pkcs7-signature; name=\"smime.p7s\""); d->contentCharset.clear(); d->contentEncoding = Base64; d->contentIsBase64 = true; From 7febacf53d325529e872354fb6e2ead9a7b96547 Mon Sep 17 00:00:00 2001 From: Daniel Wendt Date: Wed, 6 Nov 2024 09:18:40 +0100 Subject: [PATCH 11/25] fixup mail header --- src/mimepart.cpp | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/mimepart.cpp b/src/mimepart.cpp index dc29d09..12703dc 100644 --- a/src/mimepart.cpp +++ b/src/mimepart.cpp @@ -232,8 +232,7 @@ bool MimePart::write(QIODevice *device) "?=\""); } if (headers.contains("signed")) { - headers.append("; protocol=\"application/pkcs7-signature\";"); - headers.append("; micalg=sha1"); + headers.append("; protocol=\"application/pkcs7-signature\"; micalg=sha1"); } if (!d->contentCharset.isEmpty()) { headers.append("; charset=" + d->contentCharset); From d772de57944bd5bcb5cfbaf5cf344bd2ed7eb1ca Mon Sep 17 00:00:00 2001 From: Daniel Wendt Date: Thu, 7 Nov 2024 13:48:51 +0100 Subject: [PATCH 12/25] SMime: move members into the private class SMimePrivate --- src/CMakeLists.txt | 2 ++ src/smime.cpp | 79 +++++++++++++++++++++++---------------------- src/smime.h | 16 --------- src/smimepart_p.cpp | 14 ++++++++ src/smimepart_p.h | 33 +++++++++++++++++++ 5 files changed, 89 insertions(+), 55 deletions(-) create mode 100644 src/smimepart_p.cpp create mode 100644 src/smimepart_p.h diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 0e0cf68..f6dcba1 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -47,9 +47,11 @@ if(BUILD_WITH_SMIME) add_definitions(-DWITH_SMIME) list(APPEND simplemailqt_SRC smime.cpp + smimepart_p.cpp ) list(APPEND simplemailqt_HEADERS smime.h + smimepart_p.h ) set(OPENSSL_LIBS OpenSSL::SSL diff --git a/src/smime.cpp b/src/smime.cpp index 9961c98..d6baa43 100644 --- a/src/smime.cpp +++ b/src/smime.cpp @@ -2,6 +2,7 @@ #include "mimemultipart.h" #include "mimepart_p.h" +#include "smimepart_p.h" #include #include @@ -17,11 +18,7 @@ Q_LOGGING_CATEGORY(SIMPLEMAIL_SMIME, "simplemail.smime", QtInfoMsg) using namespace SimpleMail; SMime::SMime(MimeMessage *message) - : _privateKey(nullptr) - , _certificate(nullptr) - , _certificateCA(nullptr) - , _recipsReceiver(nullptr) - , _input(nullptr) + : MimePart(new SMimePrivate) { initOpenSSL(); _mimeMessage = message; @@ -30,33 +27,31 @@ SMime::SMime(MimeMessage *message) SMime::~SMime() { - BIO_free(_input); - EVP_PKEY_free(_privateKey); - X509_free(_certificate); - sk_X509_pop_free(_certificateCA, X509_free); - sk_X509_pop_free(_recipsReceiver, X509_free); _mimeMessage = nullptr; } void SMime::setKeyFile(const QString &filename, const QString &password) { - _keyfile = filename; - _password = password; + SMimePrivate *d = static_cast(d_ptr.data()); + d->_keyfile = filename; + d->_password = password; loadPKCS12PrivateKey(); } void SMime::setPublicKey(const QString &filename) { - _publicKeyfile = filename; + SMimePrivate *d = static_cast(d_ptr.data()); + d->_publicKeyfile = filename; loadPKCS12PublicKey(); } bool SMime::sign() { + SMimePrivate *d = static_cast(d_ptr.data()); bool ret = false; PKCS7 *p7 = nullptr; - if (!_certificate || !_privateKey) { + if (!d->_certificate || !d->_privateKey) { qCDebug(SIMPLEMAIL_SMIME) << "no certificate or private key"; return ret; } @@ -72,7 +67,7 @@ bool SMime::sign() goto err; /* Sign content */ - p7 = PKCS7_sign(_certificate, _privateKey, NULL, _input, flags); + p7 = PKCS7_sign(d->_certificate, d->_privateKey, NULL, d->_input, flags); if (!p7) goto err; @@ -83,7 +78,7 @@ bool SMime::sign() /* Write out S/MIME message */ if (!SMIME_write_PKCS7(out, p7, - _input, + d->_input, flags | PKCS7_CRLFEOL)) // needed for intializing/finalizing SMIME structure goto err; @@ -105,10 +100,11 @@ bool SMime::sign() bool SMime::encrypt() { + SMimePrivate *d = static_cast(d_ptr.data()); bool ret = false; PKCS7 *p7 = nullptr; - if (!_recipsReceiver) { + if (!d->_recipsReceiver) { qCDebug(SIMPLEMAIL_SMIME) << "no public key"; return ret; } @@ -121,12 +117,12 @@ bool SMime::encrypt() goto err; /* encrypt content */ - p7 = PKCS7_encrypt(_recipsReceiver, _input, EVP_des_ede3_cbc(), flags); + p7 = PKCS7_encrypt(d->_recipsReceiver, d->_input, EVP_des_ede3_cbc(), flags); if (!p7) goto err; - if (!handleData(p7, _input, flags)) + if (!handleData(p7, d->_input, flags)) goto err; _mimeMessage->setContent(std::shared_ptr(this)); @@ -142,14 +138,15 @@ bool SMime::encrypt() bool SMime::signAndEncrypt() { + SMimePrivate *d = static_cast(d_ptr.data()); bool ret = false; PKCS7 *p7 = nullptr; BIO *signedContent = nullptr; - if (!_certificate || !_privateKey) { + if (!d->_certificate || !d->_privateKey) { qCDebug(SIMPLEMAIL_SMIME) << "no certificate or private key"; return ret; } - if (!_recipsReceiver) { + if (!d->_recipsReceiver) { qCDebug(SIMPLEMAIL_SMIME) << "no public key"; return ret; } @@ -162,17 +159,17 @@ bool SMime::signAndEncrypt() goto err; /* Sign content */ - p7 = PKCS7_sign(_certificate, _privateKey, NULL, _input, flags); + p7 = PKCS7_sign(d->_certificate, d->_privateKey, NULL, d->_input, flags); if (!p7) goto err; signedContent = BIO_new(BIO_s_mem()); - if (!SMIME_write_PKCS7(signedContent, p7, _input, flags | PKCS7_CRLFEOL)) + if (!SMIME_write_PKCS7(signedContent, p7, d->_input, flags | PKCS7_CRLFEOL)) goto err; PKCS7_free(p7); p7 = NULL; - p7 = PKCS7_encrypt(_recipsReceiver, signedContent, EVP_des_ede3_cbc(), flags); + p7 = PKCS7_encrypt(d->_recipsReceiver, signedContent, EVP_des_ede3_cbc(), flags); if (!p7) goto err; @@ -217,7 +214,8 @@ void SMime::setEncryptionHeader() void SMime::loadPKCS12PrivateKey() { - QFile file(QString::fromLatin1(_keyfile.toStdString().c_str())); + SMimePrivate *d = static_cast(d_ptr.data()); + QFile file(QString::fromLatin1(d->_keyfile.toStdString().c_str())); if (!file.exists()) return; file.open(QFile::ReadOnly); @@ -226,7 +224,7 @@ void SMime::loadPKCS12PrivateKey() BIO *keyBuffer = BIO_new(BIO_s_mem()); if (keyBuffer == NULL) { - qCDebug(SIMPLEMAIL_SMIME) << "Error opening file " << _keyfile; + qCDebug(SIMPLEMAIL_SMIME) << "Error opening file " << d->_keyfile; return; } @@ -239,14 +237,14 @@ void SMime::loadPKCS12PrivateKey() } if (!PKCS12_parse( - p12, _password.toStdString().c_str(), &_privateKey, &_certificate, &_certificateCA)) { + p12, d->_password.toStdString().c_str(), &d->_privateKey, &d->_certificate, &d->_certificateCA)) { OSSL_PROVIDER_try_load(nullptr, "legacy", 1); if (!PKCS12_parse(p12, - _password.toStdString().c_str(), - &_privateKey, - &_certificate, - &_certificateCA)) { + d->_password.toStdString().c_str(), + &d->_privateKey, + &d->_certificate, + &d->_certificateCA)) { qCDebug(SIMPLEMAIL_SMIME) << "Error parsing PKCS#12 file"; } } @@ -256,7 +254,8 @@ void SMime::loadPKCS12PrivateKey() void SMime::loadPKCS12PublicKey() { - QFile file(QString::fromLatin1(_publicKeyfile.toStdString().c_str())); + SMimePrivate *d = static_cast(d_ptr.data()); + QFile file(QString::fromLatin1(d->_publicKeyfile.toStdString().c_str())); if (!file.exists()) return; file.open(QFile::ReadOnly); @@ -265,7 +264,7 @@ void SMime::loadPKCS12PublicKey() BIO *keyBuffer = BIO_new(BIO_s_mem()); if (keyBuffer == NULL) { - qCDebug(SIMPLEMAIL_SMIME) << "Error opening file " << _publicKeyfile; + qCDebug(SIMPLEMAIL_SMIME) << "Error opening file " << d->_publicKeyfile; return; } BIO_write(keyBuffer, (void *) buffer.data(), buffer.length()); @@ -273,9 +272,9 @@ void SMime::loadPKCS12PublicKey() X509 *publicrcert = PEM_read_bio_X509(keyBuffer, NULL, 0, NULL); BIO_free(keyBuffer); - _recipsReceiver = sk_X509_new_null(); + d->_recipsReceiver = sk_X509_new_null(); - if (!_recipsReceiver || !sk_X509_push(_recipsReceiver, publicrcert)) { + if (!d->_recipsReceiver || !sk_X509_push(d->_recipsReceiver, publicrcert)) { return; } publicrcert = nullptr; @@ -300,23 +299,25 @@ void SMime::wrapMimeMultiPart() bool SMime::writeInputBuffer() { - _input = BIO_new(BIO_s_mem()); - if (!_input) + SMimePrivate *d = static_cast(d_ptr.data()); + d->_input = BIO_new(BIO_s_mem()); + if (!d->_input) return false; - if (!BIO_write(_input, (void *) _message.data(), _message.length())) + if (!BIO_write(d->_input, (void *) d->_message.data(), d->_message.length())) return false; return true; } bool SMime::writeMimeMessageBuffer() { + SMimePrivate *d = static_cast(d_ptr.data()); QBuffer *buffer = new QBuffer; buffer->open(QBuffer::ReadWrite); _mimeMessage->getContent()->write(buffer); buffer->close(); - _message = buffer->data(); + d->_message = buffer->data(); delete buffer; return true; diff --git a/src/smime.h b/src/smime.h index f391273..8e25dbe 100644 --- a/src/smime.h +++ b/src/smime.h @@ -38,22 +38,6 @@ class SMTP_EXPORT SMime : public MimePart bool handleData(PKCS7 *p7, BIO *dataIn, int flags); MimeMessage *_mimeMessage; - - QByteArray _message; - QString _keyfile; - QString _publicKeyfile; - QString _password; - - // private key for signing - EVP_PKEY *_privateKey; - - // certificate from private Key - X509 *_certificate; - STACK_OF(X509) * _certificateCA; - // certificate from public key, used for encrypting - STACK_OF(X509) * _recipsReceiver; - // buffer to be signed/encrypted - BIO *_input; }; } // namespace SimpleMail diff --git a/src/smimepart_p.cpp b/src/smimepart_p.cpp new file mode 100644 index 0000000..a1fa583 --- /dev/null +++ b/src/smimepart_p.cpp @@ -0,0 +1,14 @@ +#include "smimepart_p.h" + +#include + +using namespace SimpleMail; + +SMimePrivate::~SMimePrivate() +{ + BIO_free(_input); + EVP_PKEY_free(_privateKey); + X509_free(_certificate); + sk_X509_pop_free(_certificateCA, X509_free); + sk_X509_pop_free(_recipsReceiver, X509_free); +} diff --git a/src/smimepart_p.h b/src/smimepart_p.h new file mode 100644 index 0000000..1086e00 --- /dev/null +++ b/src/smimepart_p.h @@ -0,0 +1,33 @@ +#ifndef SMIMEPART_P_H +#define SMIMEPART_P_H + +#include "mimepart_p.h" +#include + +namespace SimpleMail { + +class SMimePrivate : public MimePartPrivate +{ +public: + virtual ~SMimePrivate(); + + QByteArray _message; + QString _keyfile; + QString _publicKeyfile; + QString _password; + + // private key for signing + EVP_PKEY *_privateKey = nullptr; + + // certificate from private Key + X509 *_certificate = nullptr; + STACK_OF(X509) * _certificateCA = nullptr; + // certificate from public key, used for encrypting + STACK_OF(X509) * _recipsReceiver = nullptr; + // buffer to be signed/encrypted + BIO *_input = nullptr; +}; + +} // namespace SimpleMail + +#endif // SMIMEPART_P_H From 4f9d86217e0fdc78ca57c0d27a79bf7726008b54 Mon Sep 17 00:00:00 2001 From: Daniel Wendt Date: Thu, 7 Nov 2024 14:22:44 +0100 Subject: [PATCH 13/25] remove not needed string conversions --- src/smime.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/smime.cpp b/src/smime.cpp index d6baa43..d5d2e72 100644 --- a/src/smime.cpp +++ b/src/smime.cpp @@ -215,7 +215,7 @@ void SMime::setEncryptionHeader() void SMime::loadPKCS12PrivateKey() { SMimePrivate *d = static_cast(d_ptr.data()); - QFile file(QString::fromLatin1(d->_keyfile.toStdString().c_str())); + QFile file(d->_keyfile); if (!file.exists()) return; file.open(QFile::ReadOnly); @@ -255,7 +255,7 @@ void SMime::loadPKCS12PrivateKey() void SMime::loadPKCS12PublicKey() { SMimePrivate *d = static_cast(d_ptr.data()); - QFile file(QString::fromLatin1(d->_publicKeyfile.toStdString().c_str())); + QFile file(d->_publicKeyfile); if (!file.exists()) return; file.open(QFile::ReadOnly); From 35b73dcf26d8357538cbb1c8d46e73f898c2c48b Mon Sep 17 00:00:00 2001 From: Daniel Wendt Date: Thu, 7 Nov 2024 14:31:25 +0100 Subject: [PATCH 14/25] use QBuffer on stack --- src/smime.cpp | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/src/smime.cpp b/src/smime.cpp index d5d2e72..0a4580e 100644 --- a/src/smime.cpp +++ b/src/smime.cpp @@ -311,14 +311,13 @@ bool SMime::writeInputBuffer() bool SMime::writeMimeMessageBuffer() { SMimePrivate *d = static_cast(d_ptr.data()); - QBuffer *buffer = new QBuffer; - buffer->open(QBuffer::ReadWrite); + QBuffer buffer; + buffer.open(QBuffer::ReadWrite); - _mimeMessage->getContent()->write(buffer); - buffer->close(); + _mimeMessage->getContent()->write(&buffer); + buffer.close(); - d->_message = buffer->data(); - delete buffer; + d->_message = buffer.data(); return true; } From 7bd9861bc0a5b98b32a3e37dcfa153104fce685e Mon Sep 17 00:00:00 2001 From: Daniel Wendt Date: Thu, 7 Nov 2024 14:36:12 +0100 Subject: [PATCH 15/25] cmake improvements --- src/CMakeLists.txt | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index f6dcba1..fb15764 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -53,12 +53,6 @@ if(BUILD_WITH_SMIME) smime.h smimepart_p.h ) - set(OPENSSL_LIBS - OpenSSL::SSL - OpenSSL::Crypto - ) -else() - set(OPENSSL_LIBS) endif() add_library(SimpleMail${PROJECT_VERSION_MAJOR}Qt${QT_VERSION_MAJOR} @@ -111,9 +105,16 @@ target_link_libraries(SimpleMail${PROJECT_VERSION_MAJOR}Qt${QT_VERSION_MAJOR} PUBLIC Qt::Core Qt::Network - ${OPENSSL_LIBS} ) +if(BUILD_WITH_SMIME) + target_link_libraries(SimpleMail${PROJECT_VERSION_MAJOR}Qt${QT_VERSION_MAJOR} + PUBLIC + OpenSSL::SSL + OpenSSL::Crypto + ) +endif() + set_property(TARGET SimpleMail${PROJECT_VERSION_MAJOR}Qt${QT_VERSION_MAJOR} PROPERTY PUBLIC_HEADER ${simplemailqt_HEADERS}) install(TARGETS SimpleMail${PROJECT_VERSION_MAJOR}Qt${QT_VERSION_MAJOR} EXPORT SimpleMailTargets DESTINATION ${CMAKE_INSTALL_LIBDIR} From 04987d2310dc0872d5d9b853a1add066f5e991fb Mon Sep 17 00:00:00 2001 From: Daniel Wendt Date: Tue, 3 Dec 2024 16:15:46 +0100 Subject: [PATCH 16/25] use std::unique_ptr for members in class SMimePrivate --- src/smime.cpp | 39 +++++++++++++++++++++-------------- src/smimepart_p.cpp | 12 +++++------ src/smimepart_p.h | 50 ++++++++++++++++++++++++++++++++++++++------- 3 files changed, 72 insertions(+), 29 deletions(-) diff --git a/src/smime.cpp b/src/smime.cpp index 0a4580e..3ac8732 100644 --- a/src/smime.cpp +++ b/src/smime.cpp @@ -67,7 +67,7 @@ bool SMime::sign() goto err; /* Sign content */ - p7 = PKCS7_sign(d->_certificate, d->_privateKey, NULL, d->_input, flags); + p7 = PKCS7_sign(d->_certificate.get(), d->_privateKey.get(), NULL, d->_input.get(), flags); if (!p7) goto err; @@ -78,7 +78,7 @@ bool SMime::sign() /* Write out S/MIME message */ if (!SMIME_write_PKCS7(out, p7, - d->_input, + d->_input.get(), flags | PKCS7_CRLFEOL)) // needed for intializing/finalizing SMIME structure goto err; @@ -117,12 +117,12 @@ bool SMime::encrypt() goto err; /* encrypt content */ - p7 = PKCS7_encrypt(d->_recipsReceiver, d->_input, EVP_des_ede3_cbc(), flags); + p7 = PKCS7_encrypt(d->_recipsReceiver.get(), d->_input.get(), EVP_des_ede3_cbc(), flags); if (!p7) goto err; - if (!handleData(p7, d->_input, flags)) + if (!handleData(p7, d->_input.get(), flags)) goto err; _mimeMessage->setContent(std::shared_ptr(this)); @@ -159,17 +159,17 @@ bool SMime::signAndEncrypt() goto err; /* Sign content */ - p7 = PKCS7_sign(d->_certificate, d->_privateKey, NULL, d->_input, flags); + p7 = PKCS7_sign(d->_certificate.get(), d->_privateKey.get(), NULL, d->_input.get(), flags); if (!p7) goto err; signedContent = BIO_new(BIO_s_mem()); - if (!SMIME_write_PKCS7(signedContent, p7, d->_input, flags | PKCS7_CRLFEOL)) + if (!SMIME_write_PKCS7(signedContent, p7, d->_input.get(), flags | PKCS7_CRLFEOL)) goto err; PKCS7_free(p7); p7 = NULL; - p7 = PKCS7_encrypt(d->_recipsReceiver, signedContent, EVP_des_ede3_cbc(), flags); + p7 = PKCS7_encrypt(d->_recipsReceiver.get(), signedContent, EVP_des_ede3_cbc(), flags); if (!p7) goto err; @@ -236,18 +236,24 @@ void SMime::loadPKCS12PrivateKey() qCDebug(SIMPLEMAIL_SMIME) << "Error reading PKCS#12 file"; } + EVP_PKEY *privateKey = nullptr; + X509 *certificate = nullptr; + STACK_OF(X509) *certificateCA = nullptr; if (!PKCS12_parse( - p12, d->_password.toStdString().c_str(), &d->_privateKey, &d->_certificate, &d->_certificateCA)) { + p12, d->_password.toStdString().c_str(), &privateKey, &certificate, &certificateCA)) { OSSL_PROVIDER_try_load(nullptr, "legacy", 1); if (!PKCS12_parse(p12, d->_password.toStdString().c_str(), - &d->_privateKey, - &d->_certificate, - &d->_certificateCA)) { + &privateKey, + &certificate, + &certificateCA)) { qCDebug(SIMPLEMAIL_SMIME) << "Error parsing PKCS#12 file"; } } + d->_privateKey.reset(privateKey); + d->_certificate.reset(certificate); + d->_certificateCA.reset(certificateCA); PKCS12_free(p12); } @@ -272,11 +278,13 @@ void SMime::loadPKCS12PublicKey() X509 *publicrcert = PEM_read_bio_X509(keyBuffer, NULL, 0, NULL); BIO_free(keyBuffer); - d->_recipsReceiver = sk_X509_new_null(); - if (!d->_recipsReceiver || !sk_X509_push(d->_recipsReceiver, publicrcert)) { + STACK_OF(X509) *recipsReceiver = sk_X509_new_null(); + + if (!recipsReceiver || !sk_X509_push(recipsReceiver, publicrcert)) { return; } + d->_recipsReceiver.reset(recipsReceiver); publicrcert = nullptr; } @@ -300,10 +308,11 @@ void SMime::wrapMimeMultiPart() bool SMime::writeInputBuffer() { SMimePrivate *d = static_cast(d_ptr.data()); - d->_input = BIO_new(BIO_s_mem()); + BIO *input = BIO_new(BIO_s_mem()); + d->_input = std::unique_ptr(input); if (!d->_input) return false; - if (!BIO_write(d->_input, (void *) d->_message.data(), d->_message.length())) + if (!BIO_write(d->_input.get(), (void *) d->_message.data(), d->_message.length())) return false; return true; } diff --git a/src/smimepart_p.cpp b/src/smimepart_p.cpp index a1fa583..ec6499b 100644 --- a/src/smimepart_p.cpp +++ b/src/smimepart_p.cpp @@ -1,14 +1,12 @@ #include "smimepart_p.h" -#include - using namespace SimpleMail; SMimePrivate::~SMimePrivate() { - BIO_free(_input); - EVP_PKEY_free(_privateKey); - X509_free(_certificate); - sk_X509_pop_free(_certificateCA, X509_free); - sk_X509_pop_free(_recipsReceiver, X509_free); + _input = nullptr; + _privateKey = nullptr; + _certificate = nullptr; + _certificateCA = nullptr; + _recipsReceiver = nullptr; } diff --git a/src/smimepart_p.h b/src/smimepart_p.h index 1086e00..f085905 100644 --- a/src/smimepart_p.h +++ b/src/smimepart_p.h @@ -3,9 +3,45 @@ #include "mimepart_p.h" #include +#include -namespace SimpleMail { +template<> class std::default_delete +{ +public: + void operator()(BIO *ptr) const + { + BIO_free(ptr); + } +}; + +template<> class std::default_delete +{ +public: + void operator()(EVP_PKEY *ptr) const + { + EVP_PKEY_free(ptr); + } +}; +template<> class std::default_delete +{ +public: + void operator()(X509 *ptr) const + { + X509_free(ptr); + } +}; + +template<> class std::default_delete +{ +public: + void operator()(STACK_OF(X509) *ptr) const + { + sk_X509_pop_free(ptr, X509_free); + } +}; + +namespace SimpleMail { class SMimePrivate : public MimePartPrivate { public: @@ -17,17 +53,17 @@ class SMimePrivate : public MimePartPrivate QString _password; // private key for signing - EVP_PKEY *_privateKey = nullptr; + std::unique_ptr _privateKey = nullptr; // certificate from private Key - X509 *_certificate = nullptr; - STACK_OF(X509) * _certificateCA = nullptr; + std::unique_ptr _certificate = nullptr; + std::unique_ptr _certificateCA = nullptr; // certificate from public key, used for encrypting - STACK_OF(X509) * _recipsReceiver = nullptr; + std::unique_ptr _recipsReceiver = nullptr; + // buffer to be signed/encrypted - BIO *_input = nullptr; + std::unique_ptr _input; }; } // namespace SimpleMail - #endif // SMIMEPART_P_H From f68ef7eb19aea82e1364effbd31a52915fb17b3f Mon Sep 17 00:00:00 2001 From: Daniel Wendt Date: Fri, 20 Dec 2024 09:02:47 +0100 Subject: [PATCH 17/25] rename class SMime to SMimePart --- demos/demo5/demo5.cpp | 2 +- src/smime.cpp | 64 +++++++++++++++++++++---------------------- src/smime.h | 10 +++---- 3 files changed, 38 insertions(+), 38 deletions(-) diff --git a/demos/demo5/demo5.cpp b/demos/demo5/demo5.cpp index ff74b56..61a57f0 100644 --- a/demos/demo5/demo5.cpp +++ b/demos/demo5/demo5.cpp @@ -55,7 +55,7 @@ int main(int argc, char *argv[]) // Now create an SMime object - auto smime = new SimpleMail::SMime(&message); + auto smime = new SimpleMail::SMimePart(&message); // Setup private and public key/certificate in PKCS#12 format diff --git a/src/smime.cpp b/src/smime.cpp index 3ac8732..9fa5093 100644 --- a/src/smime.cpp +++ b/src/smime.cpp @@ -13,11 +13,11 @@ #include #include -Q_LOGGING_CATEGORY(SIMPLEMAIL_SMIME, "simplemail.smime", QtInfoMsg) +Q_LOGGING_CATEGORY(SIMPLEMAIL_SMIMEPART, "simplemail.smimepart", QtInfoMsg) using namespace SimpleMail; -SMime::SMime(MimeMessage *message) +SMimePart::SMimePart(MimeMessage *message) : MimePart(new SMimePrivate) { initOpenSSL(); @@ -25,12 +25,12 @@ SMime::SMime(MimeMessage *message) writeMimeMessageBuffer(); } -SMime::~SMime() +SMimePart::~SMimePart() { _mimeMessage = nullptr; } -void SMime::setKeyFile(const QString &filename, const QString &password) +void SMimePart::setKeyFile(const QString &filename, const QString &password) { SMimePrivate *d = static_cast(d_ptr.data()); d->_keyfile = filename; @@ -38,21 +38,21 @@ void SMime::setKeyFile(const QString &filename, const QString &password) loadPKCS12PrivateKey(); } -void SMime::setPublicKey(const QString &filename) +void SMimePart::setPublicKey(const QString &filename) { SMimePrivate *d = static_cast(d_ptr.data()); d->_publicKeyfile = filename; loadPKCS12PublicKey(); } -bool SMime::sign() +bool SMimePart::sign() { SMimePrivate *d = static_cast(d_ptr.data()); bool ret = false; PKCS7 *p7 = nullptr; if (!d->_certificate || !d->_privateKey) { - qCDebug(SIMPLEMAIL_SMIME) << "no certificate or private key"; + qCDebug(SIMPLEMAIL_SMIMEPART) << "no certificate or private key"; return ret; } @@ -86,26 +86,26 @@ bool SMime::sign() if (!handleData(p7, nullptr, 0)) goto err; - _mimeMessage->addPart(std::shared_ptr(this)); + _mimeMessage->addPart(std::shared_ptr(this)); ret = true; err: if (!ret) { - qCDebug(SIMPLEMAIL_SMIME) << "Error Signing Data"; + qCDebug(SIMPLEMAIL_SMIMEPART) << "Error Signing Data"; } PKCS7_free(p7); BIO_free(out); return ret; } -bool SMime::encrypt() +bool SMimePart::encrypt() { SMimePrivate *d = static_cast(d_ptr.data()); bool ret = false; PKCS7 *p7 = nullptr; if (!d->_recipsReceiver) { - qCDebug(SIMPLEMAIL_SMIME) << "no public key"; + qCDebug(SIMPLEMAIL_SMIMEPART) << "no public key"; return ret; } @@ -125,29 +125,29 @@ bool SMime::encrypt() if (!handleData(p7, d->_input.get(), flags)) goto err; - _mimeMessage->setContent(std::shared_ptr(this)); + _mimeMessage->setContent(std::shared_ptr(this)); ret = true; err: if (!ret) { - qCDebug(SIMPLEMAIL_SMIME) << "Error Encrypting Data"; + qCDebug(SIMPLEMAIL_SMIMEPART) << "Error Encrypting Data"; } PKCS7_free(p7); return ret; } -bool SMime::signAndEncrypt() +bool SMimePart::signAndEncrypt() { SMimePrivate *d = static_cast(d_ptr.data()); bool ret = false; PKCS7 *p7 = nullptr; BIO *signedContent = nullptr; if (!d->_certificate || !d->_privateKey) { - qCDebug(SIMPLEMAIL_SMIME) << "no certificate or private key"; + qCDebug(SIMPLEMAIL_SMIMEPART) << "no certificate or private key"; return ret; } if (!d->_recipsReceiver) { - qCDebug(SIMPLEMAIL_SMIME) << "no public key"; + qCDebug(SIMPLEMAIL_SMIMEPART) << "no public key"; return ret; } @@ -177,19 +177,19 @@ bool SMime::signAndEncrypt() if (!handleData(p7, signedContent, flags)) goto err; - _mimeMessage->setContent(std::shared_ptr(this)); + _mimeMessage->setContent(std::shared_ptr(this)); ret = true; err: if (!ret) { - qCDebug(SIMPLEMAIL_SMIME) << "Error Signing/Encrypting Data"; + qCDebug(SIMPLEMAIL_SMIMEPART) << "Error Signing/Encrypting Data"; } PKCS7_free(p7); BIO_free(signedContent); return ret; } -void SMime::setSignedHeader() +void SMimePart::setSignedHeader() { Q_D(MimePart); d->contentType = QByteArrayLiteral("application/pkcs7-signature; name=\"smime.p7s\""); @@ -200,7 +200,7 @@ void SMime::setSignedHeader() d->header.append("Content-Disposition: attachment; filename=\"smime.p7s\"\r\n"); } -void SMime::setEncryptionHeader() +void SMimePart::setEncryptionHeader() { Q_D(MimePart); d->contentType = QByteArrayLiteral( @@ -212,7 +212,7 @@ void SMime::setEncryptionHeader() d->header.append("Content-Disposition: attachment; filename=\"smime.p7m\"\r\n"); } -void SMime::loadPKCS12PrivateKey() +void SMimePart::loadPKCS12PrivateKey() { SMimePrivate *d = static_cast(d_ptr.data()); QFile file(d->_keyfile); @@ -224,7 +224,7 @@ void SMime::loadPKCS12PrivateKey() BIO *keyBuffer = BIO_new(BIO_s_mem()); if (keyBuffer == NULL) { - qCDebug(SIMPLEMAIL_SMIME) << "Error opening file " << d->_keyfile; + qCDebug(SIMPLEMAIL_SMIMEPART) << "Error opening file " << d->_keyfile; return; } @@ -233,7 +233,7 @@ void SMime::loadPKCS12PrivateKey() BIO_free(keyBuffer); if (p12 == NULL) { - qCDebug(SIMPLEMAIL_SMIME) << "Error reading PKCS#12 file"; + qCDebug(SIMPLEMAIL_SMIMEPART) << "Error reading PKCS#12 file"; } EVP_PKEY *privateKey = nullptr; @@ -248,7 +248,7 @@ void SMime::loadPKCS12PrivateKey() &privateKey, &certificate, &certificateCA)) { - qCDebug(SIMPLEMAIL_SMIME) << "Error parsing PKCS#12 file"; + qCDebug(SIMPLEMAIL_SMIMEPART) << "Error parsing PKCS#12 file"; } } d->_privateKey.reset(privateKey); @@ -258,7 +258,7 @@ void SMime::loadPKCS12PrivateKey() PKCS12_free(p12); } -void SMime::loadPKCS12PublicKey() +void SMimePart::loadPKCS12PublicKey() { SMimePrivate *d = static_cast(d_ptr.data()); QFile file(d->_publicKeyfile); @@ -270,7 +270,7 @@ void SMime::loadPKCS12PublicKey() BIO *keyBuffer = BIO_new(BIO_s_mem()); if (keyBuffer == NULL) { - qCDebug(SIMPLEMAIL_SMIME) << "Error opening file " << d->_publicKeyfile; + qCDebug(SIMPLEMAIL_SMIMEPART) << "Error opening file " << d->_publicKeyfile; return; } BIO_write(keyBuffer, (void *) buffer.data(), buffer.length()); @@ -288,13 +288,13 @@ void SMime::loadPKCS12PublicKey() publicrcert = nullptr; } -void SMime::initOpenSSL() +void SMimePart::initOpenSSL() { OpenSSL_add_all_algorithms(); ERR_load_crypto_strings(); } -void SMime::wrapMimeMultiPart() +void SMimePart::wrapMimeMultiPart() { auto &mimeMultiPart = *_mimeMessage->getContent(); @@ -305,7 +305,7 @@ void SMime::wrapMimeMultiPart() } } -bool SMime::writeInputBuffer() +bool SMimePart::writeInputBuffer() { SMimePrivate *d = static_cast(d_ptr.data()); BIO *input = BIO_new(BIO_s_mem()); @@ -317,7 +317,7 @@ bool SMime::writeInputBuffer() return true; } -bool SMime::writeMimeMessageBuffer() +bool SMimePart::writeMimeMessageBuffer() { SMimePrivate *d = static_cast(d_ptr.data()); QBuffer buffer; @@ -331,7 +331,7 @@ bool SMime::writeMimeMessageBuffer() return true; } -bool SMime::handleData(PKCS7 *p7, BIO *dataIn, int flags) +bool SMimePart::handleData(PKCS7 *p7, BIO *dataIn, int flags) { QByteArray contentBuffer; @@ -351,7 +351,7 @@ bool SMime::handleData(PKCS7 *p7, BIO *dataIn, int flags) char buffer[1024]; while (BIO_get_line(encryptedData, &buffer[0], sizeof(buffer))) { - qCDebug(SIMPLEMAIL_SMIME) << QString::fromLatin1(buffer); + qCDebug(SIMPLEMAIL_SMIMEPART) << QString::fromLatin1(buffer); if (strncmp(&buffer[0], "-----", strlen("-----")) != 0) { contentBuffer += QByteArray(&buffer[0], strlen(buffer) - 1); contentBuffer += QByteArray("\r\n"); diff --git a/src/smime.h b/src/smime.h index 8e25dbe..d20188d 100644 --- a/src/smime.h +++ b/src/smime.h @@ -1,5 +1,5 @@ -#ifndef SMIME_H -#define SMIME_H +#ifndef SMIMEPART_H +#define SMIMEPART_H #include "mimemessage.h" #include "mimepart.h" @@ -9,11 +9,11 @@ #include namespace SimpleMail { -class SMTP_EXPORT SMime : public MimePart +class SMTP_EXPORT SMimePart : public MimePart { public: - SMime(MimeMessage *message); - virtual ~SMime(); + SMimePart(MimeMessage *message); + virtual ~SMimePart(); void setKeyFile(const QString &filename, const QString &password); From 6d82e745f70351496198ca9f1900f432c702d92c Mon Sep 17 00:00:00 2001 From: Daniel Wendt Date: Fri, 20 Dec 2024 09:17:00 +0100 Subject: [PATCH 18/25] rename file smime.h/.cpp to smimepart.h/.cpp --- src/CMakeLists.txt | 4 ++-- src/SimpleMail | 2 +- src/{smime.cpp => smimepart.cpp} | 2 +- src/{smime.h => smimepart.h} | 0 4 files changed, 4 insertions(+), 4 deletions(-) rename src/{smime.cpp => smimepart.cpp} (99%) rename src/{smime.h => smimepart.h} (100%) diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index fb15764..3797ab3 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -46,11 +46,11 @@ set(simplemailqt_HEADERS_PRIVATE if(BUILD_WITH_SMIME) add_definitions(-DWITH_SMIME) list(APPEND simplemailqt_SRC - smime.cpp + smimepart.cpp smimepart_p.cpp ) list(APPEND simplemailqt_HEADERS - smime.h + smimepart.h smimepart_p.h ) endif() diff --git a/src/SimpleMail b/src/SimpleMail index efcaf39..b73ff0c 100644 --- a/src/SimpleMail +++ b/src/SimpleMail @@ -27,5 +27,5 @@ #include "server.h" #include "serverreply.h" #ifdef WITH_SMIME -#include "smime.h" +#include "smimepart.h" #endif diff --git a/src/smime.cpp b/src/smimepart.cpp similarity index 99% rename from src/smime.cpp rename to src/smimepart.cpp index 9fa5093..4f542af 100644 --- a/src/smime.cpp +++ b/src/smimepart.cpp @@ -1,4 +1,4 @@ -#include "smime.h" +#include "smimepart.h" #include "mimemultipart.h" #include "mimepart_p.h" diff --git a/src/smime.h b/src/smimepart.h similarity index 100% rename from src/smime.h rename to src/smimepart.h From 057a7b7de7479687668a7b8b74b1b78969e197bb Mon Sep 17 00:00:00 2001 From: Daniel Wendt Date: Tue, 7 Jan 2025 10:25:21 +0100 Subject: [PATCH 19/25] rename class SMimePrivate to SMimePartPrivate --- src/smimepart.cpp | 20 ++++++++++---------- src/smimepart_p.cpp | 2 +- src/smimepart_p.h | 4 ++-- 3 files changed, 13 insertions(+), 13 deletions(-) diff --git a/src/smimepart.cpp b/src/smimepart.cpp index 4f542af..c659140 100644 --- a/src/smimepart.cpp +++ b/src/smimepart.cpp @@ -18,7 +18,7 @@ Q_LOGGING_CATEGORY(SIMPLEMAIL_SMIMEPART, "simplemail.smimepart", QtInfoMsg) using namespace SimpleMail; SMimePart::SMimePart(MimeMessage *message) - : MimePart(new SMimePrivate) + : MimePart(new SMimePartPrivate) { initOpenSSL(); _mimeMessage = message; @@ -32,7 +32,7 @@ SMimePart::~SMimePart() void SMimePart::setKeyFile(const QString &filename, const QString &password) { - SMimePrivate *d = static_cast(d_ptr.data()); + SMimePartPrivate *d = static_cast(d_ptr.data()); d->_keyfile = filename; d->_password = password; loadPKCS12PrivateKey(); @@ -40,14 +40,14 @@ void SMimePart::setKeyFile(const QString &filename, const QString &password) void SMimePart::setPublicKey(const QString &filename) { - SMimePrivate *d = static_cast(d_ptr.data()); + SMimePartPrivate *d = static_cast(d_ptr.data()); d->_publicKeyfile = filename; loadPKCS12PublicKey(); } bool SMimePart::sign() { - SMimePrivate *d = static_cast(d_ptr.data()); + SMimePartPrivate *d = static_cast(d_ptr.data()); bool ret = false; PKCS7 *p7 = nullptr; @@ -100,7 +100,7 @@ bool SMimePart::sign() bool SMimePart::encrypt() { - SMimePrivate *d = static_cast(d_ptr.data()); + SMimePartPrivate *d = static_cast(d_ptr.data()); bool ret = false; PKCS7 *p7 = nullptr; @@ -138,7 +138,7 @@ bool SMimePart::encrypt() bool SMimePart::signAndEncrypt() { - SMimePrivate *d = static_cast(d_ptr.data()); + SMimePartPrivate *d = static_cast(d_ptr.data()); bool ret = false; PKCS7 *p7 = nullptr; BIO *signedContent = nullptr; @@ -214,7 +214,7 @@ void SMimePart::setEncryptionHeader() void SMimePart::loadPKCS12PrivateKey() { - SMimePrivate *d = static_cast(d_ptr.data()); + SMimePartPrivate *d = static_cast(d_ptr.data()); QFile file(d->_keyfile); if (!file.exists()) return; @@ -260,7 +260,7 @@ void SMimePart::loadPKCS12PrivateKey() void SMimePart::loadPKCS12PublicKey() { - SMimePrivate *d = static_cast(d_ptr.data()); + SMimePartPrivate *d = static_cast(d_ptr.data()); QFile file(d->_publicKeyfile); if (!file.exists()) return; @@ -307,7 +307,7 @@ void SMimePart::wrapMimeMultiPart() bool SMimePart::writeInputBuffer() { - SMimePrivate *d = static_cast(d_ptr.data()); + SMimePartPrivate *d = static_cast(d_ptr.data()); BIO *input = BIO_new(BIO_s_mem()); d->_input = std::unique_ptr(input); if (!d->_input) @@ -319,7 +319,7 @@ bool SMimePart::writeInputBuffer() bool SMimePart::writeMimeMessageBuffer() { - SMimePrivate *d = static_cast(d_ptr.data()); + SMimePartPrivate *d = static_cast(d_ptr.data()); QBuffer buffer; buffer.open(QBuffer::ReadWrite); diff --git a/src/smimepart_p.cpp b/src/smimepart_p.cpp index ec6499b..2706f57 100644 --- a/src/smimepart_p.cpp +++ b/src/smimepart_p.cpp @@ -2,7 +2,7 @@ using namespace SimpleMail; -SMimePrivate::~SMimePrivate() +SMimePartPrivate::~SMimePartPrivate() { _input = nullptr; _privateKey = nullptr; diff --git a/src/smimepart_p.h b/src/smimepart_p.h index f085905..a194137 100644 --- a/src/smimepart_p.h +++ b/src/smimepart_p.h @@ -42,10 +42,10 @@ template<> class std::default_delete }; namespace SimpleMail { -class SMimePrivate : public MimePartPrivate +class SMimePartPrivate : public MimePartPrivate { public: - virtual ~SMimePrivate(); + virtual ~SMimePartPrivate(); QByteArray _message; QString _keyfile; From 7ab722b722b008e71450c2d23da5204e0a0879e7 Mon Sep 17 00:00:00 2001 From: Daniel Wendt Date: Tue, 7 Jan 2025 15:18:45 +0100 Subject: [PATCH 20/25] MimeMessage: add additional constructor --- src/mimemessage.cpp | 10 ++++++++++ src/mimemessage.h | 1 + 2 files changed, 11 insertions(+) diff --git a/src/mimemessage.cpp b/src/mimemessage.cpp index ffc0eab..fc1d71f 100644 --- a/src/mimemessage.cpp +++ b/src/mimemessage.cpp @@ -135,6 +135,16 @@ bool MimeMessage::write(QIODevice *device) const return true; } +MimeMessage::MimeMessage(MimeMessagePrivate *d, bool createAutoMimeContent) + : d(d) +{ + if (createAutoMimeContent) { + d->content = std::make_shared(); + } + + d->autoMimeContentCreated = createAutoMimeContent; +} + void MimeMessage::setSender(const EmailAddress &sender) { d->sender = sender; diff --git a/src/mimemessage.h b/src/mimemessage.h index 1bf89da..11b725b 100644 --- a/src/mimemessage.h +++ b/src/mimemessage.h @@ -72,6 +72,7 @@ class SMTP_EXPORT MimeMessage bool write(QIODevice *device) const; protected: + MimeMessage(MimeMessagePrivate *d, bool createAutoMimeContent = true); QSharedDataPointer d; }; From 2ae0a443045a803b2758b7d13e48b5c81bdb0eb8 Mon Sep 17 00:00:00 2001 From: Daniel Wendt Date: Tue, 7 Jan 2025 15:34:22 +0100 Subject: [PATCH 21/25] SMimePart: refactoring, code cleanup --- src/SimpleMail | 1 - src/smimepart.cpp | 27 +++------------------------ src/smimepart.h | 10 ++++------ 3 files changed, 7 insertions(+), 31 deletions(-) diff --git a/src/SimpleMail b/src/SimpleMail index b73ff0c..03c6c4f 100644 --- a/src/SimpleMail +++ b/src/SimpleMail @@ -27,5 +27,4 @@ #include "server.h" #include "serverreply.h" #ifdef WITH_SMIME -#include "smimepart.h" #endif diff --git a/src/smimepart.cpp b/src/smimepart.cpp index c659140..18985fc 100644 --- a/src/smimepart.cpp +++ b/src/smimepart.cpp @@ -17,17 +17,14 @@ Q_LOGGING_CATEGORY(SIMPLEMAIL_SMIMEPART, "simplemail.smimepart", QtInfoMsg) using namespace SimpleMail; -SMimePart::SMimePart(MimeMessage *message) +SMimePart::SMimePart() : MimePart(new SMimePartPrivate) { initOpenSSL(); - _mimeMessage = message; - writeMimeMessageBuffer(); } SMimePart::~SMimePart() { - _mimeMessage = nullptr; } void SMimePart::setKeyFile(const QString &filename, const QString &password) @@ -56,7 +53,6 @@ bool SMimePart::sign() return ret; } - wrapMimeMultiPart(); setSignedHeader(); int flags = PKCS7_DETACHED | PKCS7_STREAM | PKCS7_BINARY; @@ -86,8 +82,6 @@ bool SMimePart::sign() if (!handleData(p7, nullptr, 0)) goto err; - _mimeMessage->addPart(std::shared_ptr(this)); - ret = true; err: if (!ret) { @@ -125,8 +119,6 @@ bool SMimePart::encrypt() if (!handleData(p7, d->_input.get(), flags)) goto err; - _mimeMessage->setContent(std::shared_ptr(this)); - ret = true; err: if (!ret) { @@ -177,8 +169,6 @@ bool SMimePart::signAndEncrypt() if (!handleData(p7, signedContent, flags)) goto err; - _mimeMessage->setContent(std::shared_ptr(this)); - ret = true; err: if (!ret) { @@ -294,17 +284,6 @@ void SMimePart::initOpenSSL() ERR_load_crypto_strings(); } -void SMimePart::wrapMimeMultiPart() -{ - auto &mimeMultiPart = *_mimeMessage->getContent(); - - if (typeid(mimeMultiPart) == typeid(MimeMultiPart)) { - MimeMultiPart *multiPartSigned = new MimeMultiPart(MimeMultiPart::Signed); - multiPartSigned->addPart(_mimeMessage->getContent()); - _mimeMessage->setContent(std::shared_ptr(multiPartSigned)); - } -} - bool SMimePart::writeInputBuffer() { SMimePartPrivate *d = static_cast(d_ptr.data()); @@ -317,13 +296,13 @@ bool SMimePart::writeInputBuffer() return true; } -bool SMimePart::writeMimeMessageBuffer() +bool SMimePart::writeMimeMessageBuffer(const std::shared_ptr &mimeParts) { SMimePartPrivate *d = static_cast(d_ptr.data()); QBuffer buffer; buffer.open(QBuffer::ReadWrite); - _mimeMessage->getContent()->write(&buffer); + mimeParts->write(&buffer); buffer.close(); d->_message = buffer.data(); diff --git a/src/smimepart.h b/src/smimepart.h index d20188d..e47fbc1 100644 --- a/src/smimepart.h +++ b/src/smimepart.h @@ -9,10 +9,10 @@ #include namespace SimpleMail { -class SMTP_EXPORT SMimePart : public MimePart +class SMimePart : public MimePart { public: - SMimePart(MimeMessage *message); + SMimePart(); virtual ~SMimePart(); void setKeyFile(const QString &filename, const QString &password); @@ -23,6 +23,8 @@ class SMTP_EXPORT SMimePart : public MimePart bool encrypt(); bool signAndEncrypt(); + bool writeMimeMessageBuffer(const std::shared_ptr &mimeParts); + private: void setSignedHeader(); void setEncryptionHeader(); @@ -32,12 +34,8 @@ class SMTP_EXPORT SMimePart : public MimePart void initOpenSSL(); - void wrapMimeMultiPart(); bool writeInputBuffer(); - bool writeMimeMessageBuffer(); bool handleData(PKCS7 *p7, BIO *dataIn, int flags); - - MimeMessage *_mimeMessage; }; } // namespace SimpleMail From b065f7358fe12e1b84eabc4072ce372eedaa7d2d Mon Sep 17 00:00:00 2001 From: Daniel Wendt Date: Tue, 7 Jan 2025 15:41:14 +0100 Subject: [PATCH 22/25] add class SMimeMessagePrivate --- src/CMakeLists.txt | 2 ++ src/smimemessage_p.cpp | 8 ++++++++ src/smimemessage_p.h | 18 ++++++++++++++++++ 3 files changed, 28 insertions(+) create mode 100644 src/smimemessage_p.cpp create mode 100644 src/smimemessage_p.h diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 3797ab3..9a0ad75 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -46,10 +46,12 @@ set(simplemailqt_HEADERS_PRIVATE if(BUILD_WITH_SMIME) add_definitions(-DWITH_SMIME) list(APPEND simplemailqt_SRC + smimemessage_p.cpp smimepart.cpp smimepart_p.cpp ) list(APPEND simplemailqt_HEADERS + smimemessage_p.h smimepart.h smimepart_p.h ) diff --git a/src/smimemessage_p.cpp b/src/smimemessage_p.cpp new file mode 100644 index 0000000..82c7191 --- /dev/null +++ b/src/smimemessage_p.cpp @@ -0,0 +1,8 @@ +#include "smimemessage_p.h" + +using namespace SimpleMail; + +SMimeMessagePrivate::SMimeMessagePrivate() +{ + _smimePart = std::make_shared(); +} diff --git a/src/smimemessage_p.h b/src/smimemessage_p.h new file mode 100644 index 0000000..805112a --- /dev/null +++ b/src/smimemessage_p.h @@ -0,0 +1,18 @@ +#ifndef SMIMEMESSAGE_P_H +#define SMIMEMESSAGE_P_H + +#include "mimemessage_p.h" +#include "smimepart.h" + +namespace SimpleMail { + +class SMimeMessagePrivate : public MimeMessagePrivate +{ +public: + SMimeMessagePrivate(); + std::shared_ptr _smimePart; +}; + +} // namespace SimpleMail + +#endif // SMIMEMESSAGE_P_H From 4e4fe65c7d878d8fff6deaa265017479f39351c4 Mon Sep 17 00:00:00 2001 From: Daniel Wendt Date: Tue, 7 Jan 2025 15:42:06 +0100 Subject: [PATCH 23/25] add class SMimeMessage --- src/CMakeLists.txt | 2 ++ src/SimpleMail | 1 + src/smimemessage.cpp | 62 ++++++++++++++++++++++++++++++++++++++++++++ src/smimemessage.h | 28 ++++++++++++++++++++ 4 files changed, 93 insertions(+) create mode 100644 src/smimemessage.cpp create mode 100644 src/smimemessage.h diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 9a0ad75..99eeb49 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -46,11 +46,13 @@ set(simplemailqt_HEADERS_PRIVATE if(BUILD_WITH_SMIME) add_definitions(-DWITH_SMIME) list(APPEND simplemailqt_SRC + smimemessage.cpp smimemessage_p.cpp smimepart.cpp smimepart_p.cpp ) list(APPEND simplemailqt_HEADERS + smimemessage.h smimemessage_p.h smimepart.h smimepart_p.h diff --git a/src/SimpleMail b/src/SimpleMail index 03c6c4f..d9e5505 100644 --- a/src/SimpleMail +++ b/src/SimpleMail @@ -27,4 +27,5 @@ #include "server.h" #include "serverreply.h" #ifdef WITH_SMIME +#include "smimemessage.h" #endif diff --git a/src/smimemessage.cpp b/src/smimemessage.cpp new file mode 100644 index 0000000..a76f10a --- /dev/null +++ b/src/smimemessage.cpp @@ -0,0 +1,62 @@ +#include "smimemessage.h" + +#include "mimemultipart.h" +#include "smimemessage_p.h" + +using namespace SimpleMail; + +SMimeMessage::SMimeMessage() + : MimeMessage(new SMimeMessagePrivate()) +{ +} + +SMimeMessage::~SMimeMessage() +{ + +} + +void SMimeMessage::setKeyFile(const QString &filename, const QString &password) +{ + SMimeMessagePrivate *dPtr = static_cast(d.data()); + dPtr->_smimePart->setKeyFile(filename, password); +} + +void SMimeMessage::setPublicKey(const QString &filename) +{ + SMimeMessagePrivate *dPtr = static_cast(d.data()); + dPtr->_smimePart->setPublicKey(filename); +} + +bool SMimeMessage::sign() +{ + SMimeMessagePrivate *dPtr = static_cast(d.data()); + dPtr->_smimePart->writeMimeMessageBuffer(this->getContent()); + + MimeMultiPart *multiPartSigned = new MimeMultiPart(MimeMultiPart::Signed); + multiPartSigned->addPart(getContent()); + setContent(std::shared_ptr(multiPartSigned)); + + bool ret = dPtr->_smimePart->sign(); + addPart(dPtr->_smimePart); + return ret; +} + +bool SMimeMessage::encrypt() +{ + SMimeMessagePrivate *dPtr = static_cast(d.data()); + dPtr->_smimePart->writeMimeMessageBuffer(this->getContent()); + bool ret = dPtr->_smimePart->encrypt(); + setContent(dPtr->_smimePart); + return ret; +} + +bool SMimeMessage::signAndEncrypt() +{ + SMimeMessagePrivate *dPtr = static_cast(d.data()); + dPtr->_smimePart->writeMimeMessageBuffer(this->getContent()); + bool ret = dPtr->_smimePart->signAndEncrypt(); + setContent(dPtr->_smimePart); + return ret; +} + + diff --git a/src/smimemessage.h b/src/smimemessage.h new file mode 100644 index 0000000..eaf6f2a --- /dev/null +++ b/src/smimemessage.h @@ -0,0 +1,28 @@ +#ifndef SMIMEMESSAGE_H +#define SMIMEMESSAGE_H + +#include "mimemessage.h" +#include "smtpexports.h" + +class QIODevice; +namespace SimpleMail { + +class SMimeMessagePrivate; +class SMTP_EXPORT SMimeMessage : public MimeMessage +{ +public: + explicit SMimeMessage(); + virtual ~SMimeMessage(); + + void setKeyFile(const QString &filename, const QString &password); + + void setPublicKey(const QString &filename); + + bool sign(); + bool encrypt(); + bool signAndEncrypt(); +}; + +} // namespace SimpleMail + +#endif // SMIMEMESSAGE_H From 6563b2afb56e6bfb737d1ccf3602d5bc36482e10 Mon Sep 17 00:00:00 2001 From: Daniel Wendt Date: Tue, 7 Jan 2025 16:11:40 +0100 Subject: [PATCH 24/25] update demo5 --- demos/demo5/demo5.cpp | 21 +++++++-------------- 1 file changed, 7 insertions(+), 14 deletions(-) diff --git a/demos/demo5/demo5.cpp b/demos/demo5/demo5.cpp index 61a57f0..d9a0b7e 100644 --- a/demos/demo5/demo5.cpp +++ b/demos/demo5/demo5.cpp @@ -24,9 +24,9 @@ int main(int argc, char *argv[]) server.setUsername(QLatin1String("your_email_address@host.com")); server.setPassword(QLatin1String("your_password")); - // Now we create a MimeMessage object. This is the email. + // Now we create a SMimeMessage object. This is the email. - MimeMessage message; + SMimeMessage message; EmailAddress sender(QLatin1String("your_email_address@host.com"), QLatin1String("Your Name")); message.setSender(sender); @@ -53,34 +53,27 @@ int main(int argc, char *argv[]) std::make_shared(std::make_shared(QLatin1String("document.pdf"))); message.addPart(document); - // Now create an SMime object - - auto smime = new SimpleMail::SMimePart(&message); - // Setup private and public key/certificate in PKCS#12 format - smime->setKeyFile(QLatin1String("your_private_key.p12"), + message.setKeyFile(QLatin1String("your_private_key.p12"), QLatin1String("your_private_key_password")); - smime->setPublicKey(QLatin1String("recipient_public_key.cert")); + message.setPublicKey(QLatin1String("recipient_public_key.cert")); // Sign the message. Only your private key is required. - // if(!smime->sign()) { + // if(!message.sign()) { // qDebug() << "Failed to create signed email"; - // delete smime; // return -3; // } // Encrypt the message. Only the recipient's public key/certificate is required. - // if(!smime->encrypt()) { + // if(!message.encrypt()) { // qDebug() << "Failed to create encrypted email"; - // delete smime; // return -3; // } // Sign and encrypt the message - if (!smime->signAndEncrypt()) { + if (!message.signAndEncrypt()) { qDebug() << "Failed to create signed and encrypted email"; - delete smime; return -3; } From 15cb842bc9f205f32d98efac02c67472f68a32b5 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Tue, 7 Jan 2025 16:02:50 +0000 Subject: [PATCH 25/25] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- demos/demo5/demo5.cpp | 2 +- src/smimemessage.cpp | 13 +++++------- src/smimepart.cpp | 47 +++++++++++++++++++++---------------------- src/smimepart_p.cpp | 8 ++++---- src/smimepart_p.h | 37 ++++++++++++++-------------------- 5 files changed, 48 insertions(+), 59 deletions(-) diff --git a/demos/demo5/demo5.cpp b/demos/demo5/demo5.cpp index d9a0b7e..a3e2c7a 100644 --- a/demos/demo5/demo5.cpp +++ b/demos/demo5/demo5.cpp @@ -56,7 +56,7 @@ int main(int argc, char *argv[]) // Setup private and public key/certificate in PKCS#12 format message.setKeyFile(QLatin1String("your_private_key.p12"), - QLatin1String("your_private_key_password")); + QLatin1String("your_private_key_password")); message.setPublicKey(QLatin1String("recipient_public_key.cert")); // Sign the message. Only your private key is required. diff --git a/src/smimemessage.cpp b/src/smimemessage.cpp index a76f10a..28f5a3c 100644 --- a/src/smimemessage.cpp +++ b/src/smimemessage.cpp @@ -12,24 +12,23 @@ SMimeMessage::SMimeMessage() SMimeMessage::~SMimeMessage() { - } void SMimeMessage::setKeyFile(const QString &filename, const QString &password) { - SMimeMessagePrivate *dPtr = static_cast(d.data()); + SMimeMessagePrivate *dPtr = static_cast(d.data()); dPtr->_smimePart->setKeyFile(filename, password); } void SMimeMessage::setPublicKey(const QString &filename) { - SMimeMessagePrivate *dPtr = static_cast(d.data()); + SMimeMessagePrivate *dPtr = static_cast(d.data()); dPtr->_smimePart->setPublicKey(filename); } bool SMimeMessage::sign() { - SMimeMessagePrivate *dPtr = static_cast(d.data()); + SMimeMessagePrivate *dPtr = static_cast(d.data()); dPtr->_smimePart->writeMimeMessageBuffer(this->getContent()); MimeMultiPart *multiPartSigned = new MimeMultiPart(MimeMultiPart::Signed); @@ -43,7 +42,7 @@ bool SMimeMessage::sign() bool SMimeMessage::encrypt() { - SMimeMessagePrivate *dPtr = static_cast(d.data()); + SMimeMessagePrivate *dPtr = static_cast(d.data()); dPtr->_smimePart->writeMimeMessageBuffer(this->getContent()); bool ret = dPtr->_smimePart->encrypt(); setContent(dPtr->_smimePart); @@ -52,11 +51,9 @@ bool SMimeMessage::encrypt() bool SMimeMessage::signAndEncrypt() { - SMimeMessagePrivate *dPtr = static_cast(d.data()); + SMimeMessagePrivate *dPtr = static_cast(d.data()); dPtr->_smimePart->writeMimeMessageBuffer(this->getContent()); bool ret = dPtr->_smimePart->signAndEncrypt(); setContent(dPtr->_smimePart); return ret; } - - diff --git a/src/smimepart.cpp b/src/smimepart.cpp index 18985fc..82d071e 100644 --- a/src/smimepart.cpp +++ b/src/smimepart.cpp @@ -29,24 +29,24 @@ SMimePart::~SMimePart() void SMimePart::setKeyFile(const QString &filename, const QString &password) { - SMimePartPrivate *d = static_cast(d_ptr.data()); - d->_keyfile = filename; - d->_password = password; + SMimePartPrivate *d = static_cast(d_ptr.data()); + d->_keyfile = filename; + d->_password = password; loadPKCS12PrivateKey(); } void SMimePart::setPublicKey(const QString &filename) { - SMimePartPrivate *d = static_cast(d_ptr.data()); - d->_publicKeyfile = filename; + SMimePartPrivate *d = static_cast(d_ptr.data()); + d->_publicKeyfile = filename; loadPKCS12PublicKey(); } bool SMimePart::sign() { - SMimePartPrivate *d = static_cast(d_ptr.data()); - bool ret = false; - PKCS7 *p7 = nullptr; + SMimePartPrivate *d = static_cast(d_ptr.data()); + bool ret = false; + PKCS7 *p7 = nullptr; if (!d->_certificate || !d->_privateKey) { qCDebug(SIMPLEMAIL_SMIMEPART) << "no certificate or private key"; @@ -94,9 +94,9 @@ bool SMimePart::sign() bool SMimePart::encrypt() { - SMimePartPrivate *d = static_cast(d_ptr.data()); - bool ret = false; - PKCS7 *p7 = nullptr; + SMimePartPrivate *d = static_cast(d_ptr.data()); + bool ret = false; + PKCS7 *p7 = nullptr; if (!d->_recipsReceiver) { qCDebug(SIMPLEMAIL_SMIMEPART) << "no public key"; @@ -130,10 +130,10 @@ bool SMimePart::encrypt() bool SMimePart::signAndEncrypt() { - SMimePartPrivate *d = static_cast(d_ptr.data()); - bool ret = false; - PKCS7 *p7 = nullptr; - BIO *signedContent = nullptr; + SMimePartPrivate *d = static_cast(d_ptr.data()); + bool ret = false; + PKCS7 *p7 = nullptr; + BIO *signedContent = nullptr; if (!d->_certificate || !d->_privateKey) { qCDebug(SIMPLEMAIL_SMIMEPART) << "no certificate or private key"; return ret; @@ -204,7 +204,7 @@ void SMimePart::setEncryptionHeader() void SMimePart::loadPKCS12PrivateKey() { - SMimePartPrivate *d = static_cast(d_ptr.data()); + SMimePartPrivate *d = static_cast(d_ptr.data()); QFile file(d->_keyfile); if (!file.exists()) return; @@ -226,8 +226,8 @@ void SMimePart::loadPKCS12PrivateKey() qCDebug(SIMPLEMAIL_SMIMEPART) << "Error reading PKCS#12 file"; } - EVP_PKEY *privateKey = nullptr; - X509 *certificate = nullptr; + EVP_PKEY *privateKey = nullptr; + X509 *certificate = nullptr; STACK_OF(X509) *certificateCA = nullptr; if (!PKCS12_parse( p12, d->_password.toStdString().c_str(), &privateKey, &certificate, &certificateCA)) { @@ -250,7 +250,7 @@ void SMimePart::loadPKCS12PrivateKey() void SMimePart::loadPKCS12PublicKey() { - SMimePartPrivate *d = static_cast(d_ptr.data()); + SMimePartPrivate *d = static_cast(d_ptr.data()); QFile file(d->_publicKeyfile); if (!file.exists()) return; @@ -268,7 +268,6 @@ void SMimePart::loadPKCS12PublicKey() X509 *publicrcert = PEM_read_bio_X509(keyBuffer, NULL, 0, NULL); BIO_free(keyBuffer); - STACK_OF(X509) *recipsReceiver = sk_X509_new_null(); if (!recipsReceiver || !sk_X509_push(recipsReceiver, publicrcert)) { @@ -286,9 +285,9 @@ void SMimePart::initOpenSSL() bool SMimePart::writeInputBuffer() { - SMimePartPrivate *d = static_cast(d_ptr.data()); - BIO *input = BIO_new(BIO_s_mem()); - d->_input = std::unique_ptr(input); + SMimePartPrivate *d = static_cast(d_ptr.data()); + BIO *input = BIO_new(BIO_s_mem()); + d->_input = std::unique_ptr(input); if (!d->_input) return false; if (!BIO_write(d->_input.get(), (void *) d->_message.data(), d->_message.length())) @@ -298,7 +297,7 @@ bool SMimePart::writeInputBuffer() bool SMimePart::writeMimeMessageBuffer(const std::shared_ptr &mimeParts) { - SMimePartPrivate *d = static_cast(d_ptr.data()); + SMimePartPrivate *d = static_cast(d_ptr.data()); QBuffer buffer; buffer.open(QBuffer::ReadWrite); diff --git a/src/smimepart_p.cpp b/src/smimepart_p.cpp index 2706f57..ff72b0c 100644 --- a/src/smimepart_p.cpp +++ b/src/smimepart_p.cpp @@ -4,9 +4,9 @@ using namespace SimpleMail; SMimePartPrivate::~SMimePartPrivate() { - _input = nullptr; - _privateKey = nullptr; - _certificate = nullptr; - _certificateCA = nullptr; + _input = nullptr; + _privateKey = nullptr; + _certificate = nullptr; + _certificateCA = nullptr; _recipsReceiver = nullptr; } diff --git a/src/smimepart_p.h b/src/smimepart_p.h index a194137..1f11b48 100644 --- a/src/smimepart_p.h +++ b/src/smimepart_p.h @@ -2,43 +2,36 @@ #define SMIMEPART_P_H #include "mimepart_p.h" -#include + #include +#include -template<> class std::default_delete +template <> +class std::default_delete { public: - void operator()(BIO *ptr) const - { - BIO_free(ptr); - } + void operator()(BIO *ptr) const { BIO_free(ptr); } }; -template<> class std::default_delete +template <> +class std::default_delete { public: - void operator()(EVP_PKEY *ptr) const - { - EVP_PKEY_free(ptr); - } + void operator()(EVP_PKEY *ptr) const { EVP_PKEY_free(ptr); } }; -template<> class std::default_delete +template <> +class std::default_delete { public: - void operator()(X509 *ptr) const - { - X509_free(ptr); - } + void operator()(X509 *ptr) const { X509_free(ptr); } }; -template<> class std::default_delete +template <> +class std::default_delete { public: - void operator()(STACK_OF(X509) *ptr) const - { - sk_X509_pop_free(ptr, X509_free); - } + void operator()(STACK_OF(X509) * ptr) const { sk_X509_pop_free(ptr, X509_free); } }; namespace SimpleMail { @@ -56,7 +49,7 @@ class SMimePartPrivate : public MimePartPrivate std::unique_ptr _privateKey = nullptr; // certificate from private Key - std::unique_ptr _certificate = nullptr; + std::unique_ptr _certificate = nullptr; std::unique_ptr _certificateCA = nullptr; // certificate from public key, used for encrypting std::unique_ptr _recipsReceiver = nullptr;