Skip to content

Commit

Permalink
Merge pull request #30 from jeremydumais/version_1_1_8
Browse files Browse the repository at this point in the history
Version 1.1.8
  • Loading branch information
jeremydumais authored Jun 9, 2024
2 parents 2a9b14a + f8d5bde commit 2e0de92
Show file tree
Hide file tree
Showing 15 changed files with 335 additions and 42 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/cmake.yml
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ jobs:


steps:
- uses: actions/checkout@v3
- uses: actions/checkout@v4

# Install OS specific dependencies
- name: Install Windows dependencies
Expand Down
24 changes: 24 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,30 @@

All notable changes to this project will be documented in this file

## [1.1.8]

## Enhancement / Bug fixes
- Some SMTP server send their list of supported extensions in multiple
buffers like Zoho Mail. The EHLO command when in uncrypted mode, now supports
receiving multiple buffers. In return, a delay of one second must be added for
each segment sent by the SMTP server. For SMTP servers that send the list of
supported extensions in a single segment like Gmail and Live, no additional
delay is added for the EHLO command. This doesn't affect the other commands.
- Now when we send an email to multiple recipients (to or cc), the recipients
appears as a single mail header instead of multiple headers. The old method was
not RFC 5322 compliant.

Before:

To: [email protected]

To: [email protected]

After:

To: [email protected], [email protected]


## [1.1.7]

### Added
Expand Down
5 changes: 4 additions & 1 deletion CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,7 @@ set(PROJECT_SOURCE_FILES ${SRC_PATH}/attachment.cpp
${SRC_PATH}/securesmtpclientbase.cpp
${SRC_PATH}/opportunisticsecuresmtpclient.cpp
${SRC_PATH}/forcedsecuresmtpclient.cpp
${SRC_PATH}/serveroptionsanalyzer.cpp
${SRC_PATH}/stringutils.cpp
${SRC_PATH}/errorresolver.cpp
${SRC_PATH}/cpp/attachment.cpp
Expand Down Expand Up @@ -148,7 +149,8 @@ if ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "MSVC")
add_library(${PROJECT_NAME} SHARED
${PROJECT_SOURCE_FILES} ${VersionFilesOutputVariable})
add_definitions(-D_CRT_SECURE_NO_WARNINGS)
target_compile_options(${PROJECT_NAME} PRIVATE /W4 /WX)
add_compile_options(/EHsc)
target_compile_options(${PROJECT_NAME} PRIVATE /W3 /WX)
target_compile_definitions(${PROJECT_NAME}
PRIVATE SMTPCLIENT_EXPORTS
INTERFACE NOMINMAX # avoid Win macro definition of min/max, use std one
Expand Down Expand Up @@ -250,6 +252,7 @@ if (BUILD_TESTING)
${TEST_SRC_PATH}/opportunisticsecuresmtpclient_unittest.cpp
${TEST_SRC_PATH}/smtpclientbase_unittest.cpp
${TEST_SRC_PATH}/smtpclient_unittest.cpp
${TEST_SRC_PATH}/serveroptionsanalyzer_unittest.cpp
${TEST_SRC_PATH}/errorresolver_unittest.cpp)

target_link_libraries(${PROJECT_UNITTEST_NAME} ${PROJECT_NAME} gtest gtest_main ${PTHREAD})
Expand Down
10 changes: 10 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,16 @@ for previous versions.

## 📰 What's new

- Version 1.1.8:
- Some SMTP server send their list of supported extensions in multiple
buffers like Zoho Mail. The EHLO command when in uncrypted mode, now supports
receiving multiple buffers. In return, a delay of one second must be added for
each segment sent by the SMTP server. For SMTP servers that send the list of
supported extensions in a single segment like Gmail and Live, no additional
delay is added for the EHLO command. This doesn't affect the other commands.
- Now when we send an email to multiple recipients (to or cc), the recipients
appears as a single mail header instead of multiple headers. The old method was
not RFC 5322 compliant.
- Version 1.1.7:
- Added support for the XOAUTH2 authentication method.
This change has been made by rcosnita (https://github.com/rcosnita).
Expand Down
2 changes: 1 addition & 1 deletion src/forcedsecuresmtpclient.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -83,7 +83,7 @@ int ForcedSecureSMTPClient::checkServerGreetings() {
unsigned int waitTime = 0;
ssize_t bytes_received = 0;
while ((bytes_received = BIO_read(getBIO(), outbuf, SERVERRESPONSE_BUFFER_LENGTH)) <= 0 && waitTime < getCommandTimeout()) {
sleep(1);
crossPlatformSleep(1);
waitTime += 1;
}
if (waitTime < getCommandTimeout()) {
Expand Down
38 changes: 29 additions & 9 deletions src/securesmtpclientbase.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
#include <openssl/x509_vfy.h>
#include <string>
#include <utility>
#include "serveroptionsanalyzer.h"
#include "smtpclienterrors.h"
#include "socketerrors.h"
#include "sslerrors.h"
Expand Down Expand Up @@ -268,8 +269,20 @@ int SecureSMTPClientBase::getServerSecureIdentification() {
if (tls_command_return_code != EHLO_SUCCESS_CODE) {
return tls_command_return_code;
}
std::string returnedOptions = getLastServerResponse();
// Check that the last returned option has no hyphen otherwise options are
// still missing from the server.
while (!ServerOptionsAnalyzer::containsAllOptions(returnedOptions)) {
int replyCode = getServerReply();
if (replyCode == -1) {
return SSL_CLIENT_INITSECURECLIENT_TIMEOUT;
} else if (replyCode != EHLO_SUCCESS_CODE) {
return replyCode;
}
returnedOptions += "\n" + std::string(getLastServerResponse());
}
// Inspect the returned values for authentication options
setAuthenticationOptions(SMTPClientBase::extractAuthenticationOptions(getLastServerResponse()));
setAuthenticationOptions(SMTPClientBase::extractAuthenticationOptions(returnedOptions.c_str()));
return EHLO_SUCCESS_CODE;
}

Expand All @@ -283,10 +296,6 @@ int SecureSMTPClientBase::sendCommand(const char *pCommand, int pErrorCode) {
}

int SecureSMTPClientBase::sendCommandWithFeedback(const char *pCommand, int pErrorCode, int pTimeoutCode) {
unsigned int waitTime {0};
int bytes_received {0};
char outbuf[SERVERRESPONSE_BUFFER_LENGTH];

// Check if we are in the TLS mode of before it
if (mBIO == nullptr) {
return sendRawCommand(pCommand, pErrorCode, pTimeoutCode);
Expand All @@ -297,8 +306,21 @@ int SecureSMTPClientBase::sendCommandWithFeedback(const char *pCommand, int pErr
return pErrorCode;
}

int serverReplyCode = getServerReply();
if (serverReplyCode != -1) {
return serverReplyCode;
}

cleanup();
return pTimeoutCode;
}

int SecureSMTPClientBase::getServerReply() {
unsigned int waitTime {0};
int bytes_received {0};
char outbuf[SERVERRESPONSE_BUFFER_LENGTH];
while ((bytes_received = BIO_read(mBIO, outbuf, SERVERRESPONSE_BUFFER_LENGTH)) <= 0 && waitTime < getCommandTimeout()) {
sleep(1);
crossPlatformSleep(1);
waitTime += 1;
}
if (waitTime < getCommandTimeout()) {
Expand All @@ -307,7 +329,5 @@ int SecureSMTPClientBase::sendCommandWithFeedback(const char *pCommand, int pErr
addCommunicationLogItem(outbuf, "s");
return extractReturnCode(outbuf);
}

cleanup();
return pTimeoutCode;
return -1;
}
1 change: 1 addition & 0 deletions src/securesmtpclientbase.h
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,7 @@ class SECURESMTPCLIENTBASE_API SecureSMTPClientBase : public SMTPClientBase {
// Methods to send commands to the server
int sendCommand(const char *pCommand, int pErrorCode) override;
int sendCommandWithFeedback(const char *pCommand, int pErrorCode, int pTimeoutCode) override;
int getServerReply() override;

private:
// Attributes used to communicate with the server
Expand Down
24 changes: 24 additions & 0 deletions src/serveroptionsanalyzer.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
#include "serveroptionsanalyzer.h"

using namespace jed_utils;

// Trim from start
bool ServerOptionsAnalyzer::containsAllOptions(const std::string &optionsStr) {
std::string sanitizedOptionsStr = [&optionsStr]() {
if (optionsStr.size() >= 2 && optionsStr.substr(optionsStr.size() - 2) == "\r\n") {
return optionsStr.substr(0, optionsStr.size() - 2);
} else {
return optionsStr;
}
}();
size_t lastLineStart = sanitizedOptionsStr.rfind("\r\n");
if (lastLineStart == std::string::npos) {
lastLineStart = 0;
} else {
lastLineStart += 2; // Move past the \r\n
}
if (sanitizedOptionsStr.substr(lastLineStart, 4) == "250 ") {
return true;
}
return false;
}
33 changes: 33 additions & 0 deletions src/serveroptionsanalyzer.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
#ifndef SERVEROPTIONSANALYZER_H
#define SERVEROPTIONSANALYZER_H

#include <string>

#ifdef _WIN32
#ifdef SMTPCLIENT_EXPORTS
#define SERVEROPTIONSANALYZER_API __declspec(dllexport)
#else
#define SERVEROPTIONSANALYZER_API __declspec(dllimport)
#endif
#else
#define SERVEROPTIONSANALYZER_API
#endif

namespace jed_utils {
/** @brief The ServerOptionsAnalyzer class provides utility fonctions to
* analyzer the SMTP options that are available by the server.
*/
class SERVEROPTIONSANALYZER_API ServerOptionsAnalyzer {
public:
/**
* @brief Indicate if the server has returned all it's available options
* or of there still a reply with other options to come.
* @param optionsStr The string that contains the option list returned by
* the SMTP server.
*/
static bool containsAllOptions(const std::string &optionsStr);
};
} // namespace jed_utils


#endif // SERVEROPTIONSANALYZER_H
4 changes: 4 additions & 0 deletions src/smtpclient.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -104,3 +104,7 @@ int SmtpClient::sendCommand(const char *pCommand, int pErrorCode) {
int SmtpClient::sendCommandWithFeedback(const char *pCommand, int pErrorCode, int pTimeoutCode) {
return sendRawCommand(pCommand, pErrorCode, pTimeoutCode);
}

int SmtpClient::getServerReply() {
return getRawCommandReply();
}
1 change: 1 addition & 0 deletions src/smtpclient.h
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@ class SMTPCLIENT_API SmtpClient : public SMTPClientBase {
// Methods to send commands to the server
int sendCommand(const char *pCommand, int pErrorCode) override;
int sendCommandWithFeedback(const char *pCommand, int pErrorCode, int pTimeoutCode) override;
int getServerReply() override;
};
} // namespace jed_utils

Expand Down
Loading

0 comments on commit 2e0de92

Please sign in to comment.