KDEUI
kbugreport.cpp
Go to the documentation of this file.
00001 /* This file is part of the KDE project 00002 Copyright (C) 1999 David Faure <faure@kde.org> 00003 00004 This library is free software; you can redistribute it and/or 00005 modify it under the terms of the GNU Library General Public 00006 License as published by the Free Software Foundation; either 00007 version 2 of the License, or (at your option) any later version. 00008 00009 This library is distributed in the hope that it will be useful, 00010 but WITHOUT ANY WARRANTY; without even the implied warranty of 00011 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 00012 Library General Public License for more details. 00013 00014 You should have received a copy of the GNU Library General Public License 00015 along with this library; see the file COPYING.LIB. If not, write to 00016 the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, 00017 Boston, MA 02110-1301, USA. 00018 */ 00019 00020 #include "kbugreport.h" 00021 00022 #include <QtCore/QProcess> 00023 #include <QtCore/QCoreApplication> 00024 #include <QtGui/QPushButton> 00025 #include <QtGui/QLayout> 00026 #include <QtGui/QRadioButton> 00027 #include <QtGui/QGroupBox> 00028 #include <QtGui/QCloseEvent> 00029 00030 #include <kaboutdata.h> 00031 #include <kcombobox.h> 00032 #include <ktoolinvocation.h> 00033 #include <kdebug.h> 00034 #include <klineedit.h> 00035 #include <klocale.h> 00036 #include <kmessagebox.h> 00037 #include <kstandarddirs.h> 00038 #include <kcomponentdata.h> 00039 #include <kurllabel.h> 00040 #include <ktextedit.h> 00041 #include <ktitlewidget.h> 00042 00043 #include <stdio.h> 00044 #include <pwd.h> 00045 #include <unistd.h> 00046 #include <sys/utsname.h> 00047 00048 #include "kdepackages.h" 00049 #include "kdeversion.h" 00050 00051 #include <config-compiler.h> 00052 00053 class KBugReportPrivate { 00054 public: 00055 KBugReportPrivate(KBugReport *q): q(q) {} 00056 00057 void _k_slotConfigureEmail(); 00058 void _k_slotSetFrom(); 00059 void _k_appChanged(int); 00060 void _k_updateUrl(); 00061 00062 KBugReport *q; 00063 QProcess * m_process; 00064 const KAboutData * m_aboutData; 00065 00066 KTextEdit * m_lineedit; 00067 KLineEdit * m_subject; 00068 QLabel * m_from; 00069 QLabel * m_version; 00070 QString m_strVersion; 00071 QGroupBox * m_bgSeverity; 00072 QPushButton * m_configureEmail; 00073 00074 KComboBox *appcombo; 00075 QString lastError; 00076 QString kde_version; 00077 QString appname; 00078 QString os; 00079 KUrl url; 00080 QList<QRadioButton*> severityButtons; 00081 int currentSeverity() { 00082 for (int i=0;i<severityButtons.count();i++) 00083 if (severityButtons[i]->isChecked()) return i; 00084 return -1; 00085 } 00086 bool submitBugWeb; 00087 }; 00088 00089 KBugReport::KBugReport( QWidget * _parent, bool modal, const KAboutData *aboutData ) 00090 : KDialog( _parent ), d( new KBugReportPrivate(this) ) 00091 { 00092 setCaption( i18n("Submit Bug Report") ); 00093 setButtons( Ok | Cancel ); 00094 setModal(modal); 00095 00096 // Use supplied aboutdata, otherwise the one from the active componentData 00097 // otherwise the KGlobal one. _activeInstance should neved be 0L in theory. 00098 d->m_aboutData = aboutData ? aboutData 00099 : (KGlobal::activeComponent().isValid() ? KGlobal::activeComponent().aboutData() 00100 : KGlobal::mainComponent().aboutData()); 00101 d->m_process = 0; 00102 QWidget * parent = new QWidget(this); 00103 d->submitBugWeb = false; 00104 00105 if ( d->m_aboutData->bugAddress() == QLatin1String("submit@bugs.kde.org") ) 00106 { 00107 // This is a core KDE application -> redirect to the web form 00108 d->submitBugWeb = true; 00109 setButtonGuiItem( Cancel, KStandardGuiItem::close() ); 00110 } 00111 00112 QLabel * tmpLabel; 00113 QVBoxLayout * lay = new QVBoxLayout( parent); 00114 00115 KTitleWidget *title = new KTitleWidget( this ); 00116 title->setText(i18n( "Submit Bug Report" ) ); 00117 title->setPixmap( KIcon( "tools-report-bug" ).pixmap( 32 ) ); 00118 lay->addWidget( title ); 00119 00120 QGridLayout *glay = new QGridLayout(); 00121 lay->addLayout(glay); 00122 00123 int row = 0; 00124 00125 if ( !d->submitBugWeb ) 00126 { 00127 // From 00128 QString qwtstr = i18n( "Your email address. If incorrect, use the Configure Email button to change it" ); 00129 tmpLabel = new QLabel( i18nc("Email sender address", "From:"), parent ); 00130 glay->addWidget( tmpLabel, row,0 ); 00131 tmpLabel->setWhatsThis(qwtstr ); 00132 d->m_from = new QLabel( parent ); 00133 glay->addWidget( d->m_from, row, 1 ); 00134 d->m_from->setWhatsThis(qwtstr ); 00135 00136 00137 // Configure email button 00138 d->m_configureEmail = new QPushButton( i18n("Configure Email..."), 00139 parent ); 00140 connect( d->m_configureEmail, SIGNAL( clicked() ), this, 00141 SLOT( _k_slotConfigureEmail() ) ); 00142 glay->addWidget( d->m_configureEmail, 0, 2, 3, 1, Qt::AlignTop|Qt::AlignRight ); 00143 00144 // To 00145 qwtstr = i18n( "The email address this bug report is sent to." ); 00146 tmpLabel = new QLabel( i18nc("Email receiver address", "To:"), parent ); 00147 glay->addWidget( tmpLabel, ++row,0 ); 00148 tmpLabel->setWhatsThis(qwtstr ); 00149 tmpLabel = new QLabel( d->m_aboutData->bugAddress(), parent ); 00150 tmpLabel->setTextInteractionFlags(Qt::TextBrowserInteraction); 00151 glay->addWidget( tmpLabel, row, 1 ); 00152 tmpLabel->setWhatsThis(qwtstr ); 00153 00154 setButtonGuiItem( Ok, KGuiItem( i18n("&Send"), "mail-send", i18n( "Send bug report." ), 00155 i18n( "Send this bug report to %1." , d->m_aboutData->bugAddress() ) ) ); 00156 row++; 00157 } 00158 else 00159 { 00160 d->m_configureEmail = 0; 00161 d->m_from = 0; 00162 } 00163 00164 // Program name 00165 QString qwtstr = i18n( "The application for which you wish to submit a bug report - if incorrect, please use the Report Bug menu item of the correct application" ); 00166 tmpLabel = new QLabel( i18n("Application: "), parent ); 00167 glay->addWidget( tmpLabel, row, 0 ); 00168 tmpLabel->setWhatsThis(qwtstr ); 00169 d->appcombo = new KComboBox( false, parent ); 00170 d->appcombo->setWhatsThis(qwtstr ); 00171 QStringList packageList; 00172 for (int c = 0; packages[c]; ++c) 00173 packageList << QString::fromLatin1(packages[c]); 00174 d->appcombo->addItems(packageList); 00175 connect(d->appcombo, SIGNAL(activated(int)), SLOT(_k_appChanged(int))); 00176 d->appname = d->m_aboutData 00177 ? d->m_aboutData->productName() 00178 : qApp->applicationName() ; 00179 glay->addWidget( d->appcombo, row, 1 ); 00180 int index = 0; 00181 for (; index < d->appcombo->count(); index++) { 00182 if (d->appcombo->itemText(index) == d->appname) { 00183 break; 00184 } 00185 } 00186 if (index == d->appcombo->count()) { // not present 00187 d->appcombo->addItem(d->appname); 00188 } 00189 d->appcombo->setCurrentIndex(index); 00190 00191 tmpLabel->setWhatsThis(qwtstr ); 00192 00193 // Version 00194 qwtstr = i18n( "The version of this application - please make sure that no newer version is available before sending a bug report" ); 00195 tmpLabel = new QLabel( i18n("Version:"), parent ); 00196 glay->addWidget( tmpLabel, ++row, 0 ); 00197 tmpLabel->setWhatsThis(qwtstr ); 00198 if (d->m_aboutData) 00199 d->m_strVersion = d->m_aboutData->version(); 00200 else 00201 d->m_strVersion = i18n("no version set (programmer error)"); 00202 d->kde_version = QString::fromLatin1( KDE_VERSION_STRING ); 00203 d->kde_version += ", " + QString::fromLatin1( KDE_DISTRIBUTION_TEXT ); 00204 if ( !d->submitBugWeb ) 00205 d->m_strVersion += ' ' + d->kde_version; 00206 d->m_version = new QLabel( d->m_strVersion, parent ); 00207 d->m_version->setTextInteractionFlags(Qt::TextBrowserInteraction); 00208 //glay->addWidget( d->m_version, row, 1 ); 00209 glay->addWidget( d->m_version, row, 1, 1, 2 ); 00210 d->m_version->setWhatsThis(qwtstr ); 00211 00212 tmpLabel = new QLabel(i18n("OS:"), parent); 00213 glay->addWidget( tmpLabel, ++row, 0 ); 00214 00215 struct utsname unameBuf; 00216 uname( &unameBuf ); 00217 d->os = QString::fromLatin1( unameBuf.sysname ) + 00218 " (" + QString::fromLatin1( unameBuf.machine ) + ") " 00219 "release " + QString::fromLatin1( unameBuf.release ); 00220 00221 tmpLabel = new QLabel(d->os, parent); 00222 tmpLabel->setTextInteractionFlags(Qt::TextBrowserInteraction); 00223 glay->addWidget( tmpLabel, row, 1, 1, 2 ); 00224 00225 tmpLabel = new QLabel(i18n("Compiler:"), parent); 00226 glay->addWidget( tmpLabel, ++row, 0 ); 00227 tmpLabel = new QLabel(QString::fromLatin1(KDE_COMPILER_VERSION), parent); 00228 tmpLabel->setTextInteractionFlags(Qt::TextBrowserInteraction); 00229 glay->addWidget( tmpLabel, row, 1, 1, 2 ); 00230 00231 if ( !d->submitBugWeb ) 00232 { 00233 // Severity 00234 d->m_bgSeverity = new QGroupBox( i18n("Se&verity"), parent ); 00235 static const char * const sevNames[5] = { "critical", "grave", "normal", "wishlist", "i18n" }; 00236 const QString sevTexts[5] = { i18n("Critical"), i18n("Grave"), i18nc("normal severity","Normal"), i18n("Wishlist"), i18n("Translation") }; 00237 QHBoxLayout *severityLayout=new QHBoxLayout(d->m_bgSeverity); 00238 for (int i = 0 ; i < 5 ; i++ ) 00239 { 00240 // Store the severity string as the name 00241 QRadioButton *rb = new QRadioButton( sevTexts[i], d->m_bgSeverity); 00242 rb->setObjectName(sevNames[i] ); 00243 d->severityButtons.append(rb); 00244 severityLayout->addWidget(rb); 00245 if (i==2) rb->setChecked(true); // default : "normal" 00246 } 00247 00248 lay->addWidget( d->m_bgSeverity ); 00249 00250 // Subject 00251 QHBoxLayout * hlay = new QHBoxLayout(); 00252 lay->addItem(hlay); 00253 tmpLabel = new QLabel( i18n("S&ubject: "), parent ); 00254 hlay->addWidget( tmpLabel ); 00255 d->m_subject = new KLineEdit( parent ); 00256 d->m_subject->setClearButtonShown(true); 00257 d->m_subject->setFocus(); 00258 tmpLabel->setBuddy( d->m_subject ); 00259 hlay->addWidget( d->m_subject ); 00260 00261 QString text = i18n("Enter the text (in English if possible) that you wish to submit for the " 00262 "bug report.\n" 00263 "If you press \"Send\", a mail message will be sent to the maintainer of " 00264 "this program.\n"); 00265 QLabel * label = new QLabel( parent); 00266 00267 label->setText( text ); 00268 lay->addWidget( label ); 00269 00270 // The multiline-edit 00271 d->m_lineedit = new KTextEdit( parent); 00272 d->m_lineedit->setMinimumHeight( 180 ); // make it big 00273 d->m_lineedit->setWordWrapMode(QTextOption::WrapAtWordBoundaryOrAnywhere); 00274 d->m_lineedit->setLineWrapMode(QTextEdit::WidgetWidth); 00275 d->m_lineedit->setCheckSpellingEnabled(true); 00276 d->m_lineedit->setSpellCheckingLanguage("en"); 00277 lay->addWidget( d->m_lineedit, 10 /*stretch*/ ); 00278 00279 d->_k_slotSetFrom(); 00280 } else { 00281 // Point to the web form 00282 00283 lay->addSpacing(10); 00284 QString text = i18n("<qt>To submit a bug report, click on the button below. This will open a web browser " 00285 "window on <a href=\"http://bugs.kde.org\">http://bugs.kde.org</a> where you will find " 00286 "a form to fill in. The information displayed above will be transferred to that server.</qt>"); 00287 QLabel * label = new QLabel( text, parent); 00288 label->setOpenExternalLinks( true ); 00289 label->setTextInteractionFlags( Qt::LinksAccessibleByMouse | Qt::LinksAccessibleByKeyboard ); 00290 label->setWordWrap( true ); 00291 lay->addWidget( label ); 00292 lay->addSpacing(10); 00293 00294 d->appcombo->setFocus(); 00295 00296 d->_k_updateUrl(); 00297 00298 setButtonText(Ok, i18n("&Launch Bug Report Wizard")); 00299 setButtonIcon(Ok, KIcon("tools-report-bug")); 00300 } 00301 parent->setMinimumHeight( parent->sizeHint().height() + 20 ); // WORKAROUND: prevent "cropped" kcombobox 00302 setMainWidget(parent); 00303 } 00304 00305 KBugReport::~KBugReport() 00306 { 00307 delete d; 00308 } 00309 00310 QString KBugReport::messageBody() const 00311 { 00312 if ( !d->submitBugWeb ) 00313 return d->m_lineedit->toPlainText(); 00314 else 00315 return QString(); 00316 } 00317 00318 void KBugReport::setMessageBody(const QString &messageBody) 00319 { 00320 if ( !d->submitBugWeb ) 00321 d->m_lineedit->setPlainText(messageBody); 00322 } 00323 00324 void KBugReportPrivate::_k_updateUrl() 00325 { 00326 url = KUrl( "https://bugs.kde.org/wizard.cgi" ); 00327 url.addQueryItem( "os", os ); 00328 url.addQueryItem( "compiler", KDE_COMPILER_VERSION ); 00329 url.addQueryItem( "kdeVersion", kde_version ); 00330 url.addQueryItem( "appVersion", m_strVersion ); 00331 url.addQueryItem( "package", appcombo->currentText() ); 00332 url.addQueryItem( "kbugreport", "1" ); 00333 } 00334 00335 void KBugReportPrivate::_k_appChanged(int i) 00336 { 00337 QString appName = appcombo->itemText(i); 00338 int index = appName.indexOf( '/' ); 00339 if ( index > 0 ) 00340 appName = appName.left( index ); 00341 kDebug() << "appName " << appName; 00342 00343 QString strDisplayVersion; //Version string to show in the UI 00344 if (appname == appName && m_aboutData) { 00345 m_strVersion = m_aboutData->version(); 00346 strDisplayVersion = m_strVersion; 00347 } else { 00348 m_strVersion = QLatin1String("unknown"); //English string to put in the bug report 00349 strDisplayVersion = i18nc("unknown program name", "unknown"); 00350 } 00351 00352 if ( !submitBugWeb ) { 00353 m_strVersion += ' ' + kde_version; 00354 strDisplayVersion += ' ' + kde_version; 00355 } 00356 00357 m_version->setText(strDisplayVersion); 00358 if ( submitBugWeb ) 00359 _k_updateUrl(); 00360 } 00361 00362 void KBugReportPrivate::_k_slotConfigureEmail() 00363 { 00364 if (m_process) return; 00365 m_process = new QProcess; 00366 QObject::connect( m_process, SIGNAL(finished( int, QProcess::ExitStatus )), q, SLOT(_k_slotSetFrom()) ); 00367 m_process->start( QString::fromLatin1("kcmshell4"), QStringList() << QString::fromLatin1("kcm_useraccount") ); 00368 if ( !m_process->waitForStarted() ) 00369 { 00370 kDebug() << "Couldn't start kcmshell4.."; 00371 delete m_process; 00372 m_process = 0; 00373 return; 00374 } 00375 m_configureEmail->setEnabled(false); 00376 } 00377 00378 void KBugReportPrivate::_k_slotSetFrom() 00379 { 00380 delete m_process; 00381 m_process = 0; 00382 m_configureEmail->setEnabled(true); 00383 00384 // ### KDE4: why oh why is KEmailSettings in kio? 00385 KConfig emailConf( QString::fromLatin1("emaildefaults") ); 00386 00387 KConfigGroup cg(&emailConf, "Defaults"); 00388 // find out the default profile 00389 QString profile = QString::fromLatin1("PROFILE_"); 00390 profile += cg.readEntry( QString::fromLatin1("Profile"), 00391 QString::fromLatin1("Default") ); 00392 00393 KConfigGroup profileGrp(&emailConf, profile ); 00394 QString fromaddr = profileGrp.readEntry( "EmailAddress" ); 00395 if (fromaddr.isEmpty()) { 00396 struct passwd *p; 00397 p = getpwuid(getuid()); 00398 fromaddr = QString::fromLatin1(p->pw_name); 00399 } else { 00400 QString name = profileGrp.readEntry( "FullName" ); 00401 if (!name.isEmpty()) 00402 fromaddr = name + QString::fromLatin1(" <") + fromaddr + QString::fromLatin1(">"); 00403 } 00404 m_from->setText( fromaddr ); 00405 } 00406 00407 void KBugReport::accept() 00408 { 00409 if ( d->submitBugWeb ) { 00410 KToolInvocation::invokeBrowser( d->url.url() ); 00411 return; 00412 } 00413 00414 if( d->m_lineedit->toPlainText().isEmpty() || 00415 d->m_subject->text().isEmpty() ) 00416 { 00417 QString msg = i18n("You must specify both a subject and a description " 00418 "before the report can be sent."); 00419 KMessageBox::error(this,msg); 00420 return; 00421 } 00422 00423 switch ( d->currentSeverity()) 00424 { 00425 case 0: // critical 00426 if ( KMessageBox::questionYesNo( this, i18n( 00427 "<p>You chose the severity <b>Critical</b>. " 00428 "Please note that this severity is intended only for bugs that:</p>" 00429 "<ul><li>break unrelated software on the system (or the whole system)</li>" 00430 "<li>cause serious data loss</li>" 00431 "<li>introduce a security hole on the system where the affected package is installed</li></ul>\n" 00432 "<p>Does the bug you are reporting cause any of the above damage? " 00433 "If it does not, please select a lower severity. Thank you.</p>" ),QString(),KStandardGuiItem::cont(),KStandardGuiItem::cancel() ) == KMessageBox::No ) 00434 return; 00435 break; 00436 case 1: // grave 00437 if ( KMessageBox::questionYesNo( this, i18n( 00438 "<p>You chose the severity <b>Grave</b>. " 00439 "Please note that this severity is intended only for bugs that:</p>" 00440 "<ul><li>make the package in question unusable or mostly so</li>" 00441 "<li>cause data loss</li>" 00442 "<li>introduce a security hole allowing access to the accounts of users who use the affected package</li></ul>\n" 00443 "<p>Does the bug you are reporting cause any of the above damage? " 00444 "If it does not, please select a lower severity. Thank you.</p>" ),QString(),KStandardGuiItem::cont(),KStandardGuiItem::cancel() ) == KMessageBox::No ) 00445 return; 00446 break; 00447 default: 00448 break; 00449 } 00450 if( !sendBugReport() ) 00451 { 00452 QString msg = i18n("Unable to send the bug report.\n" 00453 "Please submit a bug report manually....\n" 00454 "See http://bugs.kde.org/ for instructions."); 00455 KMessageBox::error(this, msg + "\n\n" + d->lastError); 00456 return; 00457 } 00458 00459 KMessageBox::information(this, 00460 i18n("Bug report sent, thank you for your input.")); 00461 KDialog::accept(); 00462 } 00463 00464 void KBugReport::closeEvent( QCloseEvent * e) 00465 { 00466 if( !d->submitBugWeb && ( (d->m_lineedit->toPlainText().length()>0) || d->m_subject->isModified() ) ) 00467 { 00468 int rc = KMessageBox::warningYesNo( this, 00469 i18n( "Close and discard\nedited message?" ), 00470 i18n( "Close Message" ), KStandardGuiItem::discard(), KStandardGuiItem::cont() ); 00471 if( rc == KMessageBox::No ) 00472 { 00473 e->ignore(); 00474 return; 00475 } 00476 } 00477 KDialog::closeEvent(e); 00478 } 00479 00480 00481 QString KBugReport::text() const 00482 { 00483 kDebug() << d->severityButtons[d->currentSeverity()]->objectName(); 00484 // Prepend the pseudo-headers to the contents of the mail 00485 QString severity = d->severityButtons[d->currentSeverity()]->objectName(); 00486 QString appname = d->appcombo->currentText(); 00487 QString os = QString::fromLatin1("OS: %1 (%2)\n"). 00488 arg(KDE_COMPILING_OS). 00489 arg(KDE_DISTRIBUTION_TEXT); 00490 QString bodyText; 00491 /* for(int i = 0; i < m_lineedit->numLines(); i++) 00492 { 00493 QString line = m_lineedit->textLine(i); 00494 if (!line.endsWith("\n")) 00495 line += '\n'; 00496 bodyText += line; 00497 } 00498 */ 00499 bodyText=d->m_lineedit->toPlainText(); 00500 if (bodyText.length()>0) 00501 if (bodyText[bodyText.length()-1]!='\n') bodyText+='\n'; 00502 if (severity == QLatin1String("i18n") && KGlobal::locale()->language() != KLocale::defaultLanguage()) { 00503 // Case 1 : i18n bug 00504 QString package = QString::fromLatin1("i18n_%1").arg(KGlobal::locale()->language()); 00505 package = package.replace('_', '-'); 00506 return QString::fromLatin1("Package: %1").arg(package) + 00507 QString::fromLatin1("\n" 00508 "Application: %1\n" 00509 // not really i18n's version, so better here IMHO 00510 "Version: %2\n").arg(appname).arg(d->m_strVersion)+ 00511 os+QString::fromLatin1("\n")+bodyText; 00512 } else { 00513 appname = appname.replace('_', '-'); 00514 // Case 2 : normal bug 00515 return QString::fromLatin1("Package: %1\n" 00516 "Version: %2\n" 00517 "Severity: %3\n") 00518 .arg(appname).arg(d->m_strVersion).arg(severity)+ 00519 QString::fromLatin1("Compiler: %1\n").arg(KDE_COMPILER_VERSION)+ 00520 os+QString::fromLatin1("\n")+bodyText; 00521 } 00522 } 00523 00524 bool KBugReport::sendBugReport() 00525 { 00526 QString recipient ( d->m_aboutData ? 00527 d->m_aboutData->bugAddress() : 00528 QString::fromLatin1("submit@bugs.kde.org") ); 00529 00530 QString command; 00531 command = KStandardDirs::locate("exe", "ksendbugmail"); 00532 if (command.isEmpty()) 00533 command = KStandardDirs::findExe( QString::fromLatin1("ksendbugmail") ); 00534 00535 QProcess proc; 00536 QStringList args; 00537 args << "--subject" << d->m_subject->text() << "--recipient" << recipient; 00538 proc.start( command, args ); 00539 //kDebug() << command << args; 00540 if (!proc.waitForStarted()) 00541 { 00542 kError() << "Unable to open a pipe to " << command << endl; 00543 return false; 00544 } 00545 proc.write( text().toUtf8() ); 00546 proc.closeWriteChannel(); 00547 00548 proc.waitForFinished(); 00549 kDebug() << "kbugreport: sendbugmail exit, status " << proc.exitStatus() << " code " << proc.exitCode(); 00550 00551 QByteArray line; 00552 if (proc.exitStatus() == QProcess::NormalExit && proc.exitCode() != 0) { 00553 // XXX not stderr? 00554 while (!proc.atEnd()) 00555 line = proc.readLine(); 00556 d->lastError = QString::fromUtf8( line ); 00557 return false; 00558 } 00559 return true; 00560 } 00561 00562 00563 #include "kbugreport.moc"
KDE 4.6 API Reference