summaryrefslogtreecommitdiffstats
path: root/lib/plugins/FileTransfer.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'lib/plugins/FileTransfer.cpp')
-rw-r--r--lib/plugins/FileTransfer.cpp362
1 files changed, 362 insertions, 0 deletions
diff --git a/lib/plugins/FileTransfer.cpp b/lib/plugins/FileTransfer.cpp
new file mode 100644
index 00000000..cf7cf1f3
--- /dev/null
+++ b/lib/plugins/FileTransfer.cpp
@@ -0,0 +1,362 @@
+/*
+ FileTransfer.cpp
+
+ Copyright (C) 2009 Daniel Novotny (dnovotny@redhat.com)
+ 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.
+*/
+#ifndef _GNU_SOURCE
+# define _GNU_SOURCE
+#endif
+#include <libtar.h>
+#include <bzlib.h>
+#include <zlib.h>
+#include "abrtlib.h"
+#include "abrt_curl.h"
+#include "FileTransfer.h"
+#include "debug_dump.h"
+#include "abrt_exception.h"
+#include "comm_layer_inner.h"
+
+using namespace std;
+
+#define HBLEN 255
+#define FILETRANSFER_DIRLIST DEBUG_DUMPS_DIR "/FileTransferDirlist.txt"
+
+CFileTransfer::CFileTransfer()
+:
+ m_sArchiveType(".tar.gz"),
+ m_nRetryCount(3),
+ m_nRetryDelay(20)
+{
+}
+
+void CFileTransfer::SendFile(const char *pURL, const char *pFilename)
+{
+ int len = strlen(pURL);
+ if (len == 0)
+ {
+ error_msg(_("FileTransfer: URL not specified"));
+ return;
+ }
+
+ update_client(_("Sending archive %s to %s"), pFilename, pURL);
+
+ string wholeURL = concat_path_file(pURL, strrchr(pFilename, '/') ? : pFilename);
+
+ int count = m_nRetryCount;
+ while (1)
+ {
+ FILE *f = fopen(pFilename, "r");
+ if (!f)
+ {
+ 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, wholeURL.c_str());
+ /* FILE handle: passed to the default callback, it will fread() it */
+ 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 */
+ int result = curl_easy_perform(curl);
+
+ curl_easy_cleanup(curl);
+ fclose(f);
+ if (result == 0 || --count <= 0)
+ break;
+ /* retry the upload if not succesful, wait a bit before next try */
+ sleep(m_nRetryDelay);
+ }
+}
+
+static void create_tar(const char *archive_name, const char *directory)
+{
+ TAR *tar;
+
+ if (tar_open(&tar, (char *)archive_name, NULL, O_WRONLY | O_CREAT, 0644, TAR_GNU) != 0)
+ {
+ return;
+ }
+ tar_append_tree(tar, (char *)directory, (char*)".");
+ tar_close(tar);
+}
+
+static void create_targz(const char *archive_name, const char *directory)
+{
+ char *name_without_gz = xstrdup(archive_name);
+ strrchr(name_without_gz, '.')[0] = '\0';
+
+ create_tar(name_without_gz, directory);
+
+ int fd = open(name_without_gz, O_RDONLY);
+ if (fd < 0)
+ {
+ remove(name_without_gz);
+ free(name_without_gz);
+ return;
+ }
+
+ gzFile gz = gzopen(archive_name, "w");
+ if (gz == NULL)
+ {
+ close(fd);
+ remove(name_without_gz);
+ free(name_without_gz);
+ return;
+ }
+
+ char buf[BUFSIZ];
+ ssize_t bytesRead;
+ while ((bytesRead = full_read(fd, buf, BUFSIZ)) > 0)
+ {
+ gzwrite(gz, buf, bytesRead); // TODO: check that return value == bytesRead
+ }
+
+ gzclose(gz);
+ close(fd);
+ remove(name_without_gz);
+ free(name_without_gz);
+}
+
+static void create_tarbz2(const char * archive_name, const char * directory)
+{
+ char *name_without_bz2 = xstrdup(archive_name);
+ strrchr(name_without_bz2, '.')[0] = '\0';
+
+ create_tar(name_without_bz2, directory);
+
+ int tarFD = open(name_without_bz2, O_RDONLY);
+ if (tarFD == -1)
+ {
+ remove(name_without_bz2);
+ free(name_without_bz2);
+ return;
+ }
+ FILE *f = fopen(archive_name, "w");
+ if (f == NULL)
+ {
+ close(tarFD);
+ remove(name_without_bz2);
+ free(name_without_bz2);
+ return;
+ }
+ int bzError;
+ BZFILE *bz = BZ2_bzWriteOpen(&bzError, f, /*BLOCK_MULTIPLIER:*/ 7, 0, 0);
+ if (bz == NULL)
+ {
+ fclose(f);
+ close(tarFD);
+ remove(name_without_bz2);
+ free(name_without_bz2);
+ return;
+ }
+
+ char buf[BUFSIZ];
+ ssize_t bytesRead;
+ while ((bytesRead = read(tarFD, buf, BUFSIZ)) > 0)
+ {
+ BZ2_bzWrite(&bzError, bz, buf, bytesRead);
+ }
+
+ BZ2_bzWriteClose(&bzError, bz, 0, NULL, NULL);
+ fclose(f);
+ close(tarFD);
+ remove(name_without_bz2);
+ free(name_without_bz2);
+}
+
+void CFileTransfer::CreateArchive(const char *pArchiveName, const char *pDir)
+{
+ if (m_sArchiveType == ".tar")
+ {
+ create_tar(pArchiveName, pDir);
+ }
+ else if (m_sArchiveType == ".tar.gz")
+ {
+ create_targz(pArchiveName, pDir);
+ }
+ else if (m_sArchiveType == ".tar.bz2")
+ {
+ create_tarbz2(pArchiveName, pDir);
+ }
+ else
+ {
+ throw CABRTException(EXCEP_PLUGIN, "Unknown/unsupported archive type %s", m_sArchiveType.c_str());
+ }
+}
+
+/* Returns the last component of the directory path.
+ * Careful to not return "" on "/path/path2/", but "path2".
+ */
+static string DirBase(const char *pStr)
+{
+ int end = strlen(pStr);
+ if (end > 1 && pStr[end-1] == '/')
+ {
+ end--;
+ }
+ int beg = end;
+ while (beg > 0 && pStr[beg-1] != '/')
+ {
+ beg--;
+ }
+ return string(pStr + beg, end - beg);
+}
+
+void CFileTransfer::Run(const char *pActionDir, const char *pArgs, int force)
+{
+ if (strcmp(pArgs, "store") == 0)
+ {
+ /* Remember pActiveDir for later sending */
+ FILE *dirlist = fopen(FILETRANSFER_DIRLIST, "a");
+ fprintf(dirlist, "%s\n", pActionDir);
+ fclose(dirlist);
+ VERB3 log("Remembered '%s' for future file transfer", pActionDir);
+ return;
+ }
+
+ update_client(_("FileTransfer: Creating a report..."));
+
+ char hostname[HBLEN];
+ gethostname(hostname, HBLEN-1);
+ hostname[HBLEN-1] = '\0';
+
+ char tmpdir_name[] = "/tmp/abrtuploadXXXXXX";
+ /* mkdtemp does mkdir(xxx, 0700), should be safe (is it?) */
+ if (mkdtemp(tmpdir_name) == NULL)
+ {
+ throw CABRTException(EXCEP_PLUGIN, "Can't mkdir a temporary directory in /tmp");
+ }
+
+ if (strcmp(pArgs, "one") == 0)
+ {
+ /* Just send one archive */
+ string archivename = ssprintf("%s/%s-%s%s", tmpdir_name, hostname, DirBase(pActionDir).c_str(), m_sArchiveType.c_str());
+ try
+ {
+ CreateArchive(archivename.c_str(), pActionDir);
+ SendFile(m_sURL.c_str(), archivename.c_str());
+ }
+ catch (CABRTException& e)
+ {
+ error_msg(_("Cannot create and send an archive: %s"), e.what());
+ }
+ unlink(archivename.c_str());
+ }
+ else
+ {
+ /* Tar up and send all remebered directories */
+ FILE *dirlist = fopen(FILETRANSFER_DIRLIST, "r");
+ if (!dirlist)
+ {
+ /* not an error */
+ VERB3 log("No saved crashes to transfer");
+ goto del_tmp_dir;
+ }
+
+ char dirname[PATH_MAX];
+ while (fgets(dirname, sizeof(dirname), dirlist) != NULL)
+ {
+ strchrnul(dirname, '\n')[0] = '\0';
+ string archivename = ssprintf("%s/%s-%s%s", tmpdir_name, hostname, DirBase(dirname).c_str(), m_sArchiveType.c_str());
+ try
+ {
+ VERB3 log("Creating archive '%s' of dir '%s'", archivename.c_str(), dirname);
+ CreateArchive(archivename.c_str(), dirname);
+ VERB3 log("Sending archive to '%s'", m_sURL.c_str());
+ SendFile(m_sURL.c_str(), archivename.c_str());
+ }
+ catch (CABRTException& e)
+ {
+ error_msg(_("Cannot create and send an archive: %s"), e.what());
+ }
+ VERB3 log("Deleting archive '%s'", archivename.c_str());
+ unlink(archivename.c_str());
+ }
+
+ fclose(dirlist);
+ /* all the files we're able to send should be sent now,
+ starting over with clean table */
+ unlink(FILETRANSFER_DIRLIST);
+ }
+
+ del_tmp_dir:
+ rmdir(tmpdir_name);
+}
+
+void CFileTransfer::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("URL");
+ if (it != end)
+ {
+ m_sURL = it->second;
+ }
+
+ 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());
+ }
+
+ it = pSettings.find("ArchiveType");
+ if (it != end)
+ {
+ /* currently supporting .tar, .tar.gz, .tar.bz2 and .zip */
+ m_sArchiveType = it->second;
+ if (m_sArchiveType[0] != '.')
+ {
+ m_sArchiveType = "." + m_sArchiveType;
+ }
+ }
+}
+
+//ok to delete?
+//const map_plugin_settings_t& CFileTransfer::GetSettings()
+//{
+// m_pSettings["URL"] = m_sURL;
+// m_pSettings["RetryCount"] = to_string(m_nRetryCount);
+// m_pSettings["RetryDelay"] = to_string(m_nRetryDelay);
+// m_pSettings["ArchiveType"] = m_sArchiveType;
+//
+// return m_pSettings;
+//}
+
+PLUGIN_INFO(ACTION,
+ CFileTransfer,
+ "FileTransfer",
+ "0.0.6",
+ _("Sends a report via FTP or SCTP"),
+ "dnovotny@redhat.com",
+ "https://fedorahosted.org/abrt/wiki",
+ "");