libjmmcg  release_579_6_g8cffd
A C++ library containing an eclectic mix of useful, advanced components.
curl.cpp
Go to the documentation of this file.
1 /******************************************************************************
2 ** Copyright © 2017 by J.M.McGuiness, coder@hussar.me.uk
3 **
4 ** This library is free software; you can redistribute it and/or
5 ** modify it under the terms of the GNU Lesser General Public
6 ** License as published by the Free Software Foundation; either
7 ** version 2.1 of the License, or (at your option) any later version.
8 **
9 ** This library is distributed in the hope that it will be useful,
10 ** but WITHOUT ANY WARRANTY; without even the implied warranty of
11 ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 ** Lesser General Public License for more details.
13 **
14 ** You should have received a copy of the GNU Lesser General Public
15 ** License along with this library; if not, write to the Free Software
16 ** Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
17 */
18 
19 /***************************************************************************
20  * _ _ ____ _
21  * Project ___| | | | _ \| |
22  * / __| | | | |_) | |
23  * | (__| |_| | _ <| |___
24  * \___|\___/|_| \_\_____|
25  *
26  * Copyright (C) 1998 - 2016, Daniel Stenberg, <daniel@haxx.se>, et al.
27  *
28  * This software is licensed as described in the file COPYING, which
29  * you should have received as part of this distribution. The terms
30  * are also available at https://curl.haxx.se/docs/copyright.html.
31  *
32  * You may opt to use, copy, modify, merge, publish, distribute and/or sell
33  * copies of the Software, and permit persons to whom the Software is
34  * furnished to do so, under the terms of the COPYING file.
35  *
36  * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
37  * KIND, either express or implied.
38  *
39  ***************************************************************************/
40 
41 #include "curl.hpp"
42 #include "../core/uuid.hpp"
43 
44 #include <boost/exception/diagnostic_information.hpp>
45 #include <boost/lexical_cast.hpp>
46 
47 #include <iomanip>
48 #include <sstream>
49 #include <stdexcept>
50 
51 namespace jmmcg { namespace LIBJMMCG_VER_NAMESPACE {
52 
53 curl::payload_message_t curl::payload_text_strs_{};
54 
55 curl::email_recipients::~email_recipients() noexcept(true) {
56  ::curl_slist_free_all(recipients_);
57 }
58 
59 void
60 curl::email_recipients::push_back(std::string const &addr) noexcept(true) {
61  addrs_.push_back(addr);
62  recipients_=::curl_slist_append(recipients_, addrs_.rbegin()->c_str());
63 }
64 
65 __attribute__((pure)) bool
66 curl::email_recipients::empty() const noexcept(true) {
67  return addrs_.empty();
68 }
69 
70 curl::curl(std::string const &smtp_url, unsigned short port, std::string const &username, std::string const &password, bool enable_logging, bool enable_ssl_verification) noexcept(false)
71 : handle_(::curl_easy_init()), smtp_url_(smtp_url+":"+boost::lexical_cast<std::string>(port)), username_(username), password_(password) {
72  if (!handle_) {
73  throw std::runtime_error("Failed to initialise curl library. Try running the program again, or rebooting...");
74  }
75  ::curl_easy_setopt(handle_, CURLOPT_USERNAME, username_.c_str());
76  ::curl_easy_setopt(handle_, CURLOPT_PASSWORD, password_.c_str());
77  ::curl_easy_setopt(handle_, CURLOPT_URL, smtp_url_.c_str());
78 
79  /* In this example, we'll start with a plain text connection, and upgrade
80  * to Transport Layer Security (TLS) using the STARTTLS command. Be careful
81  * of using CURLUSESSL_TRY here, because if TLS upgrade fails, the transfer
82  * will continue anyway - see the security discussion in the libcurl
83  * tutorial for more details. */
84  ::curl_easy_setopt(handle_, CURLOPT_USE_SSL, static_cast<long>(CURLUSESSL_ALL));
85  if (enable_logging) {
86  set_logging();
87  }
88  if (!enable_ssl_verification) {
89  ignore_ssl_verification();
90  }
91 }
92 
93 curl::~curl() noexcept(true) {
94  ::curl_easy_cleanup(handle_);
95 }
96 
97 void
98 curl::ignore_ssl_verification() noexcept(true) {
99  ::curl_easy_setopt(handle_, CURLOPT_SSL_VERIFYPEER, 0L);
100  ::curl_easy_setopt(handle_, CURLOPT_SSL_VERIFYHOST, 0L);
101 }
102 
103 void
104 curl::set_logging() noexcept(true) {
105  ::curl_easy_setopt(handle_, CURLOPT_VERBOSE, 1L);
106 }
107 
108 void
109 curl::from(std::string const &from) noexcept(true) {
110  from_=from;
111  ::curl_easy_setopt(handle_, CURLOPT_MAIL_FROM, from_.c_str());
112 }
113 
114 size_t
115 curl::payload_source(void *ptr, size_t size, size_t nmemb, void *userp) {
116  upload_status *upload_ctx = reinterpret_cast<upload_status *>(userp);
117  assert(upload_ctx);
118 
119  if ((size == 0) || (nmemb == 0) || ((size*nmemb) < 1)) {
120  return 0;
121  }
122  if (upload_ctx->lines_read<payload_text_strs_.size()) {
123  payload_message_t::value_type const &data=payload_text_strs_[upload_ctx->lines_read];
124  if (!data.empty()) {
125  std::memcpy(ptr, data.data(), data.size());
126  ++upload_ctx->lines_read;
127  return data.size();
128  }
129  }
130  return 0;
131 }
132 
134 curl::send(std::string const &subject, std::string const &body, recipients_t const &recipients) noexcept(false) {
135  failures_t failures;
136  for (auto const &recipient : recipients) {
137  try {
138  create_message(recipient, subject, body);
139  upload_ctx_.lines_read=0;
140  email_recipients a_recipient;
141  a_recipient.push_back(recipient);
142  assert(a_recipient.recipients_);
143  ::curl_easy_setopt(handle_, CURLOPT_MAIL_RCPT, a_recipient.recipients_);
144  CURLcode res=::curl_easy_perform(handle_);
145  if (res==CURLE_OK) {
146  ::curl_easy_setopt(handle_, CURLOPT_READFUNCTION, payload_source);
147  ::curl_easy_setopt(handle_, CURLOPT_READDATA, &upload_ctx_);
148  ::curl_easy_setopt(handle_, CURLOPT_UPLOAD, 1L);
149  res=::curl_easy_perform(handle_);
150  if (res!=CURLE_OK) {
151  failures.emplace_back(std::make_tuple(recipient, ::curl_easy_strerror(res)));
152  }
153  } else {
154  failures.emplace_back(std::make_tuple(recipient, ::curl_easy_strerror(res)));
155  }
156  } catch (std::exception const &ex) {
157  failures.emplace_back(std::make_tuple(recipient, boost::diagnostic_information(ex)));
158  } catch (...) {
159  failures.emplace_back(std::make_tuple(recipient, "Unknown error."));
160  }
161  }
162  return failures;
163 }
164 
165 std::string
166 curl::current_time_for_email() noexcept(false) {
167  std::stringstream buffer;
168  std::time_t t=std::time(nullptr);
169  std::tm tm=*std::localtime(&t);
170  buffer<<std::put_time(&tm, "%a %d %b %Y %H:%M:%S %Y %z");
171  return buffer.str();
172 }
173 
174 void
175 curl::create_message(std::string const &to, std::string const &subject, std::string const &body) const noexcept(false) {
176  const char newline[]="\r\n";
177  payload_text_strs_.clear();
178  payload_text_strs_.emplace_back("Date: "+current_time_for_email()+newline);
179  payload_text_strs_.emplace_back("To: "+to+newline);
180  payload_text_strs_.emplace_back("From: "+from_+newline);
181  payload_text_strs_.emplace_back("Message-ID: "+get_uuid_as_string()+from_+newline);
182  payload_text_strs_.emplace_back("Subject: "+subject+newline);
183  payload_text_strs_.emplace_back(newline);
184  payload_text_strs_.emplace_back(body+newline);
185 }
186 
187 } }