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

SMTP Client improvements. #2802

Merged
merged 8 commits into from
Jun 14, 2024
6 changes: 2 additions & 4 deletions Sming/Components/Network/src/Network/MailMessage.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -48,13 +48,11 @@ MailMessage& MailMessage::setBody(String&& body, MimeType mime) noexcept

MailMessage& MailMessage::setBody(IDataSourceStream* stream, MimeType mime)
{
if(this->stream != nullptr) {
if(this->stream) {
debug_e("MailMessage::setBody: Discarding already set stream!");
delete this->stream;
this->stream = nullptr;
}

this->stream = stream;
this->stream.reset(stream);
headers[HTTP_HEADER_CONTENT_TYPE] = toString(mime);

return *this;
Expand Down
10 changes: 9 additions & 1 deletion Sming/Components/Network/src/Network/MailMessage.h
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,14 @@ class MailMessage
String subject;
String cc;

~MailMessage()
{
for(auto& attachment : attachments) {
delete attachment.headers;
delete attachment.stream;
}
}

/**
* @brief Set a header value
* @param name
Expand Down Expand Up @@ -106,7 +114,7 @@ class MailMessage
MailMessage& addAttachment(IDataSourceStream* stream, const String& mime, const String& filename = "");

private:
IDataSourceStream* stream = nullptr;
std::unique_ptr<IDataSourceStream> stream = nullptr;
HttpHeaders headers;
Vector<MultipartStream::BodyPart> attachments;
};
Expand Down
33 changes: 24 additions & 9 deletions Sming/Components/Network/src/Network/SmtpClient.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
#include <Data/Stream/Base64OutputStream.h>
#include <Data/HexString.h>
#include <Crypto/Md5.h>
#include <Network/Ssl/Factory.h>

#define ADVANCE \
{ \
Expand Down Expand Up @@ -288,7 +289,7 @@ void SmtpClient::sendMailHeaders(MailMessage* mail)

if(!mail->headers.contains(HTTP_HEADER_CONTENT_TRANSFER_ENCODING)) {
mail->headers[HTTP_HEADER_CONTENT_TRANSFER_ENCODING] = _F("quoted-printable");
mail->stream = new QuotedPrintableOutputStream(mail->stream);
mail->stream = std::make_unique<QuotedPrintableOutputStream>(mail->stream.release());
}

if(!mail->attachments.isEmpty()) {
Expand All @@ -297,13 +298,13 @@ void SmtpClient::sendMailHeaders(MailMessage* mail)
text.headers = new HttpHeaders();
(*text.headers)[HTTP_HEADER_CONTENT_TYPE] = mail->headers[HTTP_HEADER_CONTENT_TYPE];
(*text.headers)[HTTP_HEADER_CONTENT_TRANSFER_ENCODING] = mail->headers[HTTP_HEADER_CONTENT_TRANSFER_ENCODING];
text.stream = mail->stream;
text.stream = mail->stream.release();

mail->attachments.insertElementAt(text, 0);

mail->headers.remove(HTTP_HEADER_CONTENT_TRANSFER_ENCODING);
mail->headers[HTTP_HEADER_CONTENT_TYPE] = F("multipart/mixed; boundary=") + mStream->getBoundary();
mail->stream = mStream;
mail->stream.reset(mStream);
}

for(auto hdr : mail->headers) {
Expand All @@ -319,8 +320,7 @@ bool SmtpClient::sendMailBody(MailMessage* mail)
}

delete stream;
stream = mail->stream; // avoid intermediate buffers
mail->stream = nullptr;
stream = mail->stream.release(); // avoid intermediate buffers

return false;
}
Expand Down Expand Up @@ -390,8 +390,15 @@ int SmtpClient::smtpParse(char* buffer, size_t len)
RETURN_ON_ERROR(SMTP_CODE_SERVICE_READY);

if(!useSsl && (options & SMTP_OPT_STARTTLS)) {
useSsl = true;
TcpConnection::internalOnConnected(ERR_OK);
if(!enableSsl(url.Host)) {
/*
* Excerpt from RFC 3207: If,
* after having issued the STARTTLS command, the client finds out that
* some failure prevents it from actually starting a TLS handshake, then
* it SHOULD abort the connection.
*/
return 0;
}
}

sendString(F("EHLO ") + url.Host + "\r\n");
Expand Down Expand Up @@ -420,8 +427,16 @@ int SmtpClient::smtpParse(char* buffer, size_t len)
if(isLastLine) {
state = eSMTP_Ready;
if(!useSsl && (options & SMTP_OPT_STARTTLS)) {
state = eSMTP_StartTLS;
} else if(url.User && authMethods.count()) {
if(Ssl::factory != nullptr) {
state = eSMTP_StartTLS;
break;
}

bitClear(options, SMTP_OPT_STARTTLS);
debug_w("[SMTP] SSL required, no factory. Continue plain-text communication.");
}

if(url.User && authMethods.count()) {
state = eSMTP_SendAuth;
}
}
Expand Down
23 changes: 23 additions & 0 deletions Sming/Components/Network/src/Network/TcpConnection.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -423,6 +423,29 @@ err_t TcpConnection::internalOnConnected(err_t err)
return res;
}

bool TcpConnection::enableSsl(const String& hostName)
{
if(tcp == nullptr) {
return false;
}

if(tcp->state != ESTABLISHED) {
return false;
}

if(!sslCreateSession()) {
return false;
}

ssl->hostName = hostName;

useSsl = true;
if(internalOnConnected(ERR_OK) != ERR_OK) {
useSsl = false;
}
return useSsl;
}

err_t TcpConnection::internalOnReceive(pbuf* p, err_t err)
{
sleep = 0;
Expand Down
7 changes: 7 additions & 0 deletions Sming/Components/Network/src/Network/TcpConnection.h
Original file line number Diff line number Diff line change
Expand Up @@ -154,6 +154,13 @@ class TcpConnection : public IpConnection
return ssl;
}

/**
* @brief Enables Secure Socket Layer on the current connection
* @param hostName
* @retval true on success, false otherwise
*/
bool enableSsl(const String& hostName = nullptr);

protected:
void initialize(tcp_pcb* pcb);
bool internalConnect(IpAddress addr, uint16_t port);
Expand Down
2 changes: 2 additions & 0 deletions Sming/Components/Network/src/Network/Url.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,8 @@ String Url::toString() const
result += ':';
result += Password;
}

result += '@';
}

result += getHostWithPort();
Expand Down
2 changes: 1 addition & 1 deletion samples/SmtpClient/app/application.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ int onServerError(SmtpClient& client, int code, char* status)
{
debugf("Status: %s", status);

return 0; // return non-zero value to abort the connection
return -1; // return non-zero value to abort the connection
}

int onMailSent(SmtpClient& client, int code, char* status)
Expand Down
Loading