mirror of
https://github.com/edeproject/ede.git
synced 2023-08-10 21:13:03 +03:00
302 lines
7.0 KiB
C++
302 lines
7.0 KiB
C++
|
/*
|
||
|
* $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;
|
||
|
}
|