Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

add smime support #104

Open
wants to merge 25 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
25 commits
Select commit Hold shift + click to select a range
2de49db
MimePart: fix nullpointer exception in copy constructor
640kb Jul 5, 2024
2a6d2de
add flag for already Base64 encoded content
640kb Jul 11, 2024
81eff58
MimeMessage: return content in a shared pointer
640kb Jul 12, 2024
46a5eb6
add class smime for signing and/or encrypting emails
640kb Jul 16, 2024
a5d933e
extends the header for signed emails
640kb Jul 1, 2024
0772874
add missing newline for signed/encrypted mails
640kb Jul 1, 2024
04eb9eb
cmake: add option for building lib with SMIME support
640kb Jul 11, 2024
3c36435
add smime header when building with OpenSSL support
640kb Jul 11, 2024
328a551
add smime demo
640kb Jul 3, 2024
7e4a7c7
[pre-commit.ci] auto fixes from pre-commit.com hooks
pre-commit-ci[bot] Jul 23, 2024
7febacf
fixup mail header
640kb Nov 6, 2024
d772de5
SMime: move members into the private class SMimePrivate
640kb Nov 7, 2024
4f9d862
remove not needed string conversions
640kb Nov 7, 2024
35b73dc
use QBuffer on stack
640kb Nov 7, 2024
7bd9861
cmake improvements
640kb Nov 7, 2024
04987d2
use std::unique_ptr for members in class SMimePrivate
640kb Dec 3, 2024
f68ef7e
rename class SMime to SMimePart
640kb Dec 20, 2024
6d82e74
rename file smime.h/.cpp to smimepart.h/.cpp
640kb Dec 20, 2024
057a7b7
rename class SMimePrivate to SMimePartPrivate
640kb Jan 7, 2025
7ab722b
MimeMessage: add additional constructor
640kb Jan 7, 2025
2ae0a44
SMimePart: refactoring, code cleanup
640kb Jan 7, 2025
b065f73
add class SMimeMessagePrivate
640kb Jan 7, 2025
4e4fe65
add class SMimeMessage
640kb Jan 7, 2025
6563b2a
update demo5
640kb Jan 7, 2025
15cb842
[pre-commit.ci] auto fixes from pre-commit.com hooks
pre-commit-ci[bot] Jan 7, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down Expand Up @@ -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)
Expand Down
3 changes: 3 additions & 0 deletions demos/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -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)
20 changes: 20 additions & 0 deletions demos/demo5/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -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
)
89 changes: 89 additions & 0 deletions demos/demo5/demo5.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
#include "../../src/SimpleMail"

#include <QtCore>

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 SMimeMessage object. This is the email.

SMimeMessage 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<MimeText>();

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<MimeAttachment>(std::make_shared<QFile>(QLatin1String("document.pdf")));
message.addPart(document);

// Setup private and public key/certificate in PKCS#12 format

message.setKeyFile(QLatin1String("your_private_key.p12"),
QLatin1String("your_private_key_password"));
message.setPublicKey(QLatin1String("recipient_public_key.cert"));

// Sign the message. Only your private key is required.
// if(!message.sign()) {
// qDebug() << "Failed to create signed email";
// return -3;
// }

// Encrypt the message. Only the recipient's public key/certificate is required.
// if(!message.encrypt()) {
// qDebug() << "Failed to create encrypted email";
// return -3;
// }

// Sign and encrypt the message
if (!message.signAndEncrypt()) {
qDebug() << "Failed to create signed and encrypted email";
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();
}
24 changes: 24 additions & 0 deletions src/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,22 @@ set(simplemailqt_HEADERS_PRIVATE
# common.h
)

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
)
endif()

add_library(SimpleMail${PROJECT_VERSION_MAJOR}Qt${QT_VERSION_MAJOR}
${simplemailqt_SRC}
${simplemailqt_HEADERS}
Expand Down Expand Up @@ -95,6 +111,14 @@ target_link_libraries(SimpleMail${PROJECT_VERSION_MAJOR}Qt${QT_VERSION_MAJOR}
Qt::Network
)

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}
Expand Down
3 changes: 3 additions & 0 deletions src/SimpleMail
Original file line number Diff line number Diff line change
Expand Up @@ -26,3 +26,6 @@
#include "mimefile.h"
#include "server.h"
#include "serverreply.h"
#ifdef WITH_SMIME
#include "smimemessage.h"
#endif
14 changes: 12 additions & 2 deletions src/mimemessage.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -55,9 +55,9 @@ MimeMessage &MimeMessage::operator=(const MimeMessage &other)
return *this;
}

MimePart &MimeMessage::getContent()
std::shared_ptr<SimpleMail::MimePart> MimeMessage::getContent()
{
return *d->content;
return d->content;
}

void MimeMessage::setContent(const std::shared_ptr<MimePart> &content)
Expand Down Expand Up @@ -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<MimeMultiPart>();
}

d->autoMimeContentCreated = createAutoMimeContent;
}

void MimeMessage::setSender(const EmailAddress &sender)
{
d->sender = sender;
Expand Down
3 changes: 2 additions & 1 deletion src/mimemessage.h
Original file line number Diff line number Diff line change
Expand Up @@ -66,12 +66,13 @@ class SMTP_EXPORT MimeMessage
QString subject() const;
QList<std::shared_ptr<MimePart>> parts() const;

MimePart &getContent();
std::shared_ptr<MimePart> getContent();
void setContent(const std::shared_ptr<MimePart> &content);

bool write(QIODevice *device) const;

protected:
MimeMessage(MimeMessagePrivate *d, bool createAutoMimeContent = true);
QSharedDataPointer<MimeMessagePrivate> d;
};

Expand Down
1 change: 1 addition & 0 deletions src/mimemultipart.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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");

Expand Down
28 changes: 26 additions & 2 deletions src/mimepart.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ MimePart::MimePart()
}

MimePart::MimePart(const MimePart &other)
: d_ptr(new MimePartPrivate)
{
Q_D(MimePart);
d->contentCharset = other.charset();
Expand All @@ -45,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()
Expand All @@ -65,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;
}
Expand Down Expand Up @@ -228,6 +231,9 @@ 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\"; micalg=sha1");
}
if (!d->contentCharset.isEmpty()) {
headers.append("; charset=" + d->contentCharset);
}
Expand Down Expand Up @@ -274,6 +280,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);
Expand All @@ -296,8 +314,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:
Expand Down
4 changes: 4 additions & 0 deletions src/mimepart.h
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down
1 change: 1 addition & 0 deletions src/mimepart_p.h
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ class MimePartPrivate : public QSharedData
QByteArray contentType;
QByteArray contentCharset;
QByteArray contentBoundary;
bool contentIsBase64 = false;

MimeContentFormatter formatter;
MimePart::Encoding contentEncoding = MimePart::_7Bit;
Expand Down
59 changes: 59 additions & 0 deletions src/smimemessage.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
#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<SMimeMessagePrivate *>(d.data());
dPtr->_smimePart->setKeyFile(filename, password);
}

void SMimeMessage::setPublicKey(const QString &filename)
{
SMimeMessagePrivate *dPtr = static_cast<SMimeMessagePrivate *>(d.data());
dPtr->_smimePart->setPublicKey(filename);
}

bool SMimeMessage::sign()
{
SMimeMessagePrivate *dPtr = static_cast<SMimeMessagePrivate *>(d.data());
dPtr->_smimePart->writeMimeMessageBuffer(this->getContent());

MimeMultiPart *multiPartSigned = new MimeMultiPart(MimeMultiPart::Signed);
multiPartSigned->addPart(getContent());
setContent(std::shared_ptr<MimeMultiPart>(multiPartSigned));

bool ret = dPtr->_smimePart->sign();
addPart(dPtr->_smimePart);
return ret;
}

bool SMimeMessage::encrypt()
{
SMimeMessagePrivate *dPtr = static_cast<SMimeMessagePrivate *>(d.data());
dPtr->_smimePart->writeMimeMessageBuffer(this->getContent());
bool ret = dPtr->_smimePart->encrypt();
setContent(dPtr->_smimePart);
return ret;
}

bool SMimeMessage::signAndEncrypt()
{
SMimeMessagePrivate *dPtr = static_cast<SMimeMessagePrivate *>(d.data());
dPtr->_smimePart->writeMimeMessageBuffer(this->getContent());
bool ret = dPtr->_smimePart->signAndEncrypt();
setContent(dPtr->_smimePart);
return ret;
}
Loading
Loading