summaryrefslogtreecommitdiffstats
path: root/src/uscxml/plugins/invoker/imap/IMAPInvoker.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/uscxml/plugins/invoker/imap/IMAPInvoker.cpp')
-rw-r--r--src/uscxml/plugins/invoker/imap/IMAPInvoker.cpp365
1 files changed, 365 insertions, 0 deletions
diff --git a/src/uscxml/plugins/invoker/imap/IMAPInvoker.cpp b/src/uscxml/plugins/invoker/imap/IMAPInvoker.cpp
new file mode 100644
index 0000000..22e32da
--- /dev/null
+++ b/src/uscxml/plugins/invoker/imap/IMAPInvoker.cpp
@@ -0,0 +1,365 @@
+/**
+ * @file
+ * @author 2012-2013 Stefan Radomski (stefan.radomski@cs.tu-darmstadt.de)
+ * @copyright Simplified BSD
+ *
+ * @cond
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the FreeBSD license as published by the FreeBSD
+ * project.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * You should have received a copy of the FreeBSD license along with this
+ * program. If not, see <http://www.opensource.org/licenses/bsd-license>.
+ * @endcond
+ */
+
+#include "IMAPInvoker.h"
+#include <glog/logging.h>
+
+#ifdef BUILD_AS_PLUGINS
+#include <Pluma/Connector.hpp>
+#endif
+
+#include <boost/algorithm/string.hpp>
+#include "uscxml/UUID.h"
+
+namespace uscxml {
+
+#ifdef BUILD_AS_PLUGINS
+PLUMA_CONNECTOR
+bool pluginConnect(pluma::Host& host) {
+ host.add( new IMAPInvokerProvider() );
+ return true;
+}
+#endif
+
+IMAPInvoker::IMAPInvoker() {
+}
+
+IMAPInvoker::~IMAPInvoker() {
+};
+
+boost::shared_ptr<InvokerImpl> IMAPInvoker::create(InterpreterImpl* interpreter) {
+ boost::shared_ptr<IMAPInvoker> invoker = boost::shared_ptr<IMAPInvoker>(new IMAPInvoker());
+ return invoker;
+}
+
+Data IMAPInvoker::getDataModelVariables() {
+ Data data;
+ return data;
+}
+
+size_t IMAPInvoker::writeCurlData(void *ptr, size_t size, size_t nmemb, void *userdata) {
+ if (!userdata)
+ return 0;
+
+ IMAPContext* ctx = (IMAPContext*)userdata;
+
+ size_t toWrite = std::min(ctx->content.length() - ctx->readPtr, size * nmemb);
+ if (toWrite > 0) {
+ memcpy (ptr, ctx->content.c_str() + ctx->readPtr, toWrite);
+ ctx->readPtr += toWrite;
+ }
+
+ return toWrite;
+}
+
+std::list<std::string> IMAPInvoker::getAtoms(std::list<Data> list) {
+ std::list<std::string> atoms;
+
+ std::list<Data>::const_iterator iter = list.begin();
+ while(iter != list.end()) {
+ const Data& data = *iter;
+ if (data.atom.size() > 0) {
+ atoms.push_back(data.atom);
+ } else if (data.array.size() > 0) {
+ std::list<Data>::const_iterator arrIter = data.array.begin();
+ while(arrIter != data.array.end()) {
+ if (arrIter->atom.size() > 0) {
+ atoms.push_back(arrIter->atom);
+ arrIter++;
+ }
+ }
+ }
+ iter++;
+ }
+ return atoms;
+}
+
+void IMAPInvoker::getAttachments(std::list<Data> list, std::list<Data>& attachments) {
+ // accumulate attachments with filename, mimetype and data
+ std::list<Data>::const_iterator iter = list.begin();
+ while(iter != list.end()) {
+ const Data& data = *iter;
+ if (data.hasKey("data")) {
+ // compound structure with all information
+ Data att = data;
+
+ if (!att.hasKey("mimetype")) {
+ if (att["data"].binary && att["data"].binary->mimeType.size() > 0) {
+ att.compound["mimetype"] = Data(att["data"].binary->mimeType, Data::VERBATIM);
+ } else {
+ att.compound["mimetype"] = Data("text/plain", Data::VERBATIM);
+ }
+ }
+
+ if (!att.hasKey("filename")) {
+ std::stringstream filenameSS;
+ filenameSS << "attachment" << attachments.size() + 1;
+ if (boost::starts_with(att.compound["mimetype"].atom, "text")) {
+ filenameSS << ".txt";
+ } else {
+ filenameSS << ".bin";
+ }
+ att.compound["filename"] = Data(filenameSS.str(), Data::VERBATIM);
+ }
+
+ attachments.push_back(att);
+
+ } else if (data.binary) {
+ // a single binary blob
+ Data att;
+
+ att.compound["data"].binary = data.binary;
+
+ if (data.binary->mimeType.size() > 0) {
+ att.compound["mimetype"] = Data(attachments.back()["data"].binary->mimeType, Data::VERBATIM);
+ } else {
+ att.compound["mimetype"] = Data("application/octet-stream", Data::VERBATIM);
+ }
+
+ std::stringstream filenameSS;
+ filenameSS << "attachment" << attachments.size() + 1;
+ if (boost::starts_with(att.compound["mimetype"].atom, "text")) {
+ filenameSS << ".txt";
+ } else {
+ filenameSS << ".bin";
+ }
+ att.compound["filename"] = Data(filenameSS.str(), Data::VERBATIM);
+
+ attachments.push_back(att);
+
+ } else if (data.compound.size() > 0) {
+ // data is some compound, descent to find attachment structures or binaries
+ std::map<std::string, Data>::const_iterator compIter = data.compound.begin();
+ while(compIter != data.compound.end()) {
+ std::list<Data> tmp;
+ tmp.push_back(compIter->second);
+ getAttachments(tmp, attachments);
+ compIter++;
+ }
+ } else if (data.array.size() > 0) {
+ // descent into array
+ getAttachments(data.array, attachments);
+ }
+ iter++;
+ }
+}
+
+void IMAPInvoker::send(const SendRequest& req) {
+ if (iequals(req.name, "mail.send")) {
+
+ struct curl_slist* recipients = NULL;
+ CURLcode curlError;
+ std::string multipartSep;
+
+ bool verbose;
+ bool useSSL;
+ std::string from;
+ std::string subject;
+ std::string contentType;
+ std::list<Data> headerParams;
+ std::list<Data> toParams;
+ std::list<Data> ccParams;
+ std::list<Data> bccParams;
+ std::list<Data> attachmentParams;
+
+ Event::getParam(req.params, "verbose", verbose);
+ Event::getParam(req.params, "ssl", useSSL);
+ Event::getParam(req.params, "Content-Type", contentType);
+ Event::getParam(req.params, "attachment", attachmentParams);
+ Event::getParam(req.params, "from", from);
+ Event::getParam(req.params, "subject", subject);
+ Event::getParam(req.params, "header", headerParams);
+ Event::getParam(req.params, "to", toParams);
+ Event::getParam(req.params, "cc", ccParams);
+ Event::getParam(req.params, "bcc", bccParams);
+
+ if (contentType.size() == 0)
+ contentType = "text/plain; charset=\"UTF-8\"";
+
+ IMAPContext* ctx = new IMAPContext();
+ std::stringstream contentSS;
+
+ std::list<std::string>::const_iterator recIter;
+ std::list<std::string> to = getAtoms(toParams);
+ std::list<std::string> cc = getAtoms(ccParams);
+ std::list<std::string> bcc = getAtoms(bccParams);
+ std::list<std::string> headers = getAtoms(headerParams);
+ std::list<Data> attachments; getAttachments(attachmentParams, attachments);
+
+ if (to.size() == 0)
+ return;
+
+ recIter = to.begin();
+ recIter++; // skip first as we need it in CURLOPT_MAIL_RCPT
+ while(recIter != to.end()) {
+ contentSS << "TO: " << *recIter << std::endl;
+ recIter++;
+ }
+ recIter = cc.begin();
+ while(recIter != cc.end()) {
+ contentSS << "CC: " << *recIter << std::endl;
+ recIter++;
+ }
+ recIter = bcc.begin();
+ while(recIter != bcc.end()) {
+ contentSS << "BCC: " << *recIter << std::endl;
+ recIter++;
+ }
+
+ recIter = headers.begin();
+ while(recIter != headers.end()) {
+ contentSS << *recIter << std::endl;
+ recIter++;
+ }
+
+ if (subject.length() > 0) {
+ boost::replace_all(subject, "\n\r", " ");
+ boost::replace_all(subject, "\r\n", " ");
+ boost::replace_all(subject, "\n", " ");
+ boost::replace_all(subject, "\r", " ");
+ contentSS << "Subject: " << subject << "\n";
+ }
+
+ // content type is different when we have attachments
+ if (attachments.size() > 0) {
+ multipartSep = UUID::getUUID();
+ boost::replace_all(multipartSep, "-", "");
+ contentSS << "Content-Type: multipart/mixed; boundary=\"" << multipartSep << "\"\n";
+ contentSS << "MIME-Version: 1.0\n";
+ contentSS << "\n";
+ contentSS << "--" << multipartSep << "\n";
+ contentSS << "Content-Type: " << contentType << "\n";
+ } else {
+ // when we have no attachment, respect user-defined or use text/plain
+ contentSS << "Content-Type: " << contentType << "\n";
+ }
+
+ contentSS << "\n";
+ contentSS << req.content;
+
+ std::list<Data>::iterator attIter = attachments.begin();
+ while(attIter != attachments.end()) {
+ // only send valid attachments
+ if(!attIter->hasKey("filename") || !attIter->hasKey("mimetype") || !attIter->hasKey("data")) {
+ LOG(ERROR) << "Not sending attachment as filename, mimetype or data is missing: " << *attIter;
+ } else {
+ contentSS << "\n\n";
+ contentSS << "--" << multipartSep << "\n";
+ contentSS << "Content-Disposition: attachment; filename=\"" << attIter->compound["filename"].atom << "\"";
+ contentSS << "\n";
+
+ contentSS << "Content-Type: " << attIter->compound["mimetype"].atom << "; ";
+ contentSS << "name=\"" << attIter->compound["filename"].atom << "\"";
+ contentSS << "\n";
+
+ if (attIter->compound["data"].binary) {
+ contentSS << "Content-Transfer-Encoding: base64";
+ contentSS << "\n\n";
+ contentSS << attIter->compound["data"].binary->base64();
+ } else {
+ contentSS << "Content-Transfer-Encoding: 7Bit";
+ contentSS << "\n\n";
+ contentSS << attIter->compound["data"].atom;
+ }
+ }
+ attIter++;
+ }
+
+ ctx->content = contentSS.str();
+ ctx->invoker = this;
+
+
+ // see http://curl.haxx.se/libcurl/c/imap-tls.html
+ _curl = curl_easy_init();
+ if(_curl) {
+ (curlError = curl_easy_setopt(_curl, CURLOPT_USERNAME, _username.c_str())) == CURLE_OK ||
+ LOG(ERROR) << "Cannot set username: " << curl_easy_strerror(curlError);
+ (curlError = curl_easy_setopt(_curl, CURLOPT_PASSWORD, _password.c_str())) == CURLE_OK ||
+ LOG(ERROR) << "Cannot set password: " << curl_easy_strerror(curlError);
+ (curlError = curl_easy_setopt(_curl, CURLOPT_URL, _server.c_str())) == CURLE_OK ||
+ LOG(ERROR) << "Cannot set server string: " << curl_easy_strerror(curlError);
+
+ if (useSSL) {
+ (curlError = curl_easy_setopt(_curl, CURLOPT_USE_SSL, (long)CURLUSESSL_ALL)) == CURLE_OK ||
+ LOG(ERROR) << "Cannot use SSL: " << curl_easy_strerror(curlError);
+#if 1
+ (curlError = curl_easy_setopt(_curl, CURLOPT_SSL_VERIFYPEER, 0L)) == CURLE_OK ||
+ LOG(ERROR) << "Cannot unset verify peer with SSL: " << curl_easy_strerror(curlError);
+ (curlError = curl_easy_setopt(_curl, CURLOPT_SSL_VERIFYHOST, 0L)) == CURLE_OK ||
+ LOG(ERROR) << "Cannot unset verify host with SSL: " << curl_easy_strerror(curlError);
+#else
+ (curlError = curl_easy_setopt(_curl, CURLOPT_CAINFO, "/path/to/certificate.pem")) == CURLE_OK ||
+ LOG(ERROR) << "Cannot set CA info path: " << curl_easy_strerror(curlError);
+#endif
+
+ }
+
+ // this is needed, even if we have a callback function
+ recipients = curl_slist_append(recipients, to.begin()->c_str());
+ (curlError = curl_easy_setopt(_curl, CURLOPT_MAIL_RCPT, recipients)) == CURLE_OK ||
+ LOG(ERROR) << "Cannot set mail recipient: " << curl_easy_strerror(curlError);
+
+ (curlError = curl_easy_setopt(_curl, CURLOPT_READFUNCTION, IMAPInvoker::writeCurlData)) == CURLE_OK ||
+ LOG(ERROR) << "Cannot register read function: " << curl_easy_strerror(curlError);
+ (curlError = curl_easy_setopt(_curl, CURLOPT_READDATA, ctx)) == CURLE_OK ||
+ LOG(ERROR) << "Cannot register userdata for read function: " << curl_easy_strerror(curlError);
+ (curlError = curl_easy_setopt(_curl, CURLOPT_UPLOAD, 1L)) == CURLE_OK ||
+ LOG(ERROR) << "Cannot set upload parameter: " << curl_easy_strerror(curlError);
+
+ if (from.length() > 0) {
+ (curlError = curl_easy_setopt(_curl, CURLOPT_MAIL_FROM, from.c_str())) == CURLE_OK ||
+ LOG(ERROR) << "Cannot set from parameter: " << curl_easy_strerror(curlError);
+ }
+
+ if (verbose) {
+ (curlError = curl_easy_setopt(_curl, CURLOPT_VERBOSE, 1L)) == CURLE_OK ||
+ LOG(ERROR) << "Cannot set curl to verbose: " << curl_easy_strerror(curlError);
+ }
+
+ CURLcode res = curl_easy_perform(_curl);
+
+ /* Check for errors */
+ if(res != CURLE_OK){
+ LOG(ERROR) << "curl_easy_perform() failed: " << curl_easy_strerror(res);
+ returnErrorExecution("error.mail.send");
+ } else {
+ returnErrorExecution("success.mail.send");
+ }
+ /* Free the list of recipients */
+ if (recipients)
+ curl_slist_free_all(recipients);
+
+ /* Always cleanup */
+ curl_easy_cleanup(_curl);
+
+ }
+
+ }
+}
+
+void IMAPInvoker::cancel(const std::string sendId) {
+}
+
+void IMAPInvoker::invoke(const InvokeRequest& req) {
+ Event::getParam(req.params, "username", _username);
+ Event::getParam(req.params, "password", _password);
+ Event::getParam(req.params, "server", _server);
+}
+
+} \ No newline at end of file