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 }