ede/ede-bug-tools/ede-bug-report/BugzillaSender.cpp

302 lines
7.0 KiB
C++
Raw Normal View History

/*
* $Id$
*
* ede-bug-report, a tool to report bugs on EDE bugzilla instance
* Part of Equinox Desktop Environment (EDE).
* Copyright (c) 2009 EDE Authors.
*
* This program is licensed under terms of the
* GNU General Public License version 2 or newer.
* See COPYING for details.
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
/* used from local xmlrpc-c source */
#include <pthreadx.h>
#include <FL/Fl_Double_Window.H>
#include <FL/Fl_Button.H>
#include <FL/Fl.H>
#include <edelib/Nls.h>
#include <edelib/MessageBox.h>
#include <edelib/String.h>
#include "PulseProgress.h"
#include "BugzillaSender.h"
#include "Bugzilla.h"
#define EDE_BUGZILLA_XMLRPC_URL "http://bugs.equinox-project.org/xmlrpc.cgi"
/* must match to existing user */
#define EDE_BUGZILLA_USER "ede-bugs@lists.sourceforge.net"
#define EDE_BUGZILLA_PASS "edebugs2709"
#define PROGRESS_STEP_REFRESH 0.06
/* message length without '\n' */
#define REPORT_STR_LEN 1024
EDELIB_NS_USING(alert)
EDELIB_NS_USING(message)
EDELIB_NS_USING(String)
/* for easier passing it to the thread */
struct BugContent {
const char *bug_title;
const char *bug_content;
};
static Fl_Double_Window *win;
static PulseProgress *progress;
static BugzillaData *bdata;
static bool data_submitted;
/* we use only one additional thread */
static int report_pipe[2];
static pthread_mutex_t runner_mutex;
/* to prevent sending data; shared between threads */
static bool cancel_sending;
static void write_string(int fd, const char *str) {
int len = strlen(str);
if(len > REPORT_STR_LEN)
len = REPORT_STR_LEN;
::write(fd, str, len);
::write(fd, "\n", 1);
}
/* these are messages prepended with 'FAILED' so we knows how error was emited */
static void write_string_fail(int fd, const char *str) {
int len = strlen(str);
if(len > REPORT_STR_LEN) {
/* our max message size sans 'FAILED' header */
len = REPORT_STR_LEN - 6;
}
::write(fd, "FAILED", 6);
::write(fd, str, len);
::write(fd, "\n", 1);
}
static int read_string(int fd, String& ret) {
char c;
char buf[REPORT_STR_LEN];
int i, nc;
memset(buf, 0, sizeof(buf));
i = 0;
nc = ::read(fd, &c, 1);
while(nc > 0 && c != '\n' && i < REPORT_STR_LEN) {
buf[i] = c;
i = i + nc;
nc = ::read(fd, &c, 1);
}
buf[i] = '\0';
ret = buf;
return i;
}
static void cancel_cb(Fl_Widget*, void*) {
/*
* XXX: we must not use win->hide() here, mostly due sucky xmlrpc-c design. When window
* is going to be closed, but we still have pending RPC connections, we will get assertion from
* xmlrpc-c. To prevent that, we will change cancel_state and hope thread will catch it before
* bugzilla_submit_bug() is called. If it was called, we can't do anything about it, because data
* is already send to Bugzilla so we can only log out from it.
*
* Alternative (and crude) solution would be to comment assertion check xmlrpc_curl_transport.c (977)
* and leave cleaning stuff to the kernel.
*/
pthread_mutex_lock(&runner_mutex);
cancel_sending = true;
pthread_mutex_unlock(&runner_mutex);
}
static void progress_timeout(void*) {
progress->step();
Fl::repeat_timeout(PROGRESS_STEP_REFRESH, progress_timeout);
}
static void clear_timeouts(void) {
Fl::remove_timeout(progress_timeout);
}
static void report_pipe_cb(int fd, void *) {
String s;
if(read_string(report_pipe[0], s) <= 0)
return;
/* check if the message started with 'FAILED' and see it as error */
if(strncmp(s.c_str(), "FAILED", 6) == 0) {
win->hide();
const char *str = s.c_str();
str += 6; /* do not show our header */
alert(str);
}
progress->copy_label(s.c_str());
/* marked as completed successfully */
if(s == "DONE") {
win->hide();
message(_("The report was sent successfully. Thank you for your contribution"));
/* the only case when data was submitted correctly */
data_submitted = true;
return;
}
/* marked as canceled */
if(s == "CANCELED")
win->hide();
}
static void* thread_worker(void *d) {
int ret;
BugContent *data = (BugContent*)d;
bool should_cancel;
ret = bugzilla_login(bdata, EDE_BUGZILLA_USER, EDE_BUGZILLA_PASS);
if(ret == -1) {
write_string_fail(report_pipe[1], _("Unable to properly login. Probably the host is temporarily down or your are not connected to the internet"));
goto done;
}
/* wait some time if user decided to press 'Cancel' */
sleep(1);
pthread_mutex_lock(&runner_mutex);
should_cancel = cancel_sending;
pthread_mutex_unlock(&runner_mutex);
if(should_cancel) {
write_string(report_pipe[1], "CANCELED");
goto done;
}
write_string(report_pipe[1], _("Submitting the report..."));
ret = bugzilla_submit_bug(bdata,
"ede",
"general",
data->bug_title,
"unspecified",
data->bug_content,
"All",
"All",
"P5",
"normal");
if(ret == -1) {
write_string_fail(report_pipe[1], _("Unable to properly submit the data. Please try again"));
goto done;
}
write_string(report_pipe[1], _("Logging out..."));
bugzilla_logout(bdata);
write_string(report_pipe[1], "DONE");
done:
delete data;
pthread_exit(NULL);
/* shutup compiler */
return NULL;
}
static void perform_send(const char *title, const char *content) {
pthread_t thread;
/*
* Must be allocated since this function could quit before thread was created
* of before thread could acceess the arguments. Freeing is done in that thread.
*/
BugContent *c = new BugContent;
c->bug_title = title;
c->bug_content = content;
/* Create joinable thread. Some implementations prefer this explicitly was set. */
pthread_attr_t attr;
pthread_attr_init(&attr);
pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_JOINABLE);
int rc = pthread_create(&thread, &attr, thread_worker, (void*)c);
if(rc) {
alert(_("Unable to create additional thread"));
/* close everything */
win->hide();
}
pthread_attr_destroy(&attr);
}
bool bugzilla_send_with_progress(const char *title, const char *content) {
data_submitted = false;
cancel_sending = false;
bdata = bugzilla_new(EDE_BUGZILLA_XMLRPC_URL);
if(!bdata) {
alert(_("Unable to initialize bugzilla interface!"));
return false;
}
if(pipe(report_pipe) != 0) {
alert(_("Unable to initialize communication pipe"));
return false;
}
/* prepare mutex */
pthread_mutex_init(&runner_mutex, NULL);
/* register our callback on pipe */
Fl::add_fd(report_pipe[0], FL_READ, report_pipe_cb);
win = new Fl_Double_Window(275, 90, _("Sending report data"));
win->begin();
progress = new PulseProgress(10, 20, 255, 25, _("Sending report..."));
progress->selection_color((Fl_Color)137);
Fl_Button *cancel = new Fl_Button(175, 55, 90, 25, _("&Cancel"));
cancel->callback(cancel_cb);
win->end();
win->set_modal();
win->show();
progress->label(_("Preparing data..."));
Fl::add_timeout(PROGRESS_STEP_REFRESH, progress_timeout);
perform_send(title, content);
while(win->shown())
Fl::wait();
/* clear pipe callback */
Fl::remove_fd(report_pipe[0]);
clear_timeouts();
pthread_mutex_destroy(&runner_mutex);
close(report_pipe[0]);
close(report_pipe[1]);
bugzilla_free(bdata);
/* true if we completed all stages */
return data_submitted;
}