Skip to content

Commit f58a3b3

Browse files
authored
Merge pull request #21 from jeremydumais/version_1_1_6
Version 1 1 6
2 parents 7725bb0 + 66d2d2b commit f58a3b3

File tree

9 files changed

+128
-21
lines changed

9 files changed

+128
-21
lines changed

.github/workflows/cmake.yml

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,12 +19,13 @@ jobs:
1919

2020

2121
steps:
22-
- uses: actions/checkout@v2
22+
- uses: actions/checkout@v3
2323

2424
# Install OS specific dependencies
2525
- name: Install Windows dependencies
2626
if: matrix.os == 'windows-latest'
2727
run: |
28+
Import-Module $env:ChocolateyInstall\helpers\chocolateyProfile.psm1
2829
choco install openssl
2930
refreshenv
3031
@@ -55,7 +56,7 @@ jobs:
5556
# Note the current convention is to use the -S and -B options here to specify source
5657
# and build directories, but this is only available with CMake 3.13 and higher.
5758
# The CMake binaries on the Github Actions machines are (as of this writing) 3.12
58-
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"
59+
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"
5960

6061
- name: Build
6162
working-directory: ${{runner.workspace}}/build

CHANGELOG.md

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,23 @@
1-
# Changelo
1+
# Changelog
22

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

5+
## [1.1.6]
6+
7+
### Added
8+
9+
- Added support in the attachment class for Content-ID. It will be really
10+
useful to uniquely identify and reference resources to embed in the message.
11+
This change has been made by hesa2020 (https://github.com/hesa2020).
12+
Many thanks!
13+
14+
### Enhancement
15+
16+
- Correction to the CMakeLists.txt, so when the repository is added as a git
17+
submodule and linked in the parent cmake project, it will now compile.
18+
This change has been made by hesa2020 (https://github.com/hesa2020).
19+
Many thanks!
20+
521
## [1.1.5]
622

723
### Added

CMakeLists.txt

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -110,15 +110,15 @@ set(PROJECT_SOURCE_FILES ${SRC_PATH}/attachment.cpp
110110
${SRC_PATH}/cpp/smtpclient.cpp)
111111

112112
if (WIN32)
113-
set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} ${CMAKE_SOURCE_DIR}/cmake/modules)
113+
set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} ${CMAKE_CURRENT_SOURCE_DIR}/cmake/modules)
114114
include(generate_product_version)
115115
generate_product_version(
116116
VersionFilesOutputVariable
117117
NAME "CPP-SMTPClient-library"
118118
ICON "${PATH_TO_APPLICATION_ICON}"
119119
VERSION_MAJOR 1
120120
VERSION_MINOR 1
121-
VERSION_PATCH 5
121+
VERSION_PATCH 6
122122
VERSION_REVISION ${BUILD_REVISION}
123123
)
124124
endif()

src/attachment.cpp

Lines changed: 36 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,8 @@
99

1010
using namespace jed_utils;
1111

12-
Attachment::Attachment(const char *pFilename, const char *pName)
13-
: mName(nullptr), mFilename(nullptr) {
12+
Attachment::Attachment(const char *pFilename, const char *pName, const char *pContentId)
13+
: mName(nullptr), mFilename(nullptr), mContentId(nullptr) {
1414
size_t pFileNameLength = strlen(pFilename);
1515
if (pFileNameLength == 0 || StringUtils::trim(std::string(pFilename)).length() == 0) {
1616
throw std::invalid_argument("filename");
@@ -25,32 +25,44 @@ Attachment::Attachment(const char *pFilename, const char *pName)
2525
mName = new char[name_len+1];
2626
strncpy(mName, pName, name_len);
2727
mName[name_len] = '\0';
28+
29+
size_t contentid_len = strlen(pContentId);
30+
mContentId = new char[contentid_len+1];
31+
strncpy(mContentId, pContentId, contentid_len);
32+
mContentId[contentid_len] = '\0';
2833
}
2934

3035
Attachment::~Attachment() {
3136
delete[] mName;
3237
mName = nullptr;
3338
delete[] mFilename;
3439
mFilename = nullptr;
40+
delete[] mContentId;
41+
mContentId = nullptr;
3542
}
3643

3744
// Copy constructor
3845
Attachment::Attachment(const Attachment& other)
3946
: mName(new char[strlen(other.mName) + 1]),
40-
mFilename(new char[strlen(other.mFilename) + 1]) {
47+
mFilename(new char[strlen(other.mFilename) + 1]),
48+
mContentId(new char[strlen(other.mContentId) + 1]) {
4149
size_t name_len = strlen(other.mName);
4250
strncpy(mName, other.mName, name_len);
4351
mName[name_len] = '\0';
4452
size_t filename_len = strlen(other.mFilename);
4553
strncpy(mFilename, other.mFilename, filename_len);
4654
mFilename[filename_len] = '\0';
55+
size_t contentid_len = strlen(other.mContentId);
56+
strncpy(mContentId, other.mContentId, contentid_len);
57+
mContentId[contentid_len] = '\0';
4758
}
4859

4960
// Assignment operator
5061
Attachment& Attachment::operator=(const Attachment& other) {
5162
if (this != &other) {
5263
delete[] mName;
5364
delete[] mFilename;
65+
delete[] mContentId;
5466
// mName
5567
size_t name_len = strlen(other.mName);
5668
mName = new char[name_len + 1];
@@ -61,35 +73,51 @@ Attachment& Attachment::operator=(const Attachment& other) {
6173
mFilename = new char[filename_len + 1];
6274
strncpy(mFilename, other.mFilename, filename_len);
6375
mFilename[filename_len] = '\0';
76+
// mContentId
77+
size_t contentid_len = strlen(other.mContentId);
78+
mContentId = new char[contentid_len + 1];
79+
strncpy(mContentId, other.mContentId, contentid_len);
80+
mContentId[contentid_len] = '\0';
6481
}
6582
return *this;
6683
}
6784

6885
// Move constructor
6986
Attachment::Attachment(Attachment&& other) noexcept
70-
: mName(other.mName), mFilename(other.mFilename) {
87+
: mName(other.mName), mFilename(other.mFilename), mContentId(other.mContentId) {
7188
// Release the data pointer from the source object so that the destructor
7289
// does not free the memory multiple times.
7390
other.mName = nullptr;
7491
other.mFilename = nullptr;
92+
other.mContentId = nullptr;
7593
}
7694

7795
// Move assignement operator
7896
Attachment& Attachment::operator=(Attachment&& other) noexcept {
7997
if (this != &other) {
8098
delete[] mName;
8199
delete[] mFilename;
100+
delete[] mContentId;
82101
// Copy the data pointer and its length from the source object.
83102
mName = other.mName;
84103
mFilename = other.mFilename;
104+
mContentId = other.mContentId;
85105
// Release the data pointer from the source object so that
86106
// the destructor does not free the memory multiple times.
87107
other.mName = nullptr;
88108
other.mFilename = nullptr;
109+
other.mContentId = nullptr;
89110
}
90111
return *this;
91112
}
92113

114+
void Attachment::setContentId(const char * pContentId) {
115+
size_t contentid_len = strlen(pContentId);
116+
mContentId = new char[contentid_len + 1];
117+
strncpy(mContentId, pContentId, contentid_len);
118+
mContentId[contentid_len] = '\0';
119+
}
120+
93121
const char *Attachment::getName() const {
94122
return mName;
95123
}
@@ -98,6 +126,10 @@ const char *Attachment::getFilename() const {
98126
return mFilename;
99127
}
100128

129+
const char *Attachment::getContentId() const {
130+
return mContentId;
131+
}
132+
101133
const char *Attachment::getBase64EncodedFile() const {
102134
// Open the file
103135
std::ifstream in(mFilename, std::ios::in | std::ios::binary);

src/attachment.h

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ class ATTACHMENT_API Attachment {
2727
* @param pName The display name of the file that will appear in
2828
* the mail content
2929
*/
30-
explicit Attachment(const char *pFilename, const char *pName = "");
30+
explicit Attachment(const char *pFilename, const char *pName = "", const char *pContentId = "");
3131

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

47+
/** Set the attachment content id. */
48+
void setContentId(const char * pContentId);
49+
4750
/** Return the display name. */
4851
const char *getName() const;
4952

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

56+
/** Return the attachment content id. */
57+
const char *getContentId() const;
58+
5359
/** Return the base64 representation of the file content. */
5460
const char *getBase64EncodedFile() const;
5561

@@ -62,6 +68,7 @@ class ATTACHMENT_API Attachment {
6268
Attachment() = default;
6369
char *mName;
6470
char *mFilename;
71+
char *mContentId;
6572
};
6673
} // namespace jed_utils
6774

src/cpp/attachment.cpp

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,12 @@
22

33
using namespace jed_utils::cpp;
44

5-
Attachment::Attachment(const std::string &pFilename, const std::string &pName)
6-
: jed_utils::Attachment(pFilename.c_str(), pName.c_str()) {
5+
Attachment::Attachment(const std::string &pFilename, const std::string &pName, const std::string &pContentId)
6+
: jed_utils::Attachment(pFilename.c_str(), pName.c_str(), pContentId.c_str()) {
7+
}
8+
9+
void Attachment::setContentId(std::string pContentId) {
10+
return jed_utils::Attachment::setContentId(pContentId.c_str());
711
}
812

913
std::string Attachment::getName() const {
@@ -14,6 +18,10 @@ std::string Attachment::getFilename() const {
1418
return jed_utils::Attachment::getFilename();
1519
}
1620

21+
std::string Attachment::getContentId() const {
22+
return jed_utils::Attachment::getContentId();
23+
}
24+
1725
std::string Attachment::getBase64EncodedFile() const {
1826
const char *retval = jed_utils::Attachment::getBase64EncodedFile();
1927
return retval == nullptr ? "" : retval;
@@ -25,6 +33,7 @@ std::string Attachment::getMimeType() const {
2533

2634
jed_utils::Attachment Attachment::toStdAttachment() const {
2735
return jed_utils::Attachment(jed_utils::Attachment::getFilename(),
28-
jed_utils::Attachment::getName());
36+
jed_utils::Attachment::getName(),
37+
jed_utils::Attachment::getContentId());
2938
}
3039

src/cpp/attachment.hpp

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ class CPP_ATTACHMENT_API Attachment : private jed_utils::Attachment {
2929
* @param pName The display name of the file that will appear in
3030
* the mail content
3131
*/
32-
explicit Attachment(const std::string &pFilename, const std::string &pName = "");
32+
explicit Attachment(const std::string &pFilename, const std::string &pName = "", const std::string &pContentId = "");
3333

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

49+
/** Set the attachment content id. */
50+
void setContentId(std::string pContentId);
51+
4952
/** Return the display name. */
5053
std::string getName() const;
5154

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

58+
/** Return the attachment content id. */
59+
std::string getContentId() const;
60+
5561
/** Return the base64 representation of the file content. */
5662
std::string getBase64EncodedFile() const;
5763

src/smtpclientbase.cpp

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -778,7 +778,7 @@ int SMTPClientBase::setMailHeaders(const Message &pMsg) {
778778
}
779779

780780
// Content-Type
781-
std::string content_type { "Content-Type: multipart/mixed; boundary=sep\r\n\r\n" };
781+
std::string content_type { "Content-Type: multipart/related; boundary=sep\r\n\r\n" };
782782
addCommunicationLogItem(content_type.c_str());
783783
int header_content_type_ret_code = (*this.*sendCommandPtr)(content_type.c_str(), CLIENT_SENDMAIL_HEADERCONTENTTYPE_ERROR);
784784
if (header_content_type_ret_code != 0) {
@@ -886,7 +886,13 @@ std::string SMTPClientBase::createAttachmentsText(const std::vector<Attachment*>
886886
for (const auto &item : pAttachments) {
887887
retval += "\r\n--sep\r\n";
888888
retval += "Content-Type: " + std::string(item->getMimeType()) + "; file=\"" + std::string(item->getName()) + "\"\r\n";
889-
retval += "Content-Disposition: Inline; filename=\"" + std::string(item->getName()) + "\"\r\n";
889+
if (item->getContentId() != nullptr && std::string(item->getContentId()).size() > 0) {
890+
retval += "X-Attachment-Id: " + std::string(item->getContentId()) + "\r\n";
891+
retval += "Content-ID: <" + std::string(item->getContentId()) + ">\r\n";
892+
retval += "Content-Disposition: attachment; filename=\"" + std::string(item->getName()) + "\"\r\n";
893+
} else {
894+
retval += "Content-Disposition: Inline; filename=\"" + std::string(item->getName()) + "\"\r\n";
895+
}
890896
retval += "Content-Transfer-Encoding: base64\r\n\r\n";
891897
retval += std::string((item->getBase64EncodedFile() != nullptr ? item->getBase64EncodedFile() : ""));
892898
}

test/smtpclient_unittest/attachment_unittest.cpp

Lines changed: 35 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
#include "gtest/gtest.h"
12
#include <gtest/gtest.h>
23
#include "../../src/attachment.h"
34
#include "../../src/cpp/attachment.hpp"
@@ -40,38 +41,67 @@ TYPED_TEST(MultiAttachmentFixture, Constructor_ValidParam) {
4041
TypeParam att1("test.png", "");
4142
}
4243

44+
TYPED_TEST(MultiAttachmentFixture, Constructor_ValidParamAndCID) {
45+
TypeParam att1("test.png", "", "CID1@localhost");
46+
}
47+
4348
TYPED_TEST(MultiAttachmentFixture, CopyConstructor_AttachmentCopyConstructorValid) {
44-
TypeParam att1("test.png", "Test");
49+
TypeParam att1("test.png", "Test", "CID1@localhost");
4550
TypeParam att2(att1);
4651
ASSERT_EQ("test.png", std::string(att1.getFilename()));
4752
ASSERT_EQ("Test", std::string(att1.getName()));
53+
ASSERT_EQ("CID1@localhost", std::string(att1.getContentId()));
4854
ASSERT_EQ("test.png", std::string(att2.getFilename()));
4955
ASSERT_EQ("Test", std::string(att2.getName()));
56+
ASSERT_EQ("CID1@localhost", std::string(att2.getContentId()));
5057
}
5158

5259
TYPED_TEST(MultiAttachmentFixture, CopyAssignment_AttachmentCopyAssignmentValid) {
53-
TypeParam att1("test.png", "123");
54-
TypeParam att2("aaa.png", "bbb");
60+
TypeParam att1("test.png", "123", "CID1@localhost");
61+
TypeParam att2("aaa.png", "bbb", "CID2@localhost");
5562
att2 = att1;
5663
ASSERT_EQ("test.png", std::string(att1.getFilename()));
5764
ASSERT_EQ("123", std::string(att1.getName()));
65+
ASSERT_EQ("CID1@localhost", std::string(att1.getContentId()));
5866
ASSERT_EQ("test.png", std::string(att2.getFilename()));
5967
ASSERT_EQ("123", std::string(att2.getName()));
68+
ASSERT_EQ("CID1@localhost", std::string(att2.getContentId()));
6069
}
6170

6271
TYPED_TEST(MultiAttachmentFixture, MoveConstructor_AttachmentMoveConstructorValid) {
63-
TypeParam att1("test.png", "123");
72+
TypeParam att1("test.png", "123", "CID1@localhost");
6473
TypeParam att2(std::move(att1));
6574
ASSERT_EQ("test.png", std::string(att2.getFilename()));
6675
ASSERT_EQ("123", std::string(att2.getName()));
76+
ASSERT_EQ("CID1@localhost", std::string(att2.getContentId()));
6777
}
6878

6979
TYPED_TEST(MultiAttachmentFixture, MoveAssignment_AttachmentMoveAssignmentValid) {
70-
TypeParam att1("test.png", "123");
80+
TypeParam att1("test.png", "123", "CID1@localhost");
7181
TypeParam att2("aaa.png", "bbb");
7282
att2 = std::move(att1);
7383
ASSERT_EQ("test.png", std::string(att2.getFilename()));
7484
ASSERT_EQ("123", std::string(att2.getName()));
85+
ASSERT_EQ("CID1@localhost", std::string(att2.getContentId()));
86+
}
87+
88+
TYPED_TEST(MultiAttachmentFixture, getContentId_with_test_at_localhost_ReturnValid) {
89+
TypeParam att("test.png", "test", "test@localhost");
90+
ASSERT_EQ("test@localhost", std::string(att.getContentId()));
91+
}
92+
93+
TYPED_TEST(MultiAttachmentFixture, setContentId_with_test2_at_localhost_ReturnValid) {
94+
TypeParam att("test.png", "test", "test@localhost");
95+
ASSERT_EQ("test@localhost", std::string(att.getContentId()));
96+
att.setContentId("test2@localhost");
97+
ASSERT_EQ("test2@localhost", std::string(att.getContentId()));
98+
}
99+
100+
TYPED_TEST(MultiAttachmentFixture, setContentId_with_empty_ReturnValid) {
101+
TypeParam att("test.png", "test", "test@localhost");
102+
ASSERT_EQ("test@localhost", std::string(att.getContentId()));
103+
att.setContentId("");
104+
ASSERT_EQ("", std::string(att.getContentId()));
75105
}
76106

77107
TYPED_TEST(MultiAttachmentFixture, getmimetype_images_png) {

0 commit comments

Comments
 (0)