Skip to content

Commit

Permalink
Merge pull request #21 from jeremydumais/version_1_1_6
Browse files Browse the repository at this point in the history
Version 1 1 6
  • Loading branch information
jeremydumais authored Sep 3, 2023
2 parents 7725bb0 + 66d2d2b commit f58a3b3
Show file tree
Hide file tree
Showing 9 changed files with 128 additions and 21 deletions.
5 changes: 3 additions & 2 deletions .github/workflows/cmake.yml
Original file line number Diff line number Diff line change
Expand Up @@ -19,12 +19,13 @@ jobs:


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

# Install OS specific dependencies
- name: Install Windows dependencies
if: matrix.os == 'windows-latest'
run: |
Import-Module $env:ChocolateyInstall\helpers\chocolateyProfile.psm1
choco install openssl
refreshenv
Expand Down Expand Up @@ -55,7 +56,7 @@ jobs:
# Note the current convention is to use the -S and -B options here to specify source
# and build directories, but this is only available with CMake 3.13 and higher.
# The CMake binaries on the Github Actions machines are (as of this writing) 3.12
run: cmake $GITHUB_WORKSPACE -DCMAKE_BUILD_TYPE=$BUILD_TYPE -DBUILD_TESTING=ON -DOPENSSL_INCLUDE_DIRECTORY="C:/Program Files/OpenSSL-Win64/include" -DOPENSSL_LIBRARY_DIRECTORY="C:/Program Files/OpenSSL-Win64/lib/VC" -DOPENSSL_LIBRARIES_BY_MACHINE_TYPE="libssl64MD;libcrypto64MD"
run: cmake $GITHUB_WORKSPACE -DCMAKE_BUILD_TYPE=$BUILD_TYPE -DBUILD_TESTING=ON -DOPENSSL_INCLUDE_DIRECTORY="C:/Program Files/OpenSSL/include" -DOPENSSL_LIBRARY_DIRECTORY="C:/Program Files/OpenSSL/lib/VC" -DOPENSSL_LIBRARIES_BY_MACHINE_TYPE="libssl64MD;libcrypto64MD"

- name: Build
working-directory: ${{runner.workspace}}/build
Expand Down
18 changes: 17 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,7 +1,23 @@
# Changelo
# Changelog

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

## [1.1.6]

### Added

- Added support in the attachment class for Content-ID. It will be really
useful to uniquely identify and reference resources to embed in the message.
This change has been made by hesa2020 (https://github.com/hesa2020).
Many thanks!

### Enhancement

- Correction to the CMakeLists.txt, so when the repository is added as a git
submodule and linked in the parent cmake project, it will now compile.
This change has been made by hesa2020 (https://github.com/hesa2020).
Many thanks!

## [1.1.5]

### Added
Expand Down
4 changes: 2 additions & 2 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -110,15 +110,15 @@ set(PROJECT_SOURCE_FILES ${SRC_PATH}/attachment.cpp
${SRC_PATH}/cpp/smtpclient.cpp)

if (WIN32)
set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} ${CMAKE_SOURCE_DIR}/cmake/modules)
set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} ${CMAKE_CURRENT_SOURCE_DIR}/cmake/modules)
include(generate_product_version)
generate_product_version(
VersionFilesOutputVariable
NAME "CPP-SMTPClient-library"
ICON "${PATH_TO_APPLICATION_ICON}"
VERSION_MAJOR 1
VERSION_MINOR 1
VERSION_PATCH 5
VERSION_PATCH 6
VERSION_REVISION ${BUILD_REVISION}
)
endif()
Expand Down
40 changes: 36 additions & 4 deletions src/attachment.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,8 @@

using namespace jed_utils;

Attachment::Attachment(const char *pFilename, const char *pName)
: mName(nullptr), mFilename(nullptr) {
Attachment::Attachment(const char *pFilename, const char *pName, const char *pContentId)
: mName(nullptr), mFilename(nullptr), mContentId(nullptr) {
size_t pFileNameLength = strlen(pFilename);
if (pFileNameLength == 0 || StringUtils::trim(std::string(pFilename)).length() == 0) {
throw std::invalid_argument("filename");
Expand All @@ -25,32 +25,44 @@ Attachment::Attachment(const char *pFilename, const char *pName)
mName = new char[name_len+1];
strncpy(mName, pName, name_len);
mName[name_len] = '\0';

size_t contentid_len = strlen(pContentId);
mContentId = new char[contentid_len+1];
strncpy(mContentId, pContentId, contentid_len);
mContentId[contentid_len] = '\0';
}

Attachment::~Attachment() {
delete[] mName;
mName = nullptr;
delete[] mFilename;
mFilename = nullptr;
delete[] mContentId;
mContentId = nullptr;
}

// Copy constructor
Attachment::Attachment(const Attachment& other)
: mName(new char[strlen(other.mName) + 1]),
mFilename(new char[strlen(other.mFilename) + 1]) {
mFilename(new char[strlen(other.mFilename) + 1]),
mContentId(new char[strlen(other.mContentId) + 1]) {
size_t name_len = strlen(other.mName);
strncpy(mName, other.mName, name_len);
mName[name_len] = '\0';
size_t filename_len = strlen(other.mFilename);
strncpy(mFilename, other.mFilename, filename_len);
mFilename[filename_len] = '\0';
size_t contentid_len = strlen(other.mContentId);
strncpy(mContentId, other.mContentId, contentid_len);
mContentId[contentid_len] = '\0';
}

// Assignment operator
Attachment& Attachment::operator=(const Attachment& other) {
if (this != &other) {
delete[] mName;
delete[] mFilename;
delete[] mContentId;
// mName
size_t name_len = strlen(other.mName);
mName = new char[name_len + 1];
Expand All @@ -61,35 +73,51 @@ Attachment& Attachment::operator=(const Attachment& other) {
mFilename = new char[filename_len + 1];
strncpy(mFilename, other.mFilename, filename_len);
mFilename[filename_len] = '\0';
// mContentId
size_t contentid_len = strlen(other.mContentId);
mContentId = new char[contentid_len + 1];
strncpy(mContentId, other.mContentId, contentid_len);
mContentId[contentid_len] = '\0';
}
return *this;
}

// Move constructor
Attachment::Attachment(Attachment&& other) noexcept
: mName(other.mName), mFilename(other.mFilename) {
: mName(other.mName), mFilename(other.mFilename), mContentId(other.mContentId) {
// Release the data pointer from the source object so that the destructor
// does not free the memory multiple times.
other.mName = nullptr;
other.mFilename = nullptr;
other.mContentId = nullptr;
}

// Move assignement operator
Attachment& Attachment::operator=(Attachment&& other) noexcept {
if (this != &other) {
delete[] mName;
delete[] mFilename;
delete[] mContentId;
// Copy the data pointer and its length from the source object.
mName = other.mName;
mFilename = other.mFilename;
mContentId = other.mContentId;
// Release the data pointer from the source object so that
// the destructor does not free the memory multiple times.
other.mName = nullptr;
other.mFilename = nullptr;
other.mContentId = nullptr;
}
return *this;
}

void Attachment::setContentId(const char * pContentId) {
size_t contentid_len = strlen(pContentId);
mContentId = new char[contentid_len + 1];
strncpy(mContentId, pContentId, contentid_len);
mContentId[contentid_len] = '\0';
}

const char *Attachment::getName() const {
return mName;
}
Expand All @@ -98,6 +126,10 @@ const char *Attachment::getFilename() const {
return mFilename;
}

const char *Attachment::getContentId() const {
return mContentId;
}

const char *Attachment::getBase64EncodedFile() const {
// Open the file
std::ifstream in(mFilename, std::ios::in | std::ios::binary);
Expand Down
9 changes: 8 additions & 1 deletion src/attachment.h
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ class ATTACHMENT_API Attachment {
* @param pName The display name of the file that will appear in
* the mail content
*/
explicit Attachment(const char *pFilename, const char *pName = "");
explicit Attachment(const char *pFilename, const char *pName = "", const char *pContentId = "");

/** Destructor of the Attachment */
virtual ~Attachment();
Expand All @@ -44,12 +44,18 @@ class ATTACHMENT_API Attachment {
/** Attachment move assignment operator. */
Attachment& operator=(Attachment&& other) noexcept;

/** Set the attachment content id. */
void setContentId(const char * pContentId);

/** Return the display name. */
const char *getName() const;

/** Return the file name including the path. */
const char *getFilename() const;

/** Return the attachment content id. */
const char *getContentId() const;

/** Return the base64 representation of the file content. */
const char *getBase64EncodedFile() const;

Expand All @@ -62,6 +68,7 @@ class ATTACHMENT_API Attachment {
Attachment() = default;
char *mName;
char *mFilename;
char *mContentId;
};
} // namespace jed_utils

Expand Down
15 changes: 12 additions & 3 deletions src/cpp/attachment.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,12 @@

using namespace jed_utils::cpp;

Attachment::Attachment(const std::string &pFilename, const std::string &pName)
: jed_utils::Attachment(pFilename.c_str(), pName.c_str()) {
Attachment::Attachment(const std::string &pFilename, const std::string &pName, const std::string &pContentId)
: jed_utils::Attachment(pFilename.c_str(), pName.c_str(), pContentId.c_str()) {
}

void Attachment::setContentId(std::string pContentId) {
return jed_utils::Attachment::setContentId(pContentId.c_str());
}

std::string Attachment::getName() const {
Expand All @@ -14,6 +18,10 @@ std::string Attachment::getFilename() const {
return jed_utils::Attachment::getFilename();
}

std::string Attachment::getContentId() const {
return jed_utils::Attachment::getContentId();
}

std::string Attachment::getBase64EncodedFile() const {
const char *retval = jed_utils::Attachment::getBase64EncodedFile();
return retval == nullptr ? "" : retval;
Expand All @@ -25,6 +33,7 @@ std::string Attachment::getMimeType() const {

jed_utils::Attachment Attachment::toStdAttachment() const {
return jed_utils::Attachment(jed_utils::Attachment::getFilename(),
jed_utils::Attachment::getName());
jed_utils::Attachment::getName(),
jed_utils::Attachment::getContentId());
}

8 changes: 7 additions & 1 deletion src/cpp/attachment.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ class CPP_ATTACHMENT_API Attachment : private jed_utils::Attachment {
* @param pName The display name of the file that will appear in
* the mail content
*/
explicit Attachment(const std::string &pFilename, const std::string &pName = "");
explicit Attachment(const std::string &pFilename, const std::string &pName = "", const std::string &pContentId = "");

/** Destructor of the Attachment */
~Attachment() override = default;
Expand All @@ -46,12 +46,18 @@ class CPP_ATTACHMENT_API Attachment : private jed_utils::Attachment {
/** Attachment move assignment operator. */
Attachment& operator=(Attachment&& other) noexcept = default;

/** Set the attachment content id. */
void setContentId(std::string pContentId);

/** Return the display name. */
std::string getName() const;

/** Return the file name including the path. */
std::string getFilename() const;

/** Return the attachment content id. */
std::string getContentId() const;

/** Return the base64 representation of the file content. */
std::string getBase64EncodedFile() const;

Expand Down
10 changes: 8 additions & 2 deletions src/smtpclientbase.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -778,7 +778,7 @@ int SMTPClientBase::setMailHeaders(const Message &pMsg) {
}

// Content-Type
std::string content_type { "Content-Type: multipart/mixed; boundary=sep\r\n\r\n" };
std::string content_type { "Content-Type: multipart/related; boundary=sep\r\n\r\n" };
addCommunicationLogItem(content_type.c_str());
int header_content_type_ret_code = (*this.*sendCommandPtr)(content_type.c_str(), CLIENT_SENDMAIL_HEADERCONTENTTYPE_ERROR);
if (header_content_type_ret_code != 0) {
Expand Down Expand Up @@ -886,7 +886,13 @@ std::string SMTPClientBase::createAttachmentsText(const std::vector<Attachment*>
for (const auto &item : pAttachments) {
retval += "\r\n--sep\r\n";
retval += "Content-Type: " + std::string(item->getMimeType()) + "; file=\"" + std::string(item->getName()) + "\"\r\n";
retval += "Content-Disposition: Inline; filename=\"" + std::string(item->getName()) + "\"\r\n";
if (item->getContentId() != nullptr && std::string(item->getContentId()).size() > 0) {
retval += "X-Attachment-Id: " + std::string(item->getContentId()) + "\r\n";
retval += "Content-ID: <" + std::string(item->getContentId()) + ">\r\n";
retval += "Content-Disposition: attachment; filename=\"" + std::string(item->getName()) + "\"\r\n";
} else {
retval += "Content-Disposition: Inline; filename=\"" + std::string(item->getName()) + "\"\r\n";
}
retval += "Content-Transfer-Encoding: base64\r\n\r\n";
retval += std::string((item->getBase64EncodedFile() != nullptr ? item->getBase64EncodedFile() : ""));
}
Expand Down
40 changes: 35 additions & 5 deletions test/smtpclient_unittest/attachment_unittest.cpp
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
#include "gtest/gtest.h"
#include <gtest/gtest.h>
#include "../../src/attachment.h"
#include "../../src/cpp/attachment.hpp"
Expand Down Expand Up @@ -40,38 +41,67 @@ TYPED_TEST(MultiAttachmentFixture, Constructor_ValidParam) {
TypeParam att1("test.png", "");
}

TYPED_TEST(MultiAttachmentFixture, Constructor_ValidParamAndCID) {
TypeParam att1("test.png", "", "CID1@localhost");
}

TYPED_TEST(MultiAttachmentFixture, CopyConstructor_AttachmentCopyConstructorValid) {
TypeParam att1("test.png", "Test");
TypeParam att1("test.png", "Test", "CID1@localhost");
TypeParam att2(att1);
ASSERT_EQ("test.png", std::string(att1.getFilename()));
ASSERT_EQ("Test", std::string(att1.getName()));
ASSERT_EQ("CID1@localhost", std::string(att1.getContentId()));
ASSERT_EQ("test.png", std::string(att2.getFilename()));
ASSERT_EQ("Test", std::string(att2.getName()));
ASSERT_EQ("CID1@localhost", std::string(att2.getContentId()));
}

TYPED_TEST(MultiAttachmentFixture, CopyAssignment_AttachmentCopyAssignmentValid) {
TypeParam att1("test.png", "123");
TypeParam att2("aaa.png", "bbb");
TypeParam att1("test.png", "123", "CID1@localhost");
TypeParam att2("aaa.png", "bbb", "CID2@localhost");
att2 = att1;
ASSERT_EQ("test.png", std::string(att1.getFilename()));
ASSERT_EQ("123", std::string(att1.getName()));
ASSERT_EQ("CID1@localhost", std::string(att1.getContentId()));
ASSERT_EQ("test.png", std::string(att2.getFilename()));
ASSERT_EQ("123", std::string(att2.getName()));
ASSERT_EQ("CID1@localhost", std::string(att2.getContentId()));
}

TYPED_TEST(MultiAttachmentFixture, MoveConstructor_AttachmentMoveConstructorValid) {
TypeParam att1("test.png", "123");
TypeParam att1("test.png", "123", "CID1@localhost");
TypeParam att2(std::move(att1));
ASSERT_EQ("test.png", std::string(att2.getFilename()));
ASSERT_EQ("123", std::string(att2.getName()));
ASSERT_EQ("CID1@localhost", std::string(att2.getContentId()));
}

TYPED_TEST(MultiAttachmentFixture, MoveAssignment_AttachmentMoveAssignmentValid) {
TypeParam att1("test.png", "123");
TypeParam att1("test.png", "123", "CID1@localhost");
TypeParam att2("aaa.png", "bbb");
att2 = std::move(att1);
ASSERT_EQ("test.png", std::string(att2.getFilename()));
ASSERT_EQ("123", std::string(att2.getName()));
ASSERT_EQ("CID1@localhost", std::string(att2.getContentId()));
}

TYPED_TEST(MultiAttachmentFixture, getContentId_with_test_at_localhost_ReturnValid) {
TypeParam att("test.png", "test", "test@localhost");
ASSERT_EQ("test@localhost", std::string(att.getContentId()));
}

TYPED_TEST(MultiAttachmentFixture, setContentId_with_test2_at_localhost_ReturnValid) {
TypeParam att("test.png", "test", "test@localhost");
ASSERT_EQ("test@localhost", std::string(att.getContentId()));
att.setContentId("test2@localhost");
ASSERT_EQ("test2@localhost", std::string(att.getContentId()));
}

TYPED_TEST(MultiAttachmentFixture, setContentId_with_empty_ReturnValid) {
TypeParam att("test.png", "test", "test@localhost");
ASSERT_EQ("test@localhost", std::string(att.getContentId()));
att.setContentId("");
ASSERT_EQ("", std::string(att.getContentId()));
}

TYPED_TEST(MultiAttachmentFixture, getmimetype_images_png) {
Expand Down

0 comments on commit f58a3b3

Please sign in to comment.