summaryrefslogtreecommitdiffstats
path: root/src/plugins/ReportUploader.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/plugins/ReportUploader.cpp')
-rw-r--r--src/plugins/ReportUploader.cpp517
1 files changed, 517 insertions, 0 deletions
diff --git a/src/plugins/ReportUploader.cpp b/src/plugins/ReportUploader.cpp
new file mode 100644
index 00000000..4100e996
--- /dev/null
+++ b/src/plugins/ReportUploader.cpp
@@ -0,0 +1,517 @@
+/*
+ ReportUploader.cpp
+
+ Copyright (C) 2009 RedHat inc.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License along
+ with this program; if not, write to the Free Software Foundation, Inc.,
+ 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+*/
+#include "abrtlib.h"
+#include "abrt_curl.h"
+#include "ReportUploader.h"
+#include "abrt_exception.h"
+#include "comm_layer_inner.h"
+
+using namespace std;
+
+
+CReportUploader::CReportUploader() :
+ m_bEncrypt(false),
+ m_bUpload(false),
+ m_nRetryCount(3),
+ m_nRetryDelay(20)
+{}
+
+CReportUploader::~CReportUploader()
+{}
+
+
+static void RunCommand(const char *cmd)
+{
+ int retcode = system(cmd);
+ if (retcode)
+ {
+ throw CABRTException(EXCEP_PLUGIN, "'%s' exited with %d", cmd, retcode);
+ }
+}
+
+static string ReadCommand(const char *cmd)
+{
+ FILE* fp = popen(cmd, "r");
+ if (!fp)
+ {
+ throw CABRTException(EXCEP_PLUGIN, "Error running '%s'", cmd);
+ }
+
+ string result;
+ char *buff;
+ while ((buff = xmalloc_fgetline(fp)) != NULL)
+ {
+ result += buff;
+ free(buff);
+ }
+
+ int retcode = pclose(fp);
+ if (retcode)
+ {
+ throw CABRTException(EXCEP_PLUGIN, "'%s' exited with %d", cmd, retcode);
+ }
+
+ return result;
+}
+
+static void WriteCommand(const char *cmd, const char *input)
+{
+ FILE* fp = popen(cmd, "w");
+ if (!fp)
+ {
+ throw CABRTException(EXCEP_PLUGIN, "error running '%s'", cmd);
+ }
+
+ /* Hoping it's not too big to get us forever blocked... */
+ fputs(input, fp);
+
+ int retcode = pclose(fp);
+ if (retcode)
+ {
+ throw CABRTException(EXCEP_PLUGIN, "'%s' exited with %d", cmd, retcode);
+ }
+}
+
+void CReportUploader::SendFile(const char *pURL, const char *pFilename, int retry_count, int retry_delay)
+{
+ if (pURL[0] == '\0')
+ {
+ error_msg(_("FileTransfer: URL not specified"));
+ return;
+ }
+
+ update_client(_("Sending archive %s to %s"), pFilename, pURL);
+
+ const char *base = (strrchr(pFilename, '/') ? : pFilename-1) + 1;
+ char *whole_url = concat_path_file(pURL, base);
+ int count = retry_count;
+ int result;
+ while (1)
+ {
+ FILE* f = fopen(pFilename, "r");
+ if (!f)
+ {
+ free(whole_url);
+ throw CABRTException(EXCEP_PLUGIN, "Can't open archive file '%s'", pFilename);
+ }
+ struct stat buf;
+ fstat(fileno(f), &buf); /* never fails */
+ CURL* curl = xcurl_easy_init();
+ /* enable uploading */
+ curl_easy_setopt(curl, CURLOPT_UPLOAD, 1L);
+ /* specify target */
+ curl_easy_setopt(curl, CURLOPT_URL, whole_url);
+ curl_easy_setopt(curl, CURLOPT_READDATA, f);
+ curl_easy_setopt(curl, CURLOPT_INFILESIZE_LARGE, (curl_off_t)buf.st_size);
+ /* everything is done here; result 0 means success */
+ result = curl_easy_perform(curl);
+ /* goodbye */
+ curl_easy_cleanup(curl);
+ fclose(f);
+ if (result != 0)
+ {
+ update_client(_("Sending failed, trying again. %s"), curl_easy_strerror((CURLcode)result));
+ }
+ if (result == 0 || --count <= 0)
+ break;
+ /* retry the upload if not succesful, wait a bit before next try */
+ sleep(retry_delay);
+ }
+ free(whole_url);
+
+ if (count <= 0 && result != 0)
+ {
+ throw CABRTException(EXCEP_PLUGIN, "Curl can not send a ticket");
+ }
+}
+
+
+static void write_str_to_file(const char *str, const char *path, const char *fname)
+{
+ char *ofile_name = concat_path_file(path, fname);
+ FILE *ofile = fopen(ofile_name, "w");
+ if (!ofile)
+ {
+ CABRTException e(EXCEP_PLUGIN, "Can't open '%s'", ofile_name);
+ free(ofile_name);
+ throw e;
+ }
+ free(ofile_name);
+ fputs(str, ofile);
+ fclose(ofile);
+}
+
+string CReportUploader::Report(const map_crash_data_t& pCrashData,
+ const map_plugin_settings_t& pSettings,
+ const char *pArgs)
+{
+ string customer_name;
+ string ticket_name;
+ string upload_url;
+ bool do_encrypt;
+ bool do_upload;
+ int retry_count;
+ int retry_delay;
+
+ /* if parse_settings fails it returns an empty map so we need to use defaults */
+ map_plugin_settings_t settings = parse_settings(pSettings);
+ // Get ticket name, customer name, and do_encrypt from config settings
+ if (!settings.empty())
+ {
+ customer_name = settings["Customer"];
+ ticket_name = settings["Ticket"];
+ upload_url = settings["URL"];
+ do_encrypt = string_to_bool(settings["Encrypt"].c_str());
+ do_upload = string_to_bool(settings["Upload"].c_str());
+ retry_count = xatoi_u(settings["RetryCount"].c_str());
+ retry_delay = xatoi_u(settings["RetryDelay"].c_str());
+ }
+ else
+ {
+ customer_name = m_sCustomer;
+ ticket_name = m_sTicket;
+ upload_url = m_sURL;
+ do_encrypt = m_bEncrypt;
+ do_upload = m_bUpload;
+ retry_count = m_nRetryCount;
+ retry_delay = m_nRetryDelay;
+ }
+ update_client(_("Creating a ReportUploader report..."));
+
+ bool have_ticket_name = (ticket_name != "");
+ if (!have_ticket_name)
+ {
+ ticket_name = "ReportUploader-newticket";
+ }
+
+ // Format the time to add to the file name
+ char timebuf[256];
+ time_t curtime = time(NULL);
+ strftime(timebuf, sizeof(timebuf), "-%Y%m%d%H%M%S", gmtime(&curtime));
+
+ // Create a tmp work directory, and within that
+ // create the "<ticketname>-yyyymmddhhmmss" directory
+ // which will be the root of the tarball
+ string file_name = ticket_name + timebuf;
+
+ char tmpdir_name[] = "/tmp/abrtuploadXXXXXX";
+ if (mkdtemp(tmpdir_name) == NULL)
+ {
+ throw CABRTException(EXCEP_PLUGIN, "Can't mkdir a temporary directory in /tmp");
+ }
+
+ char *tmptar_name = concat_path_file(tmpdir_name, file_name.c_str());
+ if (mkdir(tmptar_name, 0700))
+ {
+ CABRTException e(EXCEP_PLUGIN, "Can't mkdir '%s'", tmptar_name);
+ free(tmptar_name);
+ throw e;
+ }
+
+ // Copy each entry into the tarball root.
+ // Files are simply copied, strings are written to a file
+ // TODO: some files are totally useless:
+ // "Reported", "Message" (plugin's output), "DumpDir",
+ // "Description" (package description) - maybe skip those?
+ map_crash_data_t::const_iterator it;
+ for (it = pCrashData.begin(); it != pCrashData.end(); it++)
+ {
+ const char *content = it->second[CD_CONTENT].c_str();
+ if (it->second[CD_TYPE] == CD_TXT)
+ {
+ write_str_to_file(content, tmptar_name, it->first.c_str());
+ }
+ else if (it->second[CD_TYPE] == CD_BIN)
+ {
+ char *ofile_name = concat_path_file(tmptar_name, it->first.c_str());
+ if (copy_file(content, ofile_name, 0644) < 0)
+ {
+ CABRTException e(EXCEP_PLUGIN,
+ "Can't copy '%s' to '%s'",
+ content, ofile_name
+ );
+ free(tmptar_name);
+ free(ofile_name);
+ throw e;
+ }
+ free(ofile_name);
+ }
+ }
+
+ // add ticket_name and customer name to tarball
+ if (have_ticket_name)
+ {
+ write_str_to_file(ticket_name.c_str(), tmptar_name, "TICKET");
+ }
+ if (customer_name != "")
+ {
+ write_str_to_file(customer_name.c_str(), tmptar_name, "CUSTOMER");
+ }
+
+ // Create the compressed tarball
+ string outfile_basename = file_name + ".tar.gz";
+ char *outfile_name = concat_path_file(tmpdir_name, outfile_basename.c_str());
+ string cmd = ssprintf("tar -C %s --create --gzip --file=%s %s", tmpdir_name, outfile_name, file_name.c_str());
+ RunCommand(cmd.c_str());
+
+ // encrypt if requested
+ string key;
+ if (do_encrypt)
+ {
+ key = ReadCommand("openssl rand -base64 48");
+
+ string infile_name = outfile_name;
+ outfile_basename += ".aes";
+ outfile_name = append_to_malloced_string(outfile_name, ".aes");
+
+ cmd = ssprintf("openssl aes-128-cbc -in %s -out %s -pass stdin", infile_name.c_str(), outfile_name);
+ WriteCommand(cmd.c_str(), key.c_str());
+ }
+
+ // generate md5sum
+ cmd = ssprintf("cd %s; md5sum <%s", tmpdir_name, outfile_basename.c_str());
+ string md5sum = ReadCommand(cmd.c_str());
+
+ // upload or cp to /tmp
+ if (do_upload)
+ {
+ // FIXME: SendFile isn't working sometime (scp)
+ SendFile(upload_url.c_str(), outfile_name, retry_count, retry_delay);
+ }
+ else
+ {
+ cmd = ssprintf("cp %s /tmp/", outfile_name);
+ RunCommand(cmd.c_str());
+ }
+
+ // generate a reciept telling md5sum and encryption key
+ // note: do not internationalize these strings!
+ string msg;
+ if (have_ticket_name)
+ {
+ msg += "Please copy this into ticket: ";
+ msg += ticket_name;
+ msg += '\n';
+ msg += "========cut here========\n";
+ }
+ else
+ {
+ msg += "Please send this to your technical support:\n";
+ msg += "========cut here========\n";
+ }
+ if (do_upload)
+ {
+ msg += "RHUPLOAD: This report was sent to ";
+ msg += upload_url;
+ msg += '\n';
+ }
+ else
+ {
+ msg += "RHUPLOAD: This report was copied into /tmp/:\n";
+ }
+ if (have_ticket_name)
+ {
+ msg += "TICKET: ";
+ msg += ticket_name;
+ msg += '\n';
+ }
+ msg += "FILE: ";
+ msg += outfile_basename;
+ msg += "\nMD5SUM: ";
+ msg += md5sum;
+ msg += '\n';
+ if (do_encrypt)
+ {
+ msg += "KEY: aes-128-cbc\n";
+ msg += key;
+ msg += '\n';
+ }
+ msg += "==========end===========\n";
+
+ // warn the client (why _warn_? it's not an error, maybe update_client?):
+ //error_msg("%s", msg.c_str());
+
+ // delete the temporary directory
+ cmd = ssprintf("rm -rf %s", tmpdir_name);
+ RunCommand(cmd.c_str());
+
+ free(tmptar_name);
+ free(outfile_name);
+
+ return msg;
+}
+
+static bool is_string_safe(const char *str)
+{
+ const char *p = str;
+ while (*p)
+ {
+ unsigned char c = *p;
+ if ((c < '0' || c > '9')
+ && c != '_'
+ && c != '-'
+ ) {
+ c |= 0x20; // tolower
+ if (c < 'a' || c > 'z')
+ {
+ return false;
+ }
+ }
+ // only 0-9, -, _, A-Z, a-z reach this point
+ p++;
+ }
+ return true;
+}
+
+void CReportUploader::SetSettings(const map_plugin_settings_t& pSettings)
+{
+ m_pSettings = pSettings;
+
+ map_plugin_settings_t::const_iterator end = pSettings.end();
+ map_plugin_settings_t::const_iterator it;
+ it = pSettings.find("Customer");
+ if (it != end)
+ {
+ m_sCustomer = it->second;
+ }
+ // We use m_sTicket as part of filename,
+ // and we use resulting filename in system("cd %s; ...", filename) etc,
+ // so we are very paraniod about allowed chars
+ it = pSettings.find("Ticket");
+ if (it != end && is_string_safe(it->second.c_str()))
+ {
+ m_sTicket = it->second;
+ }
+ it = pSettings.find("URL");
+ if (it != end)
+ {
+ m_sURL = it->second;
+ }
+ it = pSettings.find("Encrypt");
+ if (it != end)
+ {
+ m_bEncrypt = string_to_bool(it->second.c_str());
+ }
+ it = pSettings.find("Upload");
+ if (it != end)
+ {
+ m_bUpload = string_to_bool(it->second.c_str());
+ }
+ it = pSettings.find("RetryCount");
+ if (it != end)
+ {
+ m_nRetryCount = xatoi_u(it->second.c_str());
+ }
+ it = pSettings.find("RetryDelay");
+ if (it != end)
+ {
+ m_nRetryDelay = xatoi_u(it->second.c_str());
+ }
+}
+
+const map_plugin_settings_t& CReportUploader::GetSettings()
+{
+ m_pSettings["Customer"] = m_sCustomer;
+ m_pSettings["Ticket"] = m_sTicket;
+ m_pSettings["URL"] = m_sURL;
+ m_pSettings["Encrypt"] = m_bEncrypt ? "yes" : "no";
+ m_pSettings["Upload"] = m_bUpload ? "yes" : "no";
+ m_pSettings["RetryCount"] = to_string(m_nRetryCount);
+ m_pSettings["RetryDelay"] = to_string(m_nRetryDelay);
+
+ return m_pSettings;
+}
+
+//todo: make static
+map_plugin_settings_t CReportUploader::parse_settings(const map_plugin_settings_t& pSettings)
+{
+ map_plugin_settings_t plugin_settings;
+
+ map_plugin_settings_t::const_iterator end = pSettings.end();
+ map_plugin_settings_t::const_iterator it;
+
+ it = pSettings.find("Customer");
+ if (it == end)
+ {
+ plugin_settings.clear();
+ return plugin_settings;
+ }
+ plugin_settings["Customer"] = it->second;
+
+ it = pSettings.find("Ticket");
+ if (it == end)
+ {
+ plugin_settings.clear();
+ return plugin_settings;
+ }
+ plugin_settings["Ticket"] = it->second;
+
+ it = pSettings.find("URL");
+ if (it == end)
+ {
+ plugin_settings.clear();
+ return plugin_settings;
+ }
+ plugin_settings["URL"] = it->second;
+
+ it = pSettings.find("Encrypt");
+ if (it == end)
+ {
+ plugin_settings.clear();
+ return plugin_settings;
+ }
+ plugin_settings["Encrypt"] = it->second;
+
+ it = pSettings.find("Upload");
+ if (it == end)
+ {
+ plugin_settings.clear();
+ return plugin_settings;
+ }
+ plugin_settings["Upload"] = it->second;
+
+ it = pSettings.find("RetryCount");
+ if (it == end)
+ {
+ plugin_settings.clear();
+ return plugin_settings;
+ }
+ plugin_settings["RetryCount"] = it->second;
+
+ it = pSettings.find("RetryDelay");
+ if (it == end)
+ {
+ plugin_settings.clear();
+ return plugin_settings;
+ }
+ plugin_settings["RetryDelay"] = it->second;
+
+ VERB1 log("User settings ok, using them instead of defaults");
+ return plugin_settings;
+}
+
+PLUGIN_INFO(REPORTER,
+ CReportUploader,
+ "ReportUploader",
+ "0.0.1",
+ _("Packs crash data into .tar.gz file, optionally uploads it via FTP/SCP/etc"),
+ "gavin@redhat.com",
+ "https://fedorahosted.org/abrt/wiki",
+ PLUGINS_LIB_DIR"/ReportUploader.glade");