/* This file is part of the KDE project Copyright (C) 1999 David Faure This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "kbugreport.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "kdepackages.h" #include "kdeversion.h" #include class KBugReportPrivate { public: KBugReportPrivate(KBugReport *q): q(q) {} void _k_slotConfigureEmail(); void _k_slotSetFrom(); void _k_appChanged(int); void _k_updateUrl(); KBugReport *q; QProcess * m_process; const KAboutData * m_aboutData; KTextEdit * m_lineedit; KLineEdit * m_subject; QLabel * m_from; QLabel * m_version; QString m_strVersion; QGroupBox * m_bgSeverity; QPushButton * m_configureEmail; KComboBox *appcombo; QString lastError; QString kde_version; QString appname; QString os; KUrl url; QList severityButtons; int currentSeverity() { for (int i=0;iisChecked()) return i; return -1; } bool submitBugWeb; }; KBugReport::KBugReport( QWidget * _parent, bool modal, const KAboutData *aboutData ) : KDialog( _parent ), d( new KBugReportPrivate(this) ) { setCaption( i18n("Submit Bug Report") ); setButtons( Ok | Cancel ); setModal(modal); // Use supplied aboutdata, otherwise the one from the active componentData // otherwise the KGlobal one. _activeInstance should neved be 0L in theory. d->m_aboutData = aboutData ? aboutData : (KGlobal::activeComponent().isValid() ? KGlobal::activeComponent().aboutData() : KGlobal::mainComponent().aboutData()); d->m_process = 0; QWidget * parent = new QWidget(this); d->submitBugWeb = false; if ( d->m_aboutData->bugAddress() == QLatin1String("submit@bugs.kde.org") ) { // This is a core KDE application -> redirect to the web form d->submitBugWeb = true; setButtonGuiItem( Cancel, KStandardGuiItem::close() ); } QLabel * tmpLabel; QVBoxLayout * lay = new QVBoxLayout( parent); KTitleWidget *title = new KTitleWidget( this ); title->setText(i18n( "Submit Bug Report" ) ); title->setPixmap( KIcon( "tools-report-bug" ).pixmap( 32 ) ); lay->addWidget( title ); QGridLayout *glay = new QGridLayout(); lay->addLayout(glay); int row = 0; if ( !d->submitBugWeb ) { // From QString qwtstr = i18n( "Your email address. If incorrect, use the Configure Email button to change it" ); tmpLabel = new QLabel( i18nc("Email sender address", "From:"), parent ); glay->addWidget( tmpLabel, row,0 ); tmpLabel->setWhatsThis(qwtstr ); d->m_from = new QLabel( parent ); glay->addWidget( d->m_from, row, 1 ); d->m_from->setWhatsThis(qwtstr ); // Configure email button d->m_configureEmail = new QPushButton( i18n("Configure Email..."), parent ); connect( d->m_configureEmail, SIGNAL(clicked()), this, SLOT(_k_slotConfigureEmail()) ); glay->addWidget( d->m_configureEmail, 0, 2, 3, 1, Qt::AlignTop|Qt::AlignRight ); // To qwtstr = i18n( "The email address this bug report is sent to." ); tmpLabel = new QLabel( i18nc("Email receiver address", "To:"), parent ); glay->addWidget( tmpLabel, ++row,0 ); tmpLabel->setWhatsThis(qwtstr ); tmpLabel = new QLabel( d->m_aboutData->bugAddress(), parent ); tmpLabel->setTextInteractionFlags(Qt::TextBrowserInteraction); glay->addWidget( tmpLabel, row, 1 ); tmpLabel->setWhatsThis(qwtstr ); setButtonGuiItem( Ok, KGuiItem( i18n("&Send"), "mail-send", i18n( "Send bug report." ), i18n( "Send this bug report to %1." , d->m_aboutData->bugAddress() ) ) ); row++; } else { d->m_configureEmail = 0; d->m_from = 0; } // Program name 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" ); tmpLabel = new QLabel( i18n("Application: "), parent ); glay->addWidget( tmpLabel, row, 0 ); tmpLabel->setWhatsThis(qwtstr ); d->appcombo = new KComboBox( false, parent ); d->appcombo->setWhatsThis(qwtstr ); QStringList packageList; for (int c = 0; packages[c]; ++c) packageList << QString::fromLatin1(packages[c]); d->appcombo->addItems(packageList); connect(d->appcombo, SIGNAL(activated(int)), SLOT(_k_appChanged(int))); d->appname = d->m_aboutData ? d->m_aboutData->productName() : qApp->applicationName() ; glay->addWidget( d->appcombo, row, 1 ); int index = 0; for (; index < d->appcombo->count(); index++) { if (d->appcombo->itemText(index) == d->appname) { break; } } if (index == d->appcombo->count()) { // not present d->appcombo->addItem(d->appname); } d->appcombo->setCurrentIndex(index); tmpLabel->setWhatsThis(qwtstr ); // Version qwtstr = i18n( "The version of this application - please make sure that no newer version is available before sending a bug report" ); tmpLabel = new QLabel( i18n("Version:"), parent ); glay->addWidget( tmpLabel, ++row, 0 ); tmpLabel->setWhatsThis(qwtstr ); if (d->m_aboutData) d->m_strVersion = d->m_aboutData->version(); else d->m_strVersion = i18n("no version set (programmer error)"); d->kde_version = QString::fromLatin1( KDE_VERSION_STRING ); d->kde_version += ", " + QString::fromLatin1( KDE_DISTRIBUTION_TEXT ); if ( !d->submitBugWeb ) d->m_strVersion += ' ' + d->kde_version; d->m_version = new QLabel( d->m_strVersion, parent ); d->m_version->setTextInteractionFlags(Qt::TextBrowserInteraction); //glay->addWidget( d->m_version, row, 1 ); glay->addWidget( d->m_version, row, 1, 1, 2 ); d->m_version->setWhatsThis(qwtstr ); tmpLabel = new QLabel(i18n("OS:"), parent); glay->addWidget( tmpLabel, ++row, 0 ); struct utsname unameBuf; uname( &unameBuf ); d->os = QString::fromLatin1( unameBuf.sysname ) + " (" + QString::fromLatin1( unameBuf.machine ) + ") " "release " + QString::fromLatin1( unameBuf.release ); tmpLabel = new QLabel(d->os, parent); tmpLabel->setTextInteractionFlags(Qt::TextBrowserInteraction); glay->addWidget( tmpLabel, row, 1, 1, 2 ); tmpLabel = new QLabel(i18n("Compiler:"), parent); glay->addWidget( tmpLabel, ++row, 0 ); tmpLabel = new QLabel(QString::fromLatin1(KDE_COMPILER_VERSION), parent); tmpLabel->setTextInteractionFlags(Qt::TextBrowserInteraction); glay->addWidget( tmpLabel, row, 1, 1, 2 ); if ( !d->submitBugWeb ) { // Severity d->m_bgSeverity = new QGroupBox( i18n("Se&verity"), parent ); static const char * const sevNames[5] = { "critical", "grave", "normal", "wishlist", "i18n" }; const QString sevTexts[5] = { i18n("Critical"), i18n("Grave"), i18nc("normal severity","Normal"), i18n("Wishlist"), i18n("Translation") }; QHBoxLayout *severityLayout=new QHBoxLayout(d->m_bgSeverity); for (int i = 0 ; i < 5 ; i++ ) { // Store the severity string as the name QRadioButton *rb = new QRadioButton( sevTexts[i], d->m_bgSeverity); rb->setObjectName(sevNames[i] ); d->severityButtons.append(rb); severityLayout->addWidget(rb); if (i==2) rb->setChecked(true); // default : "normal" } lay->addWidget( d->m_bgSeverity ); // Subject QHBoxLayout * hlay = new QHBoxLayout(); lay->addItem(hlay); tmpLabel = new QLabel( i18n("S&ubject: "), parent ); hlay->addWidget( tmpLabel ); d->m_subject = new KLineEdit( parent ); d->m_subject->setClearButtonShown(true); d->m_subject->setFocus(); tmpLabel->setBuddy( d->m_subject ); hlay->addWidget( d->m_subject ); QString text = i18n("Enter the text (in English if possible) that you wish to submit for the " "bug report.\n" "If you press \"Send\", a mail message will be sent to the maintainer of " "this program.\n"); QLabel * label = new QLabel( parent); label->setText( text ); lay->addWidget( label ); // The multiline-edit d->m_lineedit = new KTextEdit( parent); d->m_lineedit->setMinimumHeight( 180 ); // make it big d->m_lineedit->setWordWrapMode(QTextOption::WrapAtWordBoundaryOrAnywhere); d->m_lineedit->setLineWrapMode(QTextEdit::WidgetWidth); d->m_lineedit->setCheckSpellingEnabled(true); d->m_lineedit->setSpellCheckingLanguage("en"); lay->addWidget( d->m_lineedit, 10 /*stretch*/ ); d->_k_slotSetFrom(); } else { // Point to the web form lay->addSpacing(10); QString text = i18n("To submit a bug report, click on the button below. This will open a web browser " "window on http://bugs.kde.org where you will find " "a form to fill in. The information displayed above will be transferred to that server."); QLabel * label = new QLabel( text, parent); label->setOpenExternalLinks( true ); label->setTextInteractionFlags( Qt::LinksAccessibleByMouse | Qt::LinksAccessibleByKeyboard ); label->setWordWrap( true ); lay->addWidget( label ); lay->addSpacing(10); d->appcombo->setFocus(); d->_k_updateUrl(); setButtonText(Ok, i18n("&Launch Bug Report Wizard")); setButtonIcon(Ok, KIcon("tools-report-bug")); } parent->setMinimumHeight( parent->sizeHint().height() + 20 ); // WORKAROUND: prevent "cropped" kcombobox setMainWidget(parent); } KBugReport::~KBugReport() { delete d; } QString KBugReport::messageBody() const { if ( !d->submitBugWeb ) return d->m_lineedit->toPlainText(); else return QString(); } void KBugReport::setMessageBody(const QString &messageBody) { if ( !d->submitBugWeb ) d->m_lineedit->setPlainText(messageBody); } void KBugReportPrivate::_k_updateUrl() { url = KUrl( "https://bugs.kde.org/enter_bug.cgi" ); url.addQueryItem( "format", "guided" ); // use the guided form // the string format is product/component, where component is optional QStringList list = appcombo->currentText().split('/'); url.addQueryItem( "product", list[0] ); if (list.size() == 2) { url.addQueryItem( "component", list[1] ); } url.addQueryItem( "version", m_strVersion ); // TODO: guess and fill OS(sys_os) and Platform(rep_platform) fields } void KBugReportPrivate::_k_appChanged(int i) { QString appName = appcombo->itemText(i); int index = appName.indexOf( '/' ); if ( index > 0 ) appName = appName.left( index ); kDebug() << "appName " << appName; QString strDisplayVersion; //Version string to show in the UI if (appname == appName && m_aboutData) { m_strVersion = m_aboutData->version(); strDisplayVersion = m_strVersion; } else { m_strVersion = QLatin1String("unknown"); //English string to put in the bug report strDisplayVersion = i18nc("unknown program name", "unknown"); } if ( !submitBugWeb ) { m_strVersion += ' ' + kde_version; strDisplayVersion += ' ' + kde_version; } m_version->setText(strDisplayVersion); if ( submitBugWeb ) _k_updateUrl(); } void KBugReportPrivate::_k_slotConfigureEmail() { if (m_process) return; m_process = new QProcess; QObject::connect( m_process, SIGNAL(finished(int,QProcess::ExitStatus)), q, SLOT(_k_slotSetFrom()) ); m_process->start( QString::fromLatin1("kcmshell4"), QStringList() << QString::fromLatin1("kcm_useraccount") ); if ( !m_process->waitForStarted() ) { kDebug() << "Couldn't start kcmshell4.."; delete m_process; m_process = 0; return; } m_configureEmail->setEnabled(false); } void KBugReportPrivate::_k_slotSetFrom() { delete m_process; m_process = 0; m_configureEmail->setEnabled(true); // ### KDE4: why oh why is KEMailSettings in kio? KConfig emailConf( QString::fromLatin1("emaildefaults") ); KConfigGroup cg(&emailConf, "Defaults"); // find out the default profile QString profile = QString::fromLatin1("PROFILE_"); profile += cg.readEntry( QString::fromLatin1("Profile"), QString::fromLatin1("Default") ); KConfigGroup profileGrp(&emailConf, profile ); QString fromaddr = profileGrp.readEntry( "EmailAddress" ); if (fromaddr.isEmpty()) { struct passwd *p; p = getpwuid(getuid()); fromaddr = QString::fromLatin1(p->pw_name); } else { QString name = profileGrp.readEntry( "FullName" ); if (!name.isEmpty()) fromaddr = name + QString::fromLatin1(" <") + fromaddr + QString::fromLatin1(">"); } m_from->setText( fromaddr ); } void KBugReport::accept() { if ( d->submitBugWeb ) { KToolInvocation::invokeBrowser( d->url.url() ); return; } if( d->m_lineedit->toPlainText().isEmpty() || d->m_subject->text().isEmpty() ) { QString msg = i18n("You must specify both a subject and a description " "before the report can be sent."); KMessageBox::error(this,msg); return; } switch ( d->currentSeverity()) { case 0: // critical if ( KMessageBox::questionYesNo( this, i18n( "

You chose the severity Critical. " "Please note that this severity is intended only for bugs that:

" "
  • break unrelated software on the system (or the whole system)
  • " "
  • cause serious data loss
  • " "
  • introduce a security hole on the system where the affected package is installed
\n" "

Does the bug you are reporting cause any of the above damage? " "If it does not, please select a lower severity. Thank you.

" ),QString(),KStandardGuiItem::cont(),KStandardGuiItem::cancel() ) == KMessageBox::No ) return; break; case 1: // grave if ( KMessageBox::questionYesNo( this, i18n( "

You chose the severity Grave. " "Please note that this severity is intended only for bugs that:

" "
  • make the package in question unusable or mostly so
  • " "
  • cause data loss
  • " "
  • introduce a security hole allowing access to the accounts of users who use the affected package
\n" "

Does the bug you are reporting cause any of the above damage? " "If it does not, please select a lower severity. Thank you.

" ),QString(),KStandardGuiItem::cont(),KStandardGuiItem::cancel() ) == KMessageBox::No ) return; break; default: break; } if( !sendBugReport() ) { QString msg = i18n("Unable to send the bug report.\n" "Please submit a bug report manually....\n" "See http://bugs.kde.org/ for instructions."); KMessageBox::error(this, msg + "\n\n" + d->lastError); return; } KMessageBox::information(this, i18n("Bug report sent, thank you for your input.")); KDialog::accept(); } void KBugReport::closeEvent( QCloseEvent * e) { if( !d->submitBugWeb && ( (d->m_lineedit->toPlainText().length()>0) || d->m_subject->isModified() ) ) { int rc = KMessageBox::warningYesNo( this, i18n( "Close and discard\nedited message?" ), i18n( "Close Message" ), KStandardGuiItem::discard(), KStandardGuiItem::cont() ); if( rc == KMessageBox::No ) { e->ignore(); return; } } KDialog::closeEvent(e); } QString KBugReport::text() const { kDebug() << d->severityButtons[d->currentSeverity()]->objectName(); // Prepend the pseudo-headers to the contents of the mail QString severity = d->severityButtons[d->currentSeverity()]->objectName(); QString appname = d->appcombo->currentText(); QString os = QString::fromLatin1("OS: %1 (%2)\n"). arg(KDE_COMPILING_OS). arg(KDE_DISTRIBUTION_TEXT); QString bodyText; /* for(int i = 0; i < m_lineedit->numLines(); i++) { QString line = m_lineedit->textLine(i); if (!line.endsWith("\n")) line += '\n'; bodyText += line; } */ bodyText=d->m_lineedit->toPlainText(); if (bodyText.length()>0) if (bodyText[bodyText.length()-1]!='\n') bodyText+='\n'; if (severity == QLatin1String("i18n") && KGlobal::locale()->language() != KLocale::defaultLanguage()) { // Case 1 : i18n bug QString package = QString::fromLatin1("i18n_%1").arg(KGlobal::locale()->language()); package = package.replace('_', '-'); return QString::fromLatin1("Package: %1").arg(package) + QString::fromLatin1("\n" "Application: %1\n" // not really i18n's version, so better here IMHO "Version: %2\n").arg(appname).arg(d->m_strVersion)+ os+QString::fromLatin1("\n")+bodyText; } else { appname = appname.replace('_', '-'); // Case 2 : normal bug return QString::fromLatin1("Package: %1\n" "Version: %2\n" "Severity: %3\n") .arg(appname).arg(d->m_strVersion).arg(severity)+ QString::fromLatin1("Compiler: %1\n").arg(KDE_COMPILER_VERSION)+ os+QString::fromLatin1("\n")+bodyText; } } bool KBugReport::sendBugReport() { QString recipient ( d->m_aboutData ? d->m_aboutData->bugAddress() : QString::fromLatin1("submit@bugs.kde.org") ); QString command; command = KStandardDirs::locate("exe", "ksendbugmail"); if (command.isEmpty()) command = KStandardDirs::findExe( QString::fromLatin1("ksendbugmail") ); QProcess proc; QStringList args; args << "--subject" << d->m_subject->text() << "--recipient" << recipient; proc.start( command, args ); //kDebug() << command << args; if (!proc.waitForStarted()) { kError() << "Unable to open a pipe to " << command << endl; return false; } proc.write( text().toUtf8() ); proc.closeWriteChannel(); proc.waitForFinished(); kDebug() << "kbugreport: sendbugmail exit, status " << proc.exitStatus() << " code " << proc.exitCode(); QByteArray line; if (proc.exitStatus() == QProcess::NormalExit && proc.exitCode() != 0) { // XXX not stderr? while (!proc.atEnd()) line = proc.readLine(); d->lastError = QString::fromUtf8( line ); return false; } return true; } #include "kbugreport.moc"