Warning, file /pim/sink/examples/mailtransportresource/mailtransport.cpp was not indexed or was modified since last indexation (in which case cross-reference links may be missing, inaccurate or erroneous).
0001 /* 0002 Copyright (c) 2016 Christian Mollekopf <mollekopf@kolabsys.com> 0003 0004 This library is free software; you can redistribute it and/or modify it 0005 under the terms of the GNU Library General Public License as published by 0006 the Free Software Foundation; either version 2 of the License, or (at your 0007 option) any later version. 0008 0009 This library is distributed in the hope that it will be useful, but WITHOUT 0010 ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 0011 FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public 0012 License for more details. 0013 0014 You should have received a copy of the GNU Library General Public License 0015 along with this library; see the file COPYING.LIB. If not, write to the 0016 Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 0017 02110-1301, USA. 0018 */ 0019 #include "mailtransport.h" 0020 0021 #include <QByteArray> 0022 #include <QList> 0023 #include <QLoggingCategory> 0024 #include <stdio.h> 0025 #include <string.h> 0026 #include <curl/curl.h> 0027 0028 0029 Q_LOGGING_CATEGORY(mailtransportCategory, "mailtransport") 0030 0031 struct upload_status { 0032 int offset; 0033 const char *data; 0034 }; 0035 0036 0037 extern "C" { 0038 0039 static size_t payload_source(void *ptr, size_t size, size_t nmemb, void *userp) 0040 { 0041 struct upload_status *upload_ctx = (struct upload_status *)userp; 0042 const char *data; 0043 0044 if ((size == 0) || (nmemb == 0) || ((size*nmemb) < 1)) { 0045 return 0; 0046 } 0047 0048 data = &upload_ctx->data[upload_ctx->offset]; 0049 if (data) { 0050 size_t len = strlen(data); 0051 if (len > size * nmemb) { 0052 len = size * nmemb; 0053 } 0054 memcpy(ptr, data, len); 0055 upload_ctx->offset += len; 0056 return len; 0057 } 0058 0059 return 0; 0060 } 0061 0062 } 0063 0064 struct CurlVersionInfo { 0065 bool supportsSsl; 0066 QByteArray info; 0067 }; 0068 0069 CurlVersionInfo getVersionInfo() 0070 { 0071 CurlVersionInfo versionInfo; 0072 curl_version_info_data *data = curl_version_info(CURLVERSION_NOW); 0073 if (data->ssl_version) { 0074 versionInfo.info += "SSL support available: " + QByteArray{data->ssl_version} + "\n"; 0075 versionInfo.supportsSsl = true; 0076 } else { 0077 versionInfo.info += "No SSL support available.\n"; 0078 versionInfo.supportsSsl = false; 0079 } 0080 return versionInfo; 0081 } 0082 0083 bool sendMessageCurl(const char *to[], int numTos, 0084 const char *cc[], int numCcs, 0085 const char *msg, 0086 bool useStarttls, 0087 const char* from, 0088 const char *username, const char *password, 0089 const char *server, bool verifyPeer, const QByteArray &cacert, QByteArray &errorMessage, 0090 bool enableDebugOutput, 0091 int (*debug_callback)(CURL *handle, curl_infotype type, char *data, size_t size, void *userptr), 0092 int (*progress_callback)(void *clientp, curl_off_t dltotal, curl_off_t dlnow, curl_off_t ultotal, curl_off_t ulnow) 0093 ) 0094 { 0095 CURL *curl; 0096 CURLcode res = CURLE_OK; 0097 struct curl_slist *recipients = NULL; 0098 struct upload_status upload_ctx; 0099 0100 upload_ctx.offset = 0; 0101 upload_ctx.data = msg; 0102 0103 curl = curl_easy_init(); 0104 if(curl) { 0105 curl_easy_setopt(curl, CURLOPT_USERNAME, username); 0106 curl_easy_setopt(curl, CURLOPT_PASSWORD, password); 0107 0108 curl_easy_setopt(curl, CURLOPT_URL, server); 0109 0110 if (useStarttls) { 0111 curl_easy_setopt(curl, CURLOPT_USE_SSL, (long)CURLUSESSL_ALL); 0112 } 0113 0114 if (!verifyPeer) { 0115 curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, 0L); 0116 curl_easy_setopt(curl, CURLOPT_SSL_VERIFYHOST, 0L); 0117 } 0118 if (!cacert.isEmpty()) { 0119 curl_easy_setopt(curl, CURLOPT_CAINFO, cacert.constData()); 0120 } 0121 0122 if (from) { 0123 curl_easy_setopt(curl, CURLOPT_MAIL_FROM, from); 0124 } 0125 0126 for (int i = 0; i < numTos; i++) { 0127 recipients = curl_slist_append(recipients, to[i]); 0128 } 0129 for (int i = 0; i < numCcs; i++) { 0130 recipients = curl_slist_append(recipients, cc[i]); 0131 } 0132 curl_easy_setopt(curl, CURLOPT_MAIL_RCPT, recipients); 0133 0134 /* We're using a callback function to specify the payload (the headers and 0135 * body of the message). You could just use the CURLOPT_READDATA option to 0136 * specify a FILE pointer to read from. */ 0137 curl_easy_setopt(curl, CURLOPT_READFUNCTION, payload_source); 0138 curl_easy_setopt(curl, CURLOPT_READDATA, &upload_ctx); 0139 curl_easy_setopt(curl, CURLOPT_UPLOAD, 1L); 0140 0141 /* Since the traffic will be encrypted, it is very useful to turn on debug 0142 * information within libcurl to see what is happening during the transfer. 0143 */ 0144 if (enableDebugOutput) { 0145 curl_easy_setopt(curl, CURLOPT_VERBOSE, 1L); 0146 } 0147 curl_easy_setopt(curl, CURLOPT_DEBUGFUNCTION, debug_callback); 0148 0149 //Connection timeout of 40s 0150 curl_easy_setopt(curl, CURLOPT_CONNECTTIMEOUT, 40L); 0151 0152 curl_easy_setopt(curl, CURLOPT_NOPROGRESS, 0); 0153 curl_easy_setopt(curl, CURLOPT_XFERINFOFUNCTION, progress_callback); 0154 char errorBuffer[CURL_ERROR_SIZE]; 0155 curl_easy_setopt(curl, CURLOPT_ERRORBUFFER, errorBuffer); 0156 0157 res = curl_easy_perform(curl); 0158 if(res != CURLE_OK) { 0159 errorMessage += "Error code: " + QByteArray::number(res) + ", "; 0160 errorMessage += curl_easy_strerror(res); 0161 errorMessage += "; "; 0162 } 0163 long http_code = 0; 0164 curl_easy_getinfo (curl, CURLINFO_RESPONSE_CODE, &http_code); 0165 if (http_code == 200 && res != CURLE_ABORTED_BY_CALLBACK) { 0166 //Succeeded 0167 } else { 0168 errorMessage += errorBuffer; 0169 } 0170 curl_slist_free_all(recipients); 0171 curl_easy_cleanup(curl); 0172 return res == CURLE_OK; 0173 } 0174 return false; 0175 } 0176 0177 MailTransport::SendResult MailTransport::sendMessage(const KMime::Message::Ptr &message, const QByteArray &server, const QByteArray &username, const QByteArray &password, const QByteArray &cacert, MailTransport::Options options) 0178 { 0179 QByteArray from(message->from(true)->mailboxes().isEmpty() ? QByteArray() : message->from(true)->mailboxes().first().address()); 0180 QList<QByteArray> toList; 0181 for (const auto &mb : message->to(true)->mailboxes()) { 0182 toList << mb.address(); 0183 } 0184 QList<QByteArray> ccList; 0185 for (const auto &mb : message->cc(true)->mailboxes()) { 0186 ccList << mb.address(); 0187 } 0188 const bool verifyPeer = options.testFlag(VerifyPeers); 0189 const bool useStarttls = options.testFlag(UseStarttls); 0190 const bool useTls = options.testFlag(UseTls); 0191 0192 const int numTos = toList.size(); 0193 const char* to[numTos]; 0194 for (int i = 0; i < numTos; i++) { 0195 to[i] = toList.at(i); 0196 } 0197 0198 const int numCcs = ccList.size(); 0199 const char* cc[numCcs]; 0200 for (int i = 0; i < numCcs; i++) { 0201 cc[i] = ccList.at(i); 0202 } 0203 auto serverAddress = server; 0204 if (serverAddress.startsWith("smtps://")) { 0205 serverAddress = serverAddress.mid(8); 0206 } 0207 if (serverAddress.startsWith("smtp://")) { 0208 serverAddress = serverAddress.mid(7); 0209 } 0210 if (useStarttls) { 0211 serverAddress = "smtp://" + serverAddress; 0212 } else if (useTls) { 0213 serverAddress = "smtps://" + serverAddress; 0214 } 0215 0216 const auto versionInfo = getVersionInfo(); 0217 if ((useTls || useStarttls) && !versionInfo.supportsSsl) { 0218 qCWarning(mailtransportCategory) << "libcurl built without ssl support: " << versionInfo.info; 0219 } 0220 0221 bool enableDebugOutput = QLoggingCategory{"mailtransport"}.isEnabled(QtDebugMsg); 0222 QByteArray errorMessage; 0223 auto ret = sendMessageCurl(to, numTos, cc, numCcs, 0224 message->encodedContent(), 0225 useStarttls, 0226 from.isEmpty() ? nullptr : from, 0227 username, password, 0228 serverAddress, verifyPeer, cacert, 0229 errorMessage, 0230 enableDebugOutput, 0231 [] (CURL *handle, curl_infotype type, char *data, size_t size, void *) -> int { 0232 //FIXME all a callback passed to sendMessage using the provided user pointer, 0233 //because lambdas with captures cannot be converted to function pointers. 0234 qCDebug(mailtransportCategory) << QString::fromUtf8(data, size); 0235 return 0; 0236 }, 0237 [] (void *, curl_off_t, curl_off_t, curl_off_t ultotal, curl_off_t ulnow) -> int { 0238 if (ultotal > 0) { 0239 qCDebug(mailtransportCategory) << "Upload progress " << ulnow << " out of " << ultotal; 0240 } 0241 return 0; 0242 }); 0243 return {ret, errorMessage}; 0244 }