/***************************************************************** * 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 ( tqwaitCursor ); // 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 ( tqwaitCursor ); // 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) { // }