join 1.0
lightweight network framework library
Loading...
Searching...
No Matches
smtpclient.hpp
Go to the documentation of this file.
1
25#ifndef JOIN_SERVICES_SMTPCLIENT_HPP
26#define JOIN_SERVICES_SMTPCLIENT_HPP
27
28// libjoin.
29#include <join/socketstream.hpp>
30#include <join/mailmessage.hpp>
31#include <join/resolver.hpp>
32#include <join/base64.hpp>
33
34namespace join
35{
39 template <class Protocol>
41 {
42 public:
43 using Endpoint = typename Protocol::Endpoint;
44 using Stream = typename Protocol::Stream;
45
51 BasicSmtpClient (const char* host, uint16_t port = 25)
52 : _host (host)
53 , _port (port)
54 {
55 }
56
62 BasicSmtpClient (const std::string& host, uint16_t port = 25)
63 : BasicSmtpClient (host.c_str (), port)
64 {
65 }
66
71 BasicSmtpClient (const BasicSmtpClient& other) = delete;
72
79
85 : _stream (std::move (other._stream))
86 , _host (std::move (other._host))
87 , _port (other._port)
88 , _login (std::move (other._login))
89 , _password (std::move (other._password))
90 {
91 }
92
99 {
100 this->_stream = std::move (other._stream);
101 this->_host = std::move (other._host);
102 this->_port = other._port;
103 this->_login = std::move (other._login);
104 this->_password = std::move (other._password);
105 return *this;
106 }
107
111 virtual ~BasicSmtpClient () = default;
112
117 virtual std::string scheme () const
118 {
119 return "smtp";
120 }
121
126 const std::string& host () const
127 {
128 return this->_host;
129 }
130
135 uint16_t port () const
136 {
137 return this->_port;
138 }
139
144 std::string authority () const
145 {
146 std::string auth;
147
148 if (IpAddress::isIpv6Address (this->host ()))
149 {
150 auth += "[" + this->host () + "]";
151 }
152 else
153 {
154 auth += this->host ();
155 }
156
157 if (this->port () != Resolver::resolveService (this->scheme ()))
158 {
159 auth += ":" + std::to_string (this->port ());
160 }
161
162 return auth;
163 }
164
169 std::string url () const
170 {
171 return this->scheme () + "://" + this->authority ();
172 }
173
179 void credentials (const std::string& login, const std::string& password)
180 {
181 this->_login = login;
182 this->_password = password;
183 }
184
191 int setCertificate (const std::string& cert, const std::string& key = "")
192 {
193 return this->_stream.setCertificate (cert, key);
194 }
195
201 int setCaPath (const std::string& caPath)
202 {
203 return this->_stream.setCaPath (caPath);
204 }
205
211 int setCaFile (const std::string& caFile)
212 {
213 return this->_stream.setCaFile (caFile);
214 }
215
221 void setVerify (bool verify, int depth = -1)
222 {
223 this->_stream.setVerify (verify, depth);
224 }
225
231 int setCipher (const std::string& cipher)
232 {
233 return this->_stream.setCipher (cipher);
234 }
235
241 int setCipher_1_3 (const std::string& cipher)
242 {
243 return this->_stream.setCipher_1_3 (cipher);
244 }
245
251 int send (const MailMessage& mail)
252 {
253 Endpoint endpoint{Resolver::resolveHost (this->host ()), this->port ()};
254 endpoint.hostname (this->host ());
255
256 if (this->connect (endpoint) == -1)
257 {
258 this->close ();
259 return -1;
260 }
261
262 if (this->greeting () == -1)
263 {
264 this->close ();
265 return -1;
266 }
267
268 std::vector<std::string> replies;
269
270 for (;;)
271 {
272 replies.clear ();
273
274 if (this->initialize (replies) == -1)
275 {
276 this->close ();
277 return -1;
278 }
279
280 if (std::find (replies.begin (), replies.end (), "STARTTLS") != replies.end ())
281 {
282 if (this->startTls () == -1)
283 {
284 this->close ();
285 return -1;
286 }
287 continue;
288 }
289
290 break;
291 }
292
293 auto auth = std::find_if (replies.begin (), replies.end (), [] (auto const& r) {
294 return r.find ("AUTH") != std::string::npos;
295 });
296 if (auth != replies.end ())
297 {
298 if (auth->find ("LOGIN") != std::string::npos)
299 {
300 if (this->loginAuthenticate () == -1)
301 {
302 this->close ();
303 return -1;
304 }
305 }
306 else if (auth->find ("PLAIN") != std::string::npos)
307 {
308 if (this->plainAuthenticate () == -1)
309 {
310 this->close ();
311 return -1;
312 }
313 }
314 }
315
316 if (this->sendFrom (mail) == -1)
317 {
318 this->close ();
319 return -1;
320 }
321
322 if (this->sendTo (mail) == -1)
323 {
324 this->close ();
325 return -1;
326 }
327
328 if (this->sendData (mail) == -1)
329 {
330 this->close ();
331 return -1;
332 }
333
334 if (this->quit () == -1)
335 {
336 this->close ();
337 return -1;
338 }
339
340 this->close ();
341 return 0;
342 }
343
344 protected:
350 virtual int connect (const Endpoint& endpoint)
351 {
352 this->_stream.connect (endpoint);
353 return this->_stream.fail () ? -1 : 0;
354 }
355
360 void close ()
361 {
362 this->_stream.disconnect ();
363 this->_stream.close ();
364 this->_stream.clear ();
365 }
366
372 int sendMessage (const std::string& message)
373 {
374#ifdef DEBUG
375 std::cout << message << std::endl;
376#endif
377 this->_stream.write (message.c_str (), message.size ());
378 this->_stream.write ("\r\n", 2);
379 this->_stream.flush ();
380 return this->_stream.fail () ? -1 : 0;
381 }
382
388 std::string readReplies (std::vector<std::string>* replies = nullptr)
389 {
390 std::string reply, code;
391 for (;;)
392 {
393 if (!join::getline (this->_stream, reply))
394 {
395 return {};
396 }
397#ifdef DEBUG
398 std::cout << reply << std::endl;
399#endif
400 if ((reply.find ("-") != 3) && (reply.find (" ") != 3))
401 {
403 return {};
404 }
405 if (code.empty ())
406 {
407 code = reply.substr (0, 3);
408 }
409 if (replies)
410 {
411 replies->push_back (reply.substr (4));
412 }
413 if (reply[3] == ' ')
414 {
415 break;
416 }
417 }
418 return code;
419 }
420
425 int greeting ()
426 {
427 if (this->readReplies () != "220")
428 {
429 return -1;
430 }
431 return 0;
432 }
433
439 int initialize (std::vector<std::string>& replies)
440 {
441 if (this->sendMessage ("EHLO " + this->hostname ()) == -1)
442 {
443 return -1;
444 }
445 if (this->readReplies (&replies) != "250")
446 {
447 return -1;
448 }
449 return 0;
450 }
451
456 int startTls ()
457 {
458 if (this->sendMessage ("STARTTLS") == -1)
459 {
460 return -1;
461 }
462 if (this->readReplies () != "220")
463 {
464 return -1;
465 }
466 this->_stream.startEncryption ();
467 if (this->_stream.fail ())
468 {
469 return -1;
470 }
471 return 0;
472 }
473
479 {
480 if (this->sendMessage ("AUTH LOGIN") == -1)
481 {
482 return -1;
483 }
484 if (this->readReplies () != "334")
485 {
486 return -1;
487 }
488 if (this->sendMessage (Base64::encode (this->_login)) == -1)
489 {
490 return -1;
491 }
492 if (this->readReplies () != "334")
493 {
494 return -1;
495 }
496 if (this->sendMessage (Base64::encode (this->_password)) == -1)
497 {
498 return -1;
499 }
500 if (this->readReplies () != "235")
501 {
502 return -1;
503 }
504 return 0;
505 }
506
512 {
513 if (this->sendMessage ("AUTH PLAIN") == -1)
514 {
515 return -1;
516 }
517 if (this->readReplies () != "334")
518 {
519 return -1;
520 }
521 if (this->sendMessage (Base64::encode ('\0' + this->_login + '\0' + this->_password)) == -1)
522 {
523 return -1;
524 }
525 if (this->readReplies () != "235")
526 {
527 return -1;
528 }
529 return 0;
530 }
531
537 int sendFrom (const MailMessage& message)
538 {
539 if (this->sendMessage ("MAIL FROM: <" + message.sender ().address () + ">") == -1)
540 {
541 return -1;
542 }
543 if (this->readReplies () != "250")
544 {
545 return -1;
546 }
547 return 0;
548 }
549
555 int sendTo (const MailMessage& message)
556 {
557 for (auto const& recipient : message.recipients ())
558 {
559 if (this->sendMessage ("RCPT TO: <" + recipient.address () + ">") == -1)
560 {
561 return -1;
562 }
563 if (this->readReplies () != "250")
564 {
565 return -1;
566 }
567 }
568 return 0;
569 }
570
576 int sendData (const MailMessage& message)
577 {
578 if (this->sendMessage ("DATA") == -1)
579 {
580 return -1;
581 }
582 if (this->readReplies () != "354")
583 {
584 return -1;
585 }
586 if (message.writeHeaders (this->_stream) == -1)
587 {
588 return -1;
589 }
590 if (message.writeContent (this->_stream) == -1)
591 {
592 return -1;
593 }
594 if (this->readReplies () != "250")
595 {
596 return -1;
597 }
598 return 0;
599 }
600
605 int quit ()
606 {
607 if (this->sendMessage ("QUIT") == -1)
608 {
609 return -1;
610 }
611 if (this->readReplies () != "221")
612 {
613 return -1;
614 }
615 return 0;
616 }
617
622 std::string hostname () const
623 {
624 char name[255] = {};
625 gethostname (name, sizeof (name));
626 return name;
627 }
628
631
633 std::string _host;
634
636 uint16_t _port;
637
639 std::string _login;
640
642 std::string _password;
643 };
644
648 template <class Protocol>
649 class BasicSmtpSecureClient : public BasicSmtpClient<Protocol>
650 {
651 public:
652 using Endpoint = typename Protocol::Endpoint;
653
659 BasicSmtpSecureClient (const char* host, uint16_t port = 465)
660 : BasicSmtpClient<Protocol> (host, port)
661 {
662 }
663
669 BasicSmtpSecureClient (const std::string& host, uint16_t port = 465)
670 : BasicSmtpSecureClient (host.c_str (), port)
671 {
672 }
673
679
686
692 : BasicSmtpClient<Protocol> (std::move (other))
693 {
694 }
695
702 {
703 BasicSmtpClient<Protocol>::operator= (std::move (other));
704 return *this;
705 }
706
710 virtual ~BasicSmtpSecureClient () = default;
711
716 std::string scheme () const override
717 {
718 return "smtps";
719 }
720
721 protected:
727 int connect (const Endpoint& endpoint) override
728 {
729 this->_stream.connectEncrypted (endpoint);
730 return this->_stream.fail () ? -1 : 0;
731 }
732 };
733}
734
735#endif
static std::string encode(const char *data, size_t size)
encode data.
Definition base64.cpp:345
basic SMTP client.
Definition smtpclient.hpp:41
void setVerify(bool verify, int depth=-1)
Enable/Disable the verification of the peer certificate.
Definition smtpclient.hpp:221
BasicSmtpClient & operator=(const BasicSmtpClient &other)=delete
assign the basic SMTP client instance by copy.
int setCertificate(const std::string &cert, const std::string &key="")
set the certificate and the private key.
Definition smtpclient.hpp:191
int setCaFile(const std::string &caFile)
set the location of the trusted CA certificate file.
Definition smtpclient.hpp:211
int send(const MailMessage &mail)
send mail message.
Definition smtpclient.hpp:251
typename Protocol::Endpoint Endpoint
Definition smtpclient.hpp:43
int greeting()
handle greeting.
Definition smtpclient.hpp:425
int sendData(const MailMessage &message)
send message.
Definition smtpclient.hpp:576
int initialize(std::vector< std::string > &replies)
client init.
Definition smtpclient.hpp:439
BasicSmtpClient(const char *host, uint16_t port=25)
create the basic SMTP client instance.
Definition smtpclient.hpp:51
uint16_t port() const
get port.
Definition smtpclient.hpp:135
std::string _password
SMTP password.
Definition smtpclient.hpp:642
void credentials(const std::string &login, const std::string &password)
set credentials.
Definition smtpclient.hpp:179
int sendFrom(const MailMessage &message)
send sender address.
Definition smtpclient.hpp:537
int sendMessage(const std::string &message)
send message.
Definition smtpclient.hpp:372
BasicSmtpClient(const BasicSmtpClient &other)=delete
create the basic SMTP client instance by copy.
Stream _stream
stream.
Definition smtpclient.hpp:630
int setCaPath(const std::string &caPath)
set the location of the trusted CA certificates.
Definition smtpclient.hpp:201
uint16_t _port
SMTP port.
Definition smtpclient.hpp:636
BasicSmtpClient(const std::string &host, uint16_t port=25)
create the basic SMTP client instance.
Definition smtpclient.hpp:62
typename Protocol::Stream Stream
Definition smtpclient.hpp:44
std::string authority() const
get authority.
Definition smtpclient.hpp:144
int setCipher(const std::string &cipher)
set the cipher list (TLSv1.2 and below).
Definition smtpclient.hpp:231
int loginAuthenticate()
authenticate using LOGIN.
Definition smtpclient.hpp:478
virtual ~BasicSmtpClient()=default
destroy the basic SMTP client instance.
virtual std::string scheme() const
get scheme.
Definition smtpclient.hpp:117
int startTls()
start encryption.
Definition smtpclient.hpp:456
void close()
close the connection.
Definition smtpclient.hpp:360
virtual int connect(const Endpoint &endpoint)
make a connection to the given endpoint.
Definition smtpclient.hpp:350
BasicSmtpClient(BasicSmtpClient &&other)
create the basic SMTP client instance by move.
Definition smtpclient.hpp:84
int setCipher_1_3(const std::string &cipher)
set the cipher list (TLSv1.3).
Definition smtpclient.hpp:241
std::string _login
SMTP login.
Definition smtpclient.hpp:639
const std::string & host() const
get host.
Definition smtpclient.hpp:126
std::string url() const
get URL.
Definition smtpclient.hpp:169
std::string _host
SMTP host.
Definition smtpclient.hpp:633
int plainAuthenticate()
authenticate using PLAIN.
Definition smtpclient.hpp:511
int quit()
send quit.
Definition smtpclient.hpp:605
int sendTo(const MailMessage &message)
send recpient address.
Definition smtpclient.hpp:555
std::string readReplies(std::vector< std::string > *replies=nullptr)
read replies.
Definition smtpclient.hpp:388
std::string hostname() const
get host name.
Definition smtpclient.hpp:622
Basic SMTPS client.
Definition smtpclient.hpp:650
BasicSmtpSecureClient(BasicSmtpSecureClient &&other)
create the basic SMTPS client instance by move.
Definition smtpclient.hpp:691
typename Protocol::Endpoint Endpoint
Definition smtpclient.hpp:652
BasicSmtpSecureClient & operator=(const BasicSmtpSecureClient &other)=delete
assign the basic SMTPS client instance by copy.
BasicSmtpSecureClient(const BasicSmtpSecureClient &other)=delete
create the basic SMTPS client instance by copy.
int connect(const Endpoint &endpoint) override
make a connection to the given endpoint.
Definition smtpclient.hpp:727
std::string scheme() const override
get scheme.
Definition smtpclient.hpp:716
BasicSmtpSecureClient(const std::string &host, uint16_t port=465)
create the basic SMTPS client instance.
Definition smtpclient.hpp:669
virtual ~BasicSmtpSecureClient()=default
destroy the basic SMTPS client instance.
BasicSmtpSecureClient(const char *host, uint16_t port=465)
create the basic SMTPS client instance.
Definition smtpclient.hpp:659
bool isIpv6Address() const
check if IP address is an IPv6 address.
Definition ipaddress.cpp:1394
mail message.
Definition mailmessage.hpp:230
int writeContent(std::ostream &out) const
write content to the given output stream.
Definition mailmessage.cpp:449
const MailRecipients & recipients() const
get mail recipients.
Definition mailmessage.cpp:323
int writeHeaders(std::ostream &out) const
write header to the given output stream.
Definition mailmessage.cpp:368
void sender(const MailSender &from)
set mail sender.
Definition mailmessage.cpp:287
static uint16_t resolveService(const std::string &service)
resolve service name.
Definition resolver.cpp:550
IpAddress resolveHost(const std::string &host, int family, const IpAddress &server, uint16_t port=dnsPort, int timeout=5000)
resolve host name using address family.
Definition resolver.cpp:195
const std::string key(65, 'a')
key.
Definition acceptor.hpp:32
std::error_code make_error_code(join::Errc code) noexcept
Create an std::error_code object.
Definition error.cpp:150
thread_local std::error_code lastError
last error.
Definition error.cpp:32
bool getline(std::istream &in, std::string &line, std::streamsize max=1024)
read line (delimiter "\r\n").
Definition utils.hpp:308
Definition error.hpp:137