Skip to content

Commit 5b4c131

Browse files
committed
add support for attachment files
1 parent 9d86bb0 commit 5b4c131

5 files changed

Lines changed: 94 additions & 14 deletions

File tree

example/main.cpp

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ int main(int argc, char **argv) {
88
std::string emailUsername = brewtils::env::get("EMAIL_USERNAME");
99
std::string emailPassword = brewtils::env::get("EMAIL_PASSWORD");
1010

11-
bloomail::Client::Gmail gmail = bloomail::Client::Gmail();
11+
bloomail::Client::Gmail gmail = bloomail::Client::Gmail(true);
1212
gmail.login(emailUsername, emailPassword);
1313

1414
logger::success("Logged in successfully for " + emailUsername + "!");
@@ -18,9 +18,14 @@ int main(int argc, char **argv) {
1818
std::to_string(BLOOMAIL_VERSION_PATCH);
1919

2020
gmail.addToRecipient("[email protected]");
21+
// gmail.addCcRecipient("[email protected]");
22+
// gmail.addBccRecipient("[email protected]");
2123
gmail.setSubject("[Bloomail] Test message");
22-
gmail.setMessage("Hi!\n\nThis is a test message sent using bloomail " +
24+
gmail.setMessage("Hi!\n\nThis is a test message with some attachments sent "
25+
"using bloomail " +
2326
version + "\n\nThanks!");
27+
gmail.addAttachment("../assets/build.png");
28+
gmail.addAttachment("../assets/start.png");
2429
gmail.sendEmail();
2530

2631
logger::success("Email sent successfully, check inbox!");

include/bloomail/base_clients/i_base_client.h

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,16 @@
11
#pragma once
22

3-
#include <set>
4-
3+
#include <brewtils/os.h>
54
#include <brewtils/string.h>
6-
#include <logger/log.h>
75

86
namespace bloomail {
97

108
namespace BaseClient {
119

1210
class IBaseClient {
1311
protected:
12+
static const std::string BOUNDARY;
13+
1414
bool debug;
1515
bool isLoggedIn;
1616

@@ -24,6 +24,8 @@ class IBaseClient {
2424
std::set<std::string> ccRecipients;
2525
std::set<std::string> bccRecipients;
2626

27+
std::vector<std::pair<std::string, std::string>> attachments;
28+
2729
virtual void send(const std::string &cmd) = 0;
2830
virtual std::string recv() = 0;
2931
virtual void authenticate() = 0;
@@ -36,6 +38,8 @@ class IBaseClient {
3638
IBaseClient(bool debug = false);
3739
~IBaseClient();
3840

41+
virtual void login(const std::string &username,
42+
const std::string &password) = 0;
3943
virtual void sendEmail() = 0;
4044

4145
void addToRecipient(const std::string &recipient);
@@ -44,6 +48,10 @@ class IBaseClient {
4448

4549
void setSubject(const std::string &subject);
4650
void setMessage(const std::string &message);
51+
52+
void addAttachment(const std::string &pathToFile);
53+
void addAttachment(const std::string &pathToFile,
54+
const std::string &fileName);
4755
};
4856

4957
} // namespace BaseClient

include/bloomail/base_clients/smtp.h

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,8 @@ class Smtp : public IBaseClient {
1515
private:
1616
bool tlsConnected;
1717

18+
static const int CHUNK_SIZE;
19+
1820
bloomail::HelperClient::Tcp tcp;
1921
bloomail::HelperClient::Tls tls;
2022

@@ -24,11 +26,14 @@ class Smtp : public IBaseClient {
2426
void authenticate() override;
2527
void quit() override;
2628

29+
void sendHeaders();
30+
void sendAttachments();
31+
2732
public:
2833
Smtp(const std::string &host, int port, bool debug = false);
2934
~Smtp();
3035

31-
void login(const std::string &username, const std::string &password);
36+
void login(const std::string &username, const std::string &password) override;
3237
void sendEmail() override;
3338
};
3439

src/base_clients/i_base_client.cpp

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,8 @@
11
#include <bloomail/base_clients/i_base_client.h>
22

3+
const std::string bloomail::BaseClient::IBaseClient::BOUNDARY =
4+
"----BLOOMAIL_BOUNDARY";
5+
36
bloomail::BaseClient::IBaseClient::IBaseClient(bool debug) : debug(debug) {
47
return;
58
}
@@ -29,12 +32,30 @@ void bloomail::BaseClient::IBaseClient::setMessage(const std::string &message) {
2932
this->message = message;
3033
}
3134

35+
void bloomail::BaseClient::IBaseClient::addAttachment(
36+
const std::string &pathToFile) {
37+
std::string basePath = brewtils::os::basePath(pathToFile);
38+
return this->addAttachment(pathToFile, basePath);
39+
}
40+
41+
void bloomail::BaseClient::IBaseClient::addAttachment(
42+
const std::string &pathToFile, const std::string &fileName) {
43+
if (!brewtils::os::file::exists(pathToFile)) {
44+
logger::error("File " + pathToFile + " does not exist",
45+
"void bloomail::BaseClient::IBaseClient::addAttachment(const "
46+
"std::string& pathToFile, const std::string& fileName)");
47+
}
48+
this->attachments.emplace_back(std::make_pair(pathToFile, fileName));
49+
return;
50+
}
51+
3252
void bloomail::BaseClient::IBaseClient::clear() {
3353
this->subject.clear();
3454
this->message.clear();
3555
this->toRecipients.clear();
3656
this->ccRecipients.clear();
3757
this->bccRecipients.clear();
58+
this->attachments.clear();
3859
}
3960

4061
void bloomail::BaseClient::IBaseClient::validate() {

src/base_clients/smtp.cpp

Lines changed: 49 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
#include <bloomail/base_clients/smtp.h>
22

3+
const int bloomail::BaseClient::Smtp::CHUNK_SIZE = 57;
4+
35
bloomail::BaseClient::Smtp::Smtp(const std::string &host, int port, bool debug)
46
: tcp(host, port), tls(tcp), bloomail::BaseClient::IBaseClient(debug),
57
tlsConnected(false) {
@@ -31,6 +33,16 @@ void bloomail::BaseClient::Smtp::login(const std::string &username,
3133
this->isLoggedIn = true;
3234
}
3335

36+
void bloomail::BaseClient::Smtp::sendEmail() {
37+
this->validate();
38+
this->sendHeaders();
39+
this->sendAttachments();
40+
this->send("--" + bloomail::BaseClient::IBaseClient::BOUNDARY + "--\r\n.");
41+
this->recv();
42+
this->clear();
43+
return;
44+
}
45+
3446
void bloomail::BaseClient::Smtp::send(const std::string &cmd) {
3547
if (this->debug) {
3648
logger::debug((this->tlsConnected ? "[TLS] " : "[TCP] ") + cmd);
@@ -74,9 +86,7 @@ void bloomail::BaseClient::Smtp::quit() {
7486
this->recv();
7587
}
7688

77-
void bloomail::BaseClient::Smtp::sendEmail() {
78-
this->validate();
79-
89+
void bloomail::BaseClient::Smtp::sendHeaders() {
8090
this->send("MAIL FROM:<" + this->rawUsername + ">");
8191
this->recv();
8292

@@ -120,11 +130,42 @@ void bloomail::BaseClient::Smtp::sendEmail() {
120130
header += "\r\n";
121131
}
122132

123-
header += "Content-Type: text/plain; charset=UTF-8\r\n";
133+
header += "MIME-Version: 1.0\r\n";
134+
header += "Content-Type: multipart/mixed; boundary=" +
135+
bloomail::BaseClient::IBaseClient::BOUNDARY + "\r\n";
124136
header += "\r\n";
125-
this->send(header + brewtils::string::replace(message, "\n", "\r\n") +
126-
"\r\n.");
127-
this->recv();
137+
header += "--" + bloomail::BaseClient::IBaseClient::BOUNDARY + "\r\n";
138+
header += "Content-Type: text/plain; charset=UTF-8\r\n";
139+
header += "Content-Transfer-Encoding: 7bit\r\n\r\n";
140+
header += brewtils::string::replace(this->message, "\n", "\r\n");
141+
this->send(header);
142+
}
128143

129-
return this->clear();
144+
void bloomail::BaseClient::Smtp::sendAttachments() {
145+
for (const auto &[filePath, fileName] : this->attachments) {
146+
std::ifstream file(filePath, std::ios::binary);
147+
if (!file.is_open()) {
148+
logger::error("Could not open file " + filePath);
149+
continue;
150+
}
151+
152+
std::string attachmentHeader;
153+
attachmentHeader +=
154+
"--" + bloomail::BaseClient::IBaseClient::BOUNDARY + "\r\n";
155+
attachmentHeader +=
156+
"Content-Type: " + brewtils::os::file::getMimeType(filePath) + "\r\n";
157+
attachmentHeader +=
158+
"Content-Disposition: attachment; filename=\"" + fileName + "\"\r\n";
159+
attachmentHeader += "Content-Transfer-Encoding: base64\r\n\r\n";
160+
this->send(attachmentHeader);
161+
162+
char buffer[bloomail::BaseClient::Smtp::CHUNK_SIZE];
163+
while (file.read(buffer, sizeof(buffer)) || file.gcount() > 0) {
164+
std::string chunk(buffer, file.gcount());
165+
std::string encoded = brewtils::base64::encode(chunk);
166+
this->send(encoded);
167+
}
168+
this->send("\r\n");
169+
}
170+
return;
130171
}

0 commit comments

Comments
 (0)