You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
520 lines
17 KiB
520 lines
17 KiB
/*****************************************************************
|
|
* drkonqi - The KDE Crash Handler
|
|
*
|
|
* Copyright (C) 2000-2003 Hans Petter Bieker <bieker@kde.org>
|
|
*
|
|
* Redistribution and use in source and binary forms, with or without
|
|
* modification, are permitted provided that the following conditions
|
|
* are met:
|
|
*
|
|
* 1. Redistributions of source code must retain the above copyright
|
|
* notice, this list of conditions and the following disclaimer.
|
|
* 2. Redistributions in binary form must reproduce the above copyright
|
|
* notice, this list of conditions and the following disclaimer in the
|
|
* documentation and/or other materials provided with the distribution.
|
|
*
|
|
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
|
|
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
|
|
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
|
|
* IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
|
|
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
|
|
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
|
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
|
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
|
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
|
|
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
*****************************************************************/
|
|
|
|
#include <tqstring.h>
|
|
#include <tqlabel.h>
|
|
#include <tqhbox.h>
|
|
|
|
#include <tdelocale.h>
|
|
#include <tdeglobal.h>
|
|
#include <kstandarddirs.h>
|
|
#include <kbugreport.h>
|
|
#include <tdefiledialog.h>
|
|
#include <tdemessagebox.h>
|
|
#include <kprocess.h>
|
|
#include <tdeapplication.h>
|
|
#include <dcopclient.h>
|
|
#include <tdetempfile.h>
|
|
|
|
#include "netwm.h"
|
|
|
|
#include "backtrace.h"
|
|
#include "drbugreport.h"
|
|
#include "bugdescription.h"
|
|
#include "debugger.h"
|
|
#include "krashconf.h"
|
|
#include "sha1.h"
|
|
#include "toplevel.h"
|
|
#include "toplevel.moc"
|
|
|
|
Toplevel :: Toplevel(KrashConfig *krashconf, TQWidget *parent, const char *name)
|
|
: KDialogBase( Tabbed,
|
|
krashconf->programName(),
|
|
User3 | User2 | User1 | Close,
|
|
Close,
|
|
parent,
|
|
name,
|
|
true, // modal
|
|
false, // no separator
|
|
i18n("&Bug report"),
|
|
i18n("&Debugger"),
|
|
i18n("&Report Crash")
|
|
),
|
|
m_krashconf(krashconf), m_bugreport(0), m_bugdescription(0)
|
|
{
|
|
TQHBox *page = addHBoxPage(i18n("&General"));
|
|
page->setSpacing(20);
|
|
|
|
// picture of konqi
|
|
TQLabel *lab = new TQLabel(page);
|
|
lab->setFrameStyle(TQFrame::Panel | TQFrame::Sunken);
|
|
TQPixmap pix(locate("appdata", TQString::fromLatin1("pics/konqi.png")));
|
|
lab->setPixmap(pix);
|
|
lab->setFixedSize( lab->sizeHint() );
|
|
|
|
TQLabel * info = new TQLabel(generateText(), page);
|
|
info->setMinimumSize(info->sizeHint());
|
|
|
|
if (m_krashconf->showBacktrace())
|
|
{
|
|
page = addHBoxPage(i18n("&Backtrace"));
|
|
new KrashDebugger(m_krashconf, page);
|
|
}
|
|
|
|
showButton( User1, m_krashconf->showBugReport() );
|
|
showButton( User2, m_krashconf->showDebugger() );
|
|
showButton( User3, true );
|
|
|
|
connect(this, TQT_SIGNAL(closeClicked()), TQT_SLOT(accept()));
|
|
connect(m_krashconf, TQT_SIGNAL(newDebuggingApplication(const TQString&)), TQT_SLOT(slotNewDebuggingApp(const TQString&)));
|
|
|
|
if ( !m_krashconf->safeMode() && kapp->dcopClient()->attach() )
|
|
kapp->dcopClient()->registerAs( kapp->name() );
|
|
}
|
|
|
|
Toplevel :: ~Toplevel()
|
|
{
|
|
}
|
|
|
|
TQString Toplevel :: generateText() const
|
|
{
|
|
TQString str;
|
|
|
|
if (!m_krashconf->errorDescriptionText().isEmpty())
|
|
str += i18n("<p><b>Short description</b></p><p>%1</p>")
|
|
.arg(m_krashconf->errorDescriptionText());
|
|
|
|
if (!m_krashconf->signalText().isEmpty())
|
|
str += i18n("<p><b>What is this?</b></p><p>%1</p>")
|
|
.arg(m_krashconf->signalText());
|
|
|
|
if (!m_krashconf->whatToDoText().isEmpty())
|
|
str += i18n("<p><b>What can I do?</b></p><p>%1</p>")
|
|
.arg(m_krashconf->whatToDoText());
|
|
|
|
// check if the string is still empty. if so, display a default.
|
|
if (str.isEmpty())
|
|
str = i18n("<p><b>Application crashed</b></p>"
|
|
"<p>The program %appname crashed.</p>");
|
|
|
|
// scan the string for %appname etc
|
|
m_krashconf->expandString(str, false);
|
|
|
|
return str;
|
|
}
|
|
|
|
// starting bug report
|
|
void Toplevel :: slotUser1()
|
|
{
|
|
if (m_bugreport)
|
|
return;
|
|
|
|
int i = KMessageBox::No;
|
|
if ( m_krashconf->pid() != 0 )
|
|
i = KMessageBox::warningYesNoCancel
|
|
(0,
|
|
i18n("<p>Do you want to generate a "
|
|
"backtrace? This will help the "
|
|
"developers to figure out what went "
|
|
"wrong.</p>\n"
|
|
"<p>Unfortunately this will take some "
|
|
"time on slow machines.</p>"
|
|
"<p><b>Note: A backtrace is not a "
|
|
"substitute for a proper description "
|
|
"of the bug and information on how to "
|
|
"reproduce it. It is not possible "
|
|
"to fix the bug without a proper "
|
|
"description.</b></p>"),
|
|
i18n("Include Backtrace"),i18n("Generate"),i18n("Do Not Generate"));
|
|
|
|
if (i == KMessageBox::Cancel) return;
|
|
|
|
m_bugreport = new DrKBugReport(0, true, m_krashconf->aboutData());
|
|
|
|
if (i == KMessageBox::Yes) {
|
|
TQApplication::setOverrideCursor ( TQt::waitCursor );
|
|
|
|
// generate the backtrace
|
|
BackTrace *backtrace = new BackTrace(m_krashconf, TQT_TQOBJECT(this));
|
|
connect(backtrace, TQT_SIGNAL(someError()), TQT_SLOT(slotBacktraceSomeError()));
|
|
connect(backtrace, TQT_SIGNAL(done(const TQString &)), TQT_SLOT(slotBacktraceDone(const TQString &)));
|
|
|
|
backtrace->start();
|
|
|
|
return;
|
|
}
|
|
|
|
int result = m_bugreport->exec();
|
|
delete m_bugreport;
|
|
m_bugreport = 0;
|
|
if (result == KDialogBase::Accepted)
|
|
close();
|
|
}
|
|
|
|
void Toplevel :: slotUser2()
|
|
{
|
|
TQString str = m_krashconf->debugger();
|
|
m_krashconf->expandString(str, true);
|
|
|
|
TDEProcess proc;
|
|
proc.setUseShell(true);
|
|
proc << str;
|
|
proc.start(TDEProcess::DontCare);
|
|
}
|
|
|
|
void Toplevel :: slotNewDebuggingApp(const TQString& launchName)
|
|
{
|
|
setButtonText( User3, launchName );
|
|
showButton( User3, true );
|
|
}
|
|
|
|
void Toplevel :: slotUser3()
|
|
{
|
|
enableButton(User3, false);
|
|
TQApplication::setOverrideCursor ( TQt::waitCursor );
|
|
|
|
// generate the backtrace
|
|
BackTrace *backtrace = new BackTrace(m_krashconf, TQT_TQOBJECT(this));
|
|
connect(backtrace, TQT_SIGNAL(someError()), TQT_SLOT(slotSendReportBacktraceSomeError()));
|
|
connect(backtrace, TQT_SIGNAL(done(const TQString &)), TQT_SLOT(slotSendReportBacktraceDone(const TQString &)));
|
|
|
|
backtrace->start();
|
|
|
|
return;
|
|
}
|
|
|
|
void Toplevel :: slotBacktraceDone(const TQString &str)
|
|
{
|
|
// Do not translate.. This will be included in the _MAIL_.
|
|
TQString buf = TQString::fromLatin1
|
|
("\n\n\nHere is a backtrace generated by DrKonqi:\n") + str;
|
|
|
|
m_bugreport->setText(buf);
|
|
|
|
TQApplication::restoreOverrideCursor();
|
|
|
|
m_bugreport->exec();
|
|
delete m_bugreport;
|
|
m_bugreport = 0;
|
|
}
|
|
|
|
void Toplevel :: slotBacktraceSomeError()
|
|
{
|
|
TQApplication::restoreOverrideCursor();
|
|
|
|
KMessageBox::sorry(0, i18n("It was not possible to generate a backtrace."),
|
|
i18n("Backtrace Not Possible"));
|
|
|
|
m_bugreport->exec();
|
|
delete m_bugreport;
|
|
m_bugreport = 0;
|
|
}
|
|
|
|
void Toplevel::slotSendReportBacktraceSomeError()
|
|
{
|
|
TQApplication::restoreOverrideCursor();
|
|
|
|
KMessageBox::sorry(0, i18n("It was not possible to generate a backtrace."), i18n("Backtrace Not Possible"));
|
|
|
|
delete m_bugdescription;
|
|
m_bugdescription = 0;
|
|
|
|
enableButton(User3, true);
|
|
}
|
|
|
|
void Toplevel::slotSendReportBacktraceDone(const TQString &str)
|
|
{
|
|
int i = KMessageBox::No;
|
|
if ( m_krashconf->pid() != 0 ) {
|
|
i = KMessageBox::warningYesNoCancel
|
|
(0,
|
|
i18n("<p>Do you want to include a "
|
|
"description of what you were doing "
|
|
"when this application crashed? This "
|
|
"would help the "
|
|
"developers to figure out what went "
|
|
"wrong.</p>\n"),
|
|
i18n("Include Description"),i18n("Add Description"),i18n("Just Report the Crash"));
|
|
}
|
|
|
|
if (i == KMessageBox::Cancel) {
|
|
TQApplication::restoreOverrideCursor();
|
|
enableButton(User3, true);
|
|
|
|
return;
|
|
}
|
|
|
|
m_bugdescription = new BugDescription(0, true, m_krashconf->aboutData());
|
|
|
|
if (i == KMessageBox::Yes) {
|
|
// Get description
|
|
// Also get Email address if desired
|
|
// Possibly reduce hash difficulty if Email address provided?
|
|
// BugDescription
|
|
if (m_bugdescription->exec() == TQDialog::Rejected) {
|
|
delete m_bugdescription;
|
|
m_bugdescription = 0;
|
|
|
|
return;
|
|
}
|
|
}
|
|
|
|
// Get automatic system information
|
|
TQString autoSystemInformation;
|
|
KBugReport* kbugreport = new KBugReport(0, true, m_krashconf->aboutData());
|
|
autoSystemInformation += "Application: ";
|
|
autoSystemInformation += m_krashconf->appName();
|
|
autoSystemInformation += "\n";
|
|
autoSystemInformation += "Signal: ";
|
|
autoSystemInformation += TQString("%1").arg(m_krashconf->signalNumber());
|
|
autoSystemInformation += "\n";
|
|
autoSystemInformation += "Compiler: ";
|
|
autoSystemInformation += kbugreport->compilerVersion();
|
|
autoSystemInformation += "\n";
|
|
autoSystemInformation += "Kernel: ";
|
|
autoSystemInformation += kbugreport->operatingSystem();
|
|
autoSystemInformation += "\n";
|
|
autoSystemInformation += "TDE Version: ";
|
|
autoSystemInformation += kbugreport->tdeVersion();
|
|
autoSystemInformation += "\n";
|
|
autoSystemInformation += "Timestamp: ";
|
|
autoSystemInformation += TQString("%1").arg(TQDateTime::currentDateTime().toTime_t());
|
|
autoSystemInformation += "\n";
|
|
delete kbugreport;
|
|
kbugreport = 0;
|
|
|
|
// Generate automatic crash description
|
|
TQString autoCrashDescription = m_krashconf->errorDescriptionText();
|
|
m_krashconf->expandString(autoCrashDescription, false);
|
|
|
|
// Generate full crash report
|
|
TQString backtraceSubmission = str;
|
|
backtraceSubmission.append("\n==== (tdebugreport) automatic crash description ====\n");
|
|
backtraceSubmission.append(TQString("%1\n").arg(autoCrashDescription));
|
|
backtraceSubmission.append("\n==== (tdebugreport) automatic system description ====\n");
|
|
backtraceSubmission.append(TQString("%1\n").arg(autoSystemInformation));
|
|
if (m_bugdescription->emailAddress().contains("@") && m_bugdescription->emailAddress().contains(".")) {
|
|
backtraceSubmission.append("\n==== (tdebugreport) reporting Email address ====\n");
|
|
backtraceSubmission.append(TQString("%1\n").arg(m_bugdescription->emailAddress()));
|
|
}
|
|
if (m_bugdescription->crashDescription() != "") {
|
|
backtraceSubmission.append("\n==== (tdebugreport) user-generated crash description ====\n");
|
|
backtraceSubmission.append(TQString("%1\n").arg(m_bugdescription->crashDescription()));
|
|
}
|
|
|
|
// Calculate proof-of-work hash
|
|
SHA1 sha;
|
|
TQByteArray hash(sha.size() / 8);
|
|
hash.fill(255);
|
|
|
|
backtraceSubmission.append("\n==== (tdebugreport) proof of work ====\n");
|
|
int proofOfWorkPos = backtraceSubmission.length();
|
|
backtraceSubmission.append(TQUuid::createUuid().toString());
|
|
m_backtraceSubmissionData = TQCString(backtraceSubmission.ascii());
|
|
|
|
while ((hash[0] != 0) || ((hash[1] & 0xfc) != 0)) { // First 14 bits of the SHA1 hash must be zero
|
|
TQCString proofOfWork(TQUuid::createUuid().toString().ascii());
|
|
memcpy(m_backtraceSubmissionData.data() + proofOfWorkPos, proofOfWork.data(), proofOfWork.size());
|
|
sha.reset();
|
|
sha.process(m_backtraceSubmissionData.data(), m_backtraceSubmissionData.size()-1);
|
|
memcpy(hash.data(), sha.hash(), hash.size());
|
|
}
|
|
|
|
TQApplication::restoreOverrideCursor();
|
|
|
|
i = KMessageBox::Yes;
|
|
while (i == KMessageBox::Yes) {
|
|
i = KMessageBox::warningYesNoCancel
|
|
(0,
|
|
i18n("<p>The crash report is ready. Do you want to send it now?</p>\n"),
|
|
i18n("Ready to Send"),i18n("View Report"),i18n("Send Report"));
|
|
|
|
if (i == KMessageBox::Cancel) {
|
|
delete m_bugdescription;
|
|
m_bugdescription = 0;
|
|
enableButton(User3, true);
|
|
|
|
return;
|
|
}
|
|
|
|
if (i == KMessageBox::Yes) {
|
|
BugDescription fullReport(0, true, NULL);
|
|
fullReport.fullReportViewMode(true);
|
|
fullReport.setText(TQString(m_backtraceSubmissionData.data()));
|
|
fullReport.showMaximized();
|
|
fullReport.exec();
|
|
}
|
|
}
|
|
|
|
postCrashDataToServer(m_backtraceSubmissionData);
|
|
|
|
delete m_bugdescription;
|
|
m_bugdescription = 0;
|
|
}
|
|
|
|
int Toplevel::postCrashDataToServer(TQCString data) {
|
|
m_serverResponse = "";
|
|
TQCString formDataBoundary = "-----------------------------------DrKonqiCrashReporterBoundary";
|
|
|
|
TQCString postData;
|
|
postData += "--";
|
|
postData += formDataBoundary;
|
|
postData += "\r\n";
|
|
postData += "Content-Disposition: form-data; name=\"crashreport\"; filename=\"crashreport.txt\"\r\n";
|
|
postData += "Content-Type: application/octet-stream\r\n";
|
|
postData += (TQString("Content-Length: %1\r\n").arg(data.count())).ascii();
|
|
postData += "Content-Transfer-Encoding: binary\r\n\r\n";
|
|
postData += data;
|
|
postData += "\r\n";
|
|
postData += "--";
|
|
postData += formDataBoundary;
|
|
postData += "--\r\n";
|
|
|
|
KURL url("https://crashreport.trinitydesktop.org/");
|
|
// TDEIO::TransferJob* job = TDEIO::http_post(url, postData, false);
|
|
TDEIO::TransferJob* job = TDEIO::http_post(url, postData, true);
|
|
job->addMetaData("content-type", TQString("Content-Type: multipart/form-data; boundary=%1").arg(formDataBoundary));
|
|
job->addMetaData("referrer", "http://drkonqi-client.crashreport.trinitydesktop.org");
|
|
connect(job, TQT_SIGNAL(data(TDEIO::Job *, const TQByteArray &)), TQT_SLOT(postCrashDataToServerData(TDEIO::Job *, const TQByteArray &)));
|
|
connect(job, TQT_SIGNAL(result(TDEIO::Job *)), TQT_SLOT(postCrashDataToServerResult(TDEIO::Job *)));
|
|
// connect(job, TQT_SIGNAL(totalSize(TDEIO::Job *, TDEIO::filesize_t )),
|
|
// TQT_SLOT(totalSize(TDEIO::Job *, TDEIO::filesize_t)));
|
|
// connect(job, TQT_SIGNAL(mimetype(TDEIO::Job *, const TQString &)),
|
|
// TQT_SLOT(mimetype(TDEIO::Job *, const TQString &)));
|
|
connect(job, TQT_SIGNAL(redirection(TDEIO::Job *, const KURL&)), TQT_SLOT(postCrashDataToServerDataRedirection(TDEIO::Job *, const KURL&)));
|
|
|
|
return 0;
|
|
}
|
|
|
|
void Toplevel::postCrashDataToServerData(TDEIO::Job *, const TQByteArray &ba)
|
|
{
|
|
uint offset = 0;
|
|
if (m_serverResponse.count() > 0) {
|
|
offset = m_serverResponse.count() - 1;
|
|
}
|
|
uint size = ba.count();
|
|
|
|
m_serverResponse.resize(offset + size + 1);
|
|
memcpy(m_serverResponse.data() + offset, ba.data(), size);
|
|
*(m_serverResponse.data() + offset + size) = 0;
|
|
}
|
|
|
|
void Toplevel::postCrashDataToServerResult(TDEIO::Job *job)
|
|
{
|
|
int err = job->error();
|
|
if (err == 0) {
|
|
TQString responseString(m_serverResponse);
|
|
if (responseString.startsWith("ACK\n")) {
|
|
responseString = responseString.mid(4);
|
|
KMessageBox::information
|
|
(0,
|
|
i18n("<p>Your crash report has been uploaded!</p><p>You may reference it if desired by its unique ID:<br>%1</p>").arg(responseString),
|
|
i18n("Report uploaded"));
|
|
close();
|
|
}
|
|
else {
|
|
responseString = responseString.mid(4);
|
|
// KMessageBox::error
|
|
// (0,
|
|
// i18n("<p>Your crash report failed to upload!</p><p>Please check your network settings and try again.</p><p>The server responded:<br>%1</p>").arg(responseString),
|
|
// i18n("Upload failure"));
|
|
|
|
int i = KMessageBox::warningYesNoCancel
|
|
(0,
|
|
i18n("<p>Your crash report failed to upload!</p><p>Please check your network settings and try again.</p><p>The server responded:<br>%1</p>").arg(responseString),
|
|
i18n("Upload failure"),i18n("Save Report"),i18n("Retry Upload"));
|
|
|
|
if (i == KMessageBox::No) {
|
|
postCrashDataToServer(m_backtraceSubmissionData);
|
|
}
|
|
else if (i == KMessageBox::Yes) {
|
|
saveOfflineCrashReport(m_backtraceSubmissionData);
|
|
}
|
|
else {
|
|
enableButton(User3, true);
|
|
}
|
|
}
|
|
}
|
|
else {
|
|
int i = KMessageBox::warningYesNoCancel
|
|
(0,
|
|
i18n("<p>Your crash report failed to upload!</p><p>Please check your network settings and try again.</p>"),
|
|
i18n("Upload failure"),i18n("Save Report"),i18n("Retry Upload"));
|
|
|
|
if (i == KMessageBox::No) {
|
|
postCrashDataToServer(m_backtraceSubmissionData);
|
|
}
|
|
else if (i == KMessageBox::Yes) {
|
|
saveOfflineCrashReport(m_backtraceSubmissionData);
|
|
}
|
|
else {
|
|
enableButton(User3, true);
|
|
}
|
|
}
|
|
}
|
|
|
|
int Toplevel::saveOfflineCrashReport(TQCString data)
|
|
{
|
|
TQString defname = m_krashconf->execName() + TQString::fromLatin1( ".tdecrash" );
|
|
if( defname.contains( '/' ))
|
|
defname = defname.mid( defname.findRev( '/' ) + 1 );
|
|
TQString filename = KFileDialog::getSaveFileName(defname, TQString::null, this, i18n("Select Filename"));
|
|
if (filename.isEmpty()) {
|
|
enableButton(User3, true);
|
|
return 1;
|
|
}
|
|
else {
|
|
TQFile f(filename);
|
|
|
|
if (f.exists()) {
|
|
if (KMessageBox::Cancel ==
|
|
KMessageBox::warningContinueCancel( 0,
|
|
i18n( "A file named \"%1\" already exists. "
|
|
"Are you sure you want to overwrite it?" ).arg( filename ),
|
|
i18n( "Overwrite File?" ),
|
|
i18n( "&Overwrite" ) ))
|
|
return 2;
|
|
}
|
|
|
|
if (f.open(IO_WriteOnly)) {
|
|
f.writeBlock(data.data(), data.count()-1);
|
|
f.close();
|
|
enableButton(User3, true);
|
|
return 0;
|
|
}
|
|
else {
|
|
KMessageBox::sorry(this, i18n("Cannot open file %1 for writing").arg(filename));
|
|
enableButton(User3, true);
|
|
return 3;
|
|
}
|
|
}
|
|
}
|
|
|
|
void Toplevel::postCrashDataToServerDataRedirection(TDEIO::Job * /*job*/, const KURL& url)
|
|
{
|
|
//
|
|
} |