join 1.0
lightweight network framework library
Loading...
Searching...
No Matches
smtpclient.hpp
Go to the documentation of this file.
1
25#ifndef __JOIN_SMTP_CLIENT_HPP__
26#define __JOIN_SMTP_CLIENT_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) {return r.find ("AUTH") != std::string::npos;});
294 if (auth != replies.end ())
295 {
296 if (auth->find ("LOGIN") != std::string::npos)
297 {
298 if (this->loginAuthenticate () == -1)
299 {
300 this->close ();
301 return -1;
302 }
303 }
304 else if (auth->find ("PLAIN") != std::string::npos)
305 {
306 if (this->plainAuthenticate () == -1)
307 {
308 this->close ();
309 return -1;
310 }
311 }
312 }
313
314 if (this->sendFrom (mail) == -1)
315 {
316 this->close ();
317 return -1;
318 }
319
320 if (this->sendTo (mail) == -1)
321 {
322 this->close ();
323 return -1;
324 }
325
326 if (this->sendData (mail) == -1)
327 {
328 this->close ();
329 return -1;
330 }
331
332 if (this->quit () == -1)
333 {
334 this->close ();
335 return -1;
336 }
337
338 this->close ();
339 return 0;
340 }
341
342 protected:
348 virtual int connect (const Endpoint& endpoint)
349 {
350 this->_stream.connect (endpoint);
351 return this->_stream.fail () ? -1 : 0;
352 }
353
358 void close ()
359 {
360 this->_stream.disconnect ();
361 this->_stream.close ();
362 this->_stream.clear ();
363 }
364
370 int sendMessage (const std::string& message)
371 {
372 #ifdef DEBUG
373 std::cout << message << std::endl;
374 #endif
375 this->_stream.write (message.c_str (), message.size ());
376 this->_stream.write ("\r\n", 2);
377 this->_stream.flush ();
378 return this->_stream.fail () ? -1 : 0;
379 }
380
386 std::string readReplies (std::vector <std::string>* replies = nullptr)
387 {
388 std::string reply, code;
389 for (;;)
390 {
391 if (!join::getline (this->_stream, reply))
392 {
393 return {};
394 }
395 #ifdef DEBUG
396 std::cout << reply << std::endl;
397 #endif
398 if ((reply.find ("-") != 3) && (reply.find (" ") != 3))
399 {
401 return {};
402 }
403 if (code.empty ())
404 {
405 code = reply.substr (0, 3);
406 }
407 if (replies)
408 {
409 replies->push_back (reply.substr (4));
410 }
411 if (reply[3] == ' ')
412 {
413 break;
414 }
415 }
416 return code;
417 }
418
423 int greeting ()
424 {
425 if (this->readReplies () != "220")
426 {
427 return -1;
428 }
429 return 0;
430 }
431
437 int initialize (std::vector <std::string>& replies)
438 {
439 if (this->sendMessage ("EHLO " + this->hostname ()) == -1)
440 {
441 return -1;
442 }
443 if (this->readReplies (&replies) != "250")
444 {
445 return -1;
446 }
447 return 0;
448 }
449
454 int startTls ()
455 {
456 if (this->sendMessage ("STARTTLS") == -1)
457 {
458 return -1;
459 }
460 if (this->readReplies () != "220")
461 {
462 return -1;
463 }
464 this->_stream.startEncryption ();
465 if (this->_stream.fail ())
466 {
467 return -1;
468 }
469 return 0;
470 }
471
477 {
478 if (this->sendMessage ("AUTH LOGIN") == -1)
479 {
480 return -1;
481 }
482 if (this->readReplies () != "334")
483 {
484 return -1;
485 }
486 if (this->sendMessage (Base64::encode (this->_login)) == -1)
487 {
488 return -1;
489 }
490 if (this->readReplies () != "334")
491 {
492 return -1;
493 }
494 if (this->sendMessage (Base64::encode (this->_password)) == -1)
495 {
496 return -1;
497 }
498 if (this->readReplies () != "235")
499 {
500 return -1;
501 }
502 return 0;
503 }
504
510 {
511 if (this->sendMessage ("AUTH PLAIN") == -1)
512 {
513 return -1;
514 }
515 if (this->readReplies () != "334")
516 {
517 return -1;
518 }
519 if (this->sendMessage (Base64::encode ('\0' + this->_login + '\0' + this->_password)) == -1)
520 {
521 return -1;
522 }
523 if (this->readReplies () != "235")
524 {
525 return -1;
526 }
527 return 0;
528 }
529
535 int sendFrom (const MailMessage& message)
536 {
537 if (this->sendMessage ("MAIL FROM: <" + message.sender ().address () + ">") == -1)
538 {
539 return -1;
540 }
541 if (this->readReplies () != "250")
542 {
543 return -1;
544 }
545 return 0;
546 }
547
553 int sendTo (const MailMessage& message)
554 {
555 for (auto const& recipient : message.recipients ())
556 {
557 if (this->sendMessage ("RCPT TO: <" + recipient.address () + ">") == -1)
558 {
559 return -1;
560 }
561 if (this->readReplies () != "250")
562 {
563 return -1;
564 }
565 }
566 return 0;
567 }
568
574 int sendData (const MailMessage& message)
575 {
576 if (this->sendMessage ("DATA") == -1)
577 {
578 return -1;
579 }
580 if (this->readReplies () != "354")
581 {
582 return -1;
583 }
584 if (message.writeHeaders (this->_stream) == -1)
585 {
586 return -1;
587 }
588 if (message.writeContent (this->_stream) == -1)
589 {
590 return -1;
591 }
592 if (this->readReplies () != "250")
593 {
594 return -1;
595 }
596 return 0;
597 }
598
603 int quit ()
604 {
605 if (this->sendMessage ("QUIT") == -1)
606 {
607 return -1;
608 }
609 if (this->readReplies () != "221")
610 {
611 return -1;
612 }
613 return 0;
614 }
615
620 std::string hostname () const
621 {
622 char name[255] = {};
623 gethostname (name, sizeof (name));
624 return name;
625 }
626
629
631 std::string _host;
632
634 uint16_t _port;
635
637 std::string _login;
638
640 std::string _password;
641 };
642
646 template <class Protocol>
647 class BasicSmtpSecureClient : public BasicSmtpClient <Protocol>
648 {
649 public:
650 using Endpoint = typename Protocol::Endpoint;
651
657 BasicSmtpSecureClient (const char* host, uint16_t port = 465)
658 : BasicSmtpClient <Protocol> (host, port)
659 {
660 }
661
667 BasicSmtpSecureClient (const std::string& host, uint16_t port = 465)
668 : BasicSmtpSecureClient (host.c_str (), port)
669 {
670 }
671
677
684
690 : BasicSmtpClient <Protocol> (std::move (other))
691 {
692 }
693
700 {
701 BasicSmtpClient <Protocol>::operator= (std::move (other));
702 return *this;
703 }
704
708 virtual ~BasicSmtpSecureClient () = default;
709
714 std::string scheme () const override
715 {
716 return "smtps";
717 }
718
719 protected:
725 int connect (const Endpoint& endpoint) override
726 {
727 this->_stream.connectEncrypted (endpoint);
728 return this->_stream.fail () ? -1 : 0;
729 }
730 };
731}
732
733#endif
static std::string encode(const char *data, size_t size)
encode data.
Definition base64.cpp:343
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:423
int sendData(const MailMessage &message)
send message.
Definition smtpclient.hpp:574
int initialize(std::vector< std::string > &replies)
client init.
Definition smtpclient.hpp:437
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:640
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:535
int sendMessage(const std::string &message)
send message.
Definition smtpclient.hpp:370
BasicSmtpClient(const BasicSmtpClient &other)=delete
create the basic SMTP client instance by copy.
Stream _stream
stream.
Definition smtpclient.hpp:628
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:634
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:476
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:454
void close()
close the connection.
Definition smtpclient.hpp:358
virtual int connect(const Endpoint &endpoint)
make a connection to the given endpoint.
Definition smtpclient.hpp:348
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:637
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:631
int plainAuthenticate()
authenticate using PLAIN.
Definition smtpclient.hpp:509
int quit()
send quit.
Definition smtpclient.hpp:603
int sendTo(const MailMessage &message)
send recpient address.
Definition smtpclient.hpp:553
std::string readReplies(std::vector< std::string > *replies=nullptr)
read replies.
Definition smtpclient.hpp:386
std::string hostname() const
get host name.
Definition smtpclient.hpp:620
Basic SMTPS client.
Definition smtpclient.hpp:648
BasicSmtpSecureClient(BasicSmtpSecureClient &&other)
create the basic SMTPS client instance by move.
Definition smtpclient.hpp:689
typename Protocol::Endpoint Endpoint
Definition smtpclient.hpp:650
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:725
std::string scheme() const override
get scheme.
Definition smtpclient.hpp:714
BasicSmtpSecureClient(const std::string &host, uint16_t port=465)
create the basic SMTPS client instance.
Definition smtpclient.hpp:667
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:657
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:443
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:546
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:194
const std::string key(65, 'a')
key.
Definition acceptor.hpp:32
std::error_code make_error_code(join::Errc code)
Create an std::error_code object.
Definition error.cpp:154
__inline__ bool getline(std::istream &in, std::string &line, std::streamsize max=1024)
read line (delimiter "\r\n").
Definition utils.hpp:299
thread_local std::error_code lastError
last error.
Definition error.cpp:32
Definition error.hpp:106