kbugreport.cpp
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 <tqhbuttongroup.h> 00021 #include <tqpushbutton.h> 00022 #include <tqlabel.h> 00023 #include <tqlayout.h> 00024 #include <tqmultilineedit.h> 00025 #include <tqradiobutton.h> 00026 #include <tqwhatsthis.h> 00027 #include <tqregexp.h> 00028 00029 #include <kaboutdata.h> 00030 #include <kapplication.h> 00031 #include <kconfig.h> 00032 #include <kdebug.h> 00033 #include <klineedit.h> 00034 #include <klocale.h> 00035 #include <kmessagebox.h> 00036 #include <kprocess.h> 00037 #include <kstandarddirs.h> 00038 #include <kstdguiitem.h> 00039 #include <kurl.h> 00040 #include <kurllabel.h> 00041 00042 #include "kbugreport.h" 00043 00044 #include <stdio.h> 00045 #include <pwd.h> 00046 #include <unistd.h> 00047 00048 #include <sys/utsname.h> 00049 00050 #include "kdepackages.h" 00051 #include <kcombobox.h> 00052 #include <config.h> 00053 #include <ktempfile.h> 00054 #include <tqtextstream.h> 00055 #include <tqfile.h> 00056 00057 class KBugReportPrivate { 00058 public: 00059 KComboBox *appcombo; 00060 TQString lastError; 00061 TQString kde_version; 00062 TQString appname; 00063 TQString os; 00064 TQPushButton *submitBugButton; 00065 KURL url; 00066 }; 00067 00068 KBugReport::KBugReport( TQWidget * parentw, bool modal, const KAboutData *aboutData ) 00069 : KDialogBase( Plain, 00070 i18n("Submit Bug Report"), 00071 Ok | Cancel, 00072 Ok, 00073 parentw, 00074 "KBugReport", 00075 modal, // modal 00076 true // separator 00077 ) 00078 { 00079 d = new KBugReportPrivate; 00080 00081 // Use supplied aboutdata, otherwise the one from the active instance 00082 // otherwise the KGlobal one. _activeInstance should neved be 0L in theory. 00083 m_aboutData = aboutData 00084 ? aboutData 00085 : ( KGlobal::_activeInstance ? KGlobal::_activeInstance->aboutData() 00086 : KGlobal::instance()->aboutData() ); 00087 m_process = 0; 00088 TQWidget * parent = plainPage(); 00089 d->submitBugButton = 0; 00090 00091 //if ( m_aboutData->bugAddress() == TQString::fromLatin1("submit@bugs.trinitydesktop.org") ) 00092 //{ 00093 // // This is a core KDE application -> redirect to the web form 00094 // Always redirect to the Web form for Trinity 00095 d->submitBugButton = new TQPushButton( parent ); 00096 setButtonCancel( KStdGuiItem::close() ); 00097 //} 00098 00099 TQLabel * tmpLabel; 00100 TQVBoxLayout * lay = new TQVBoxLayout( parent, 0, spacingHint() ); 00101 00102 TQGridLayout *glay = new TQGridLayout( lay, 4, 3 ); 00103 glay->setColStretch( 1, 10 ); 00104 glay->setColStretch( 2, 10 ); 00105 00106 int row = 0; 00107 00108 if ( !d->submitBugButton ) 00109 { 00110 // From 00111 TQString qwtstr = i18n( "Your email address. If incorrect, use the Configure Email button to change it" ); 00112 tmpLabel = new TQLabel( i18n("From:"), parent ); 00113 glay->addWidget( tmpLabel, row,0 ); 00114 TQWhatsThis::add( tmpLabel, qwtstr ); 00115 m_from = new TQLabel( parent ); 00116 glay->addWidget( m_from, row, 1 ); 00117 TQWhatsThis::add( m_from, qwtstr ); 00118 00119 00120 // Configure email button 00121 m_configureEmail = new TQPushButton( i18n("Configure Email..."), 00122 parent ); 00123 connect( m_configureEmail, TQT_SIGNAL( clicked() ), this, 00124 TQT_SLOT( slotConfigureEmail() ) ); 00125 glay->addMultiCellWidget( m_configureEmail, 0, 2, 2, 2, (TQ_Alignment)(AlignTop|AlignRight) ); 00126 00127 // To 00128 qwtstr = i18n( "The email address this bug report is sent to." ); 00129 tmpLabel = new TQLabel( i18n("To:"), parent ); 00130 glay->addWidget( tmpLabel, ++row,0 ); 00131 TQWhatsThis::add( tmpLabel, qwtstr ); 00132 tmpLabel = new TQLabel( m_aboutData->bugAddress(), parent ); 00133 glay->addWidget( tmpLabel, row, 1 ); 00134 TQWhatsThis::add( tmpLabel, qwtstr ); 00135 00136 setButtonOK( KGuiItem( i18n("&Send"), "mail_send", i18n( "Send bug report." ), 00137 i18n( "Send this bug report to %1." ).arg( m_aboutData->bugAddress() ) ) ); 00138 00139 } 00140 else 00141 { 00142 m_configureEmail = 0; 00143 m_from = 0; 00144 showButtonOK( false ); 00145 } 00146 00147 // Program name 00148 TQString 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" ); 00149 tmpLabel = new TQLabel( i18n("Application: "), parent ); 00150 glay->addWidget( tmpLabel, ++row, 0 ); 00151 TQWhatsThis::add( tmpLabel, qwtstr ); 00152 d->appcombo = new KComboBox( false, parent, "app"); 00153 TQWhatsThis::add( d->appcombo, qwtstr ); 00154 d->appcombo->insertStrList((const char**)packages); 00155 connect(d->appcombo, TQT_SIGNAL(activated(int)), TQT_SLOT(appChanged(int))); 00156 d->appname = TQString::fromLatin1( m_aboutData 00157 ? m_aboutData->productName() 00158 : tqApp->name() ); 00159 glay->addWidget( d->appcombo, row, 1 ); 00160 int index = 0; 00161 for (; index < d->appcombo->count(); index++) { 00162 if (d->appcombo->text(index) == d->appname) { 00163 break; 00164 } 00165 } 00166 if (index == d->appcombo->count()) { // not present 00167 d->appcombo->insertItem(d->appname); 00168 } 00169 d->appcombo->setCurrentItem(index); 00170 00171 TQWhatsThis::add( tmpLabel, qwtstr ); 00172 00173 // Version 00174 qwtstr = i18n( "The version of this application - please make sure that no newer version is available before sending a bug report" ); 00175 tmpLabel = new TQLabel( i18n("Version:"), parent ); 00176 glay->addWidget( tmpLabel, ++row, 0 ); 00177 TQWhatsThis::add( tmpLabel, qwtstr ); 00178 if (m_aboutData) 00179 m_strVersion = m_aboutData->version(); 00180 else 00181 m_strVersion = i18n("no version set (programmer error!)"); 00182 d->kde_version = TQString::fromLatin1( KDE_VERSION_STRING ); 00183 d->kde_version += ", " + TQString::fromLatin1( KDE_DISTRIBUTION_TEXT ); 00184 if ( !d->submitBugButton ) 00185 m_strVersion += " " + d->kde_version; 00186 m_version = new TQLabel( m_strVersion, parent ); 00187 //glay->addWidget( m_version, row, 1 ); 00188 glay->addMultiCellWidget( m_version, row, row, 1, 2 ); 00189 TQWhatsThis::add( m_version, qwtstr ); 00190 00191 tmpLabel = new TQLabel(i18n("OS:"), parent); 00192 glay->addWidget( tmpLabel, ++row, 0 ); 00193 00194 struct utsname unameBuf; 00195 uname( &unameBuf ); 00196 d->os = TQString::fromLatin1( unameBuf.sysname ) + 00197 " (" + TQString::fromLatin1( unameBuf.machine ) + ") " 00198 "release " + TQString::fromLatin1( unameBuf.release ); 00199 00200 tmpLabel = new TQLabel(d->os, parent); 00201 glay->addMultiCellWidget( tmpLabel, row, row, 1, 2 ); 00202 00203 tmpLabel = new TQLabel(i18n("Compiler:"), parent); 00204 glay->addWidget( tmpLabel, ++row, 0 ); 00205 tmpLabel = new TQLabel(TQString::fromLatin1(KDE_COMPILER_VERSION), parent); 00206 glay->addMultiCellWidget( tmpLabel, row, row, 1, 2 ); 00207 00208 if ( !d->submitBugButton ) 00209 { 00210 // Severity 00211 m_bgSeverity = new TQHButtonGroup( i18n("Se&verity"), parent ); 00212 static const char * const sevNames[5] = { "critical", "grave", "normal", "wishlist", "i18n" }; 00213 const TQString sevTexts[5] = { i18n("Critical"), i18n("Grave"), i18n("normal severity","Normal"), i18n("Wishlist"), i18n("Translation") }; 00214 00215 for (int i = 0 ; i < 5 ; i++ ) 00216 { 00217 // Store the severity string as the name 00218 TQRadioButton *rb = new TQRadioButton( sevTexts[i], m_bgSeverity, sevNames[i] ); 00219 if (i==2) rb->setChecked(true); // default : "normal" 00220 } 00221 00222 lay->addWidget( m_bgSeverity ); 00223 00224 // Subject 00225 TQHBoxLayout * hlay = new TQHBoxLayout( lay ); 00226 tmpLabel = new TQLabel( i18n("S&ubject: "), parent ); 00227 hlay->addWidget( tmpLabel ); 00228 m_subject = new KLineEdit( parent ); 00229 m_subject->setFocus(); 00230 tmpLabel->setBuddy(m_subject); 00231 hlay->addWidget( m_subject ); 00232 00233 TQString text = i18n("Enter the text (in English if possible) that you wish to submit for the " 00234 "bug report.\n" 00235 "If you press \"Send\", a mail message will be sent to the maintainer of " 00236 "this program.\n"); 00237 TQLabel * label = new TQLabel( parent, "label" ); 00238 00239 label->setText( text ); 00240 lay->addWidget( label ); 00241 00242 // The multiline-edit 00243 m_lineedit = new TQMultiLineEdit( parent, TQMULTILINEEDIT_OBJECT_NAME_STRING ); 00244 m_lineedit->setMinimumHeight( 180 ); // make it big 00245 m_lineedit->setWordWrap(TQMultiLineEdit::WidgetWidth); 00246 lay->addWidget( m_lineedit, 10 /*stretch*/ ); 00247 00248 slotSetFrom(); 00249 } else { 00250 // Point to the web form 00251 00252 lay->addSpacing(10); 00253 TQString text = i18n("Reporting bugs and requesting enhancements are maintained using the Bugzilla reporting system.\n" 00254 "You'll need a login account and password to use the reporting system.\n" 00255 "To control spam and rogue elements the login requires a valid email address.\n" 00256 "Consider using any large email service if you want to avoid using your private email address.\n" 00257 "\n" 00258 "Selecting the button below opens your web browser to http://bugs.trinitydesktop.org,\n" 00259 "where you will find the report form.\n" 00260 "The information displayed above will be transferred to the reporting system.\n" 00261 "Session cookies must be enabled to use the reporting system.\n" 00262 "\n" 00263 "Thank you for helping!"); 00264 TQLabel * label = new TQLabel( text, parent, "label"); 00265 lay->addWidget( label ); 00266 lay->addSpacing(10); 00267 00268 updateURL(); 00269 d->submitBugButton->setText( i18n("&Launch Bug Report Wizard") ); 00270 d->submitBugButton->setSizePolicy(TQSizePolicy::Fixed,TQSizePolicy::Fixed); 00271 lay->addWidget( d->submitBugButton ); 00272 lay->addSpacing(10); 00273 00274 connect( d->submitBugButton, TQT_SIGNAL(clicked()), 00275 this, TQT_SLOT(slotOk())); 00276 } 00277 } 00278 00279 KBugReport::~KBugReport() 00280 { 00281 delete d; 00282 } 00283 00284 void KBugReport::updateURL() 00285 { 00286 KURL url ( "http://bugs.trinitydesktop.org/enter_bug.cgi" ); 00287 url.addQueryItem( "product", "TDE" ); 00288 url.addQueryItem( "op_sys", d->os ); 00289 url.addQueryItem( "cf_kde_compiler", KDE_COMPILER_VERSION ); 00290 url.addQueryItem( "cf_kde_version", d->kde_version ); 00291 url.addQueryItem( "cf_kde_appversion", m_strVersion ); 00292 url.addQueryItem( "cf_kde_package", d->appcombo->currentText() ); 00293 url.addQueryItem( "cf_kde_kbugreport", "1" ); 00294 d->url = url; 00295 } 00296 00297 void KBugReport::appChanged(int i) 00298 { 00299 TQString appName = d->appcombo->text(i); 00300 int index = appName.find( '/' ); 00301 if ( index > 0 ) 00302 appName = appName.left( index ); 00303 kdDebug() << "appName " << appName << endl; 00304 00305 if (d->appname == appName && m_aboutData) 00306 m_strVersion = m_aboutData->version(); 00307 else 00308 m_strVersion = i18n("unknown program name", "unknown"); 00309 00310 if ( !d->submitBugButton ) 00311 m_strVersion += d->kde_version; 00312 00313 m_version->setText(m_strVersion); 00314 if ( d->submitBugButton ) 00315 updateURL(); 00316 } 00317 00318 void KBugReport::slotConfigureEmail() 00319 { 00320 if (m_process) return; 00321 m_process = new KProcess; 00322 *m_process << TQString::fromLatin1("kcmshell") << TQString::fromLatin1("kcm_useraccount"); 00323 connect(m_process, TQT_SIGNAL(processExited(KProcess *)), TQT_SLOT(slotSetFrom())); 00324 if (!m_process->start()) 00325 { 00326 kdDebug() << "Couldn't start kcmshell.." << endl; 00327 delete m_process; 00328 m_process = 0; 00329 return; 00330 } 00331 m_configureEmail->setEnabled(false); 00332 } 00333 00334 void KBugReport::slotSetFrom() 00335 { 00336 delete m_process; 00337 m_process = 0; 00338 m_configureEmail->setEnabled(true); 00339 00340 // ### KDE4: why oh why is KEmailSettings in kio? 00341 KConfig emailConf( TQString::fromLatin1("emaildefaults") ); 00342 00343 // find out the default profile 00344 emailConf.setGroup( TQString::fromLatin1("Defaults") ); 00345 TQString profile = TQString::fromLatin1("PROFILE_"); 00346 profile += emailConf.readEntry( TQString::fromLatin1("Profile"), 00347 TQString::fromLatin1("Default") ); 00348 00349 emailConf.setGroup( profile ); 00350 TQString fromaddr = emailConf.readEntry( TQString::fromLatin1("EmailAddress") ); 00351 if (fromaddr.isEmpty()) { 00352 struct passwd *p; 00353 p = getpwuid(getuid()); 00354 fromaddr = TQString::fromLatin1(p->pw_name); 00355 } else { 00356 TQString name = emailConf.readEntry( TQString::fromLatin1("FullName")); 00357 if (!name.isEmpty()) 00358 fromaddr = name + TQString::fromLatin1(" <") + fromaddr + TQString::fromLatin1(">"); 00359 } 00360 m_from->setText( fromaddr ); 00361 } 00362 00363 void KBugReport::slotUrlClicked(const TQString &urlText) 00364 { 00365 if ( kapp ) 00366 kapp->invokeBrowser( urlText ); 00367 00368 // When using the web form, a click can also close the window, as there's 00369 // not much to do. It also gives the user a direct response to his click: 00370 if ( d->submitBugButton ) 00371 KDialogBase::slotCancel(); 00372 } 00373 00374 00375 void KBugReport::slotOk( void ) 00376 { 00377 if ( d->submitBugButton ) { 00378 if ( kapp ) 00379 kapp->invokeBrowser( d->url.url() ); 00380 return; 00381 } 00382 00383 if( m_lineedit->text().isEmpty() || 00384 m_subject->text().isEmpty() ) 00385 { 00386 TQString msg = i18n("You must specify both a subject and a description " 00387 "before the report can be sent."); 00388 KMessageBox::error(this,msg); 00389 return; 00390 } 00391 00392 switch ( m_bgSeverity->id( m_bgSeverity->selected() ) ) 00393 { 00394 case 0: // critical 00395 if ( KMessageBox::questionYesNo( this, i18n( 00396 "<p>You chose the severity <b>Critical</b>. " 00397 "Please note that this severity is intended only for bugs that</p>" 00398 "<ul><li>break unrelated software on the system (or the whole system)</li>" 00399 "<li>cause serious data loss</li>" 00400 "<li>introduce a security hole on the system where the affected package is installed</li></ul>\n" 00401 "<p>Does the bug you are reporting cause any of the above damage? " 00402 "If it does not, please select a lower severity. Thank you!</p>" ),TQString::null,KStdGuiItem::cont(),KStdGuiItem::cancel() ) == KMessageBox::No ) 00403 return; 00404 break; 00405 case 1: // grave 00406 if ( KMessageBox::questionYesNo( this, i18n( 00407 "<p>You chose the severity <b>Grave</b>. " 00408 "Please note that this severity is intended only for bugs that</p>" 00409 "<ul><li>make the package in question unusable or mostly so</li>" 00410 "<li>cause data loss</li>" 00411 "<li>introduce a security hole allowing access to the accounts of users who use the affected package</li></ul>\n" 00412 "<p>Does the bug you are reporting cause any of the above damage? " 00413 "If it does not, please select a lower severity. Thank you!</p>" ),TQString::null,KStdGuiItem::cont(),KStdGuiItem::cancel() ) == KMessageBox::No ) 00414 return; 00415 break; 00416 } 00417 if( !sendBugReport() ) 00418 { 00419 TQString msg = i18n("Unable to send the bug report.\n" 00420 "Please submit a bug report manually...\n" 00421 "See http://bugs.trinitydesktop.org/ for instructions."); 00422 KMessageBox::error(this, msg + "\n\n" + d->lastError); 00423 return; 00424 } 00425 00426 KMessageBox::information(this, 00427 i18n("Bug report sent, thank you for your input.")); 00428 accept(); 00429 } 00430 00431 void KBugReport::slotCancel() 00432 { 00433 if( !d->submitBugButton && ( m_lineedit->edited() || m_subject->edited() ) ) 00434 { 00435 int rc = KMessageBox::warningYesNo( this, 00436 i18n( "Close and discard\nedited message?" ), 00437 i18n( "Close Message" ), KStdGuiItem::discard(), KStdGuiItem::cont() ); 00438 if( rc == KMessageBox::No ) 00439 return; 00440 } 00441 KDialogBase::slotCancel(); 00442 } 00443 00444 00445 TQString KBugReport::text() const 00446 { 00447 kdDebug() << m_bgSeverity->selected()->name() << endl; 00448 // Prepend the pseudo-headers to the contents of the mail 00449 TQString severity = TQString::fromLatin1(m_bgSeverity->selected()->name()); 00450 TQString appname = d->appcombo->currentText(); 00451 TQString os = TQString::fromLatin1("OS: %1 (%2)\n"). 00452 arg(KDE_COMPILING_OS). 00453 arg(KDE_DISTRIBUTION_TEXT); 00454 TQString bodyText; 00455 for(int i = 0; i < m_lineedit->numLines(); i++) 00456 { 00457 TQString line = m_lineedit->textLine(i); 00458 if (!line.endsWith("\n")) 00459 line += '\n'; 00460 bodyText += line; 00461 } 00462 00463 if (severity == TQString::fromLatin1("i18n") && KGlobal::locale()->language() != KLocale::defaultLanguage()) { 00464 // Case 1 : i18n bug 00465 TQString package = TQString::fromLatin1("i18n_%1").arg(KGlobal::locale()->language()); 00466 package = package.replace(TQString::fromLatin1("_"), TQString::fromLatin1("-")); 00467 return TQString::fromLatin1("Package: %1").arg(package) + 00468 TQString::fromLatin1("\n" 00469 "Application: %1\n" 00470 // not really i18n's version, so better here IMHO 00471 "Version: %2\n").arg(appname).arg(m_strVersion)+ 00472 os+TQString::fromLatin1("\n")+bodyText; 00473 } else { 00474 appname = appname.replace(TQString::fromLatin1("_"), TQString::fromLatin1("-")); 00475 // Case 2 : normal bug 00476 return TQString::fromLatin1("Package: %1\n" 00477 "Version: %2\n" 00478 "Severity: %3\n") 00479 .arg(appname).arg(m_strVersion).arg(severity)+ 00480 TQString::fromLatin1("Compiler: %1\n").arg(KDE_COMPILER_VERSION)+ 00481 os+TQString::fromLatin1("\n")+bodyText; 00482 } 00483 } 00484 00485 bool KBugReport::sendBugReport() 00486 { 00487 TQString recipient ( m_aboutData ? 00488 m_aboutData->bugAddress() : 00489 TQString::fromLatin1("submit@bugs.trinitydesktop.org") ); 00490 00491 TQString command; 00492 command = locate("exe", "ksendbugmail"); 00493 if (command.isEmpty()) 00494 command = KStandardDirs::findExe( TQString::fromLatin1("ksendbugmail") ); 00495 00496 KTempFile outputfile; 00497 outputfile.close(); 00498 00499 TQString subject = m_subject->text(); 00500 command += " --subject "; 00501 command += KProcess::quote(subject); 00502 command += " --recipient "; 00503 command += KProcess::quote(recipient); 00504 command += " > "; 00505 command += KProcess::quote(outputfile.name()); 00506 00507 fflush(stdin); 00508 fflush(stderr); 00509 00510 FILE * fd = popen(TQFile::encodeName(command), "w"); 00511 if (!fd) 00512 { 00513 kdError() << "Unable to open a pipe to " << command << endl; 00514 return false; 00515 } 00516 00517 TQString btext = text(); 00518 fwrite(btext.ascii(),btext.length(),1,fd); 00519 fflush(fd); 00520 00521 int error = pclose(fd); 00522 kdDebug() << "exit status1 " << error << " " << (WIFEXITED(error)) << " " << WEXITSTATUS(error) << endl; 00523 00524 if ((WIFEXITED(error)) && WEXITSTATUS(error) == 1) { 00525 TQFile of(outputfile.name()); 00526 if (of.open(IO_ReadOnly )) { 00527 TQTextStream is(&of); 00528 is.setEncoding(TQTextStream::UnicodeUTF8); 00529 TQString line; 00530 while (!is.eof()) 00531 line = is.readLine(); 00532 d->lastError = line; 00533 } else { 00534 d->lastError = TQString::null; 00535 } 00536 outputfile.unlink(); 00537 return false; 00538 } 00539 outputfile.unlink(); 00540 return true; 00541 } 00542 00543 void KBugReport::virtual_hook( int id, void* data ) 00544 { KDialogBase::virtual_hook( id, data ); } 00545 00546 #include "kbugreport.moc"