00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020 #include "selftestdialog_p.h"
00021 #include "agentmanager.h"
00022 #include "session_p.h"
00023 #include "servermanager_p.h"
00024
00025 #include <akonadi/private/xdgbasedirs_p.h>
00026
00027 #include <KDebug>
00028 #include <KIcon>
00029 #include <KFileDialog>
00030 #include <KLocale>
00031 #include <KMessageBox>
00032 #include <KRun>
00033 #include <KStandardDirs>
00034
00035 #include <QtCore/QFileInfo>
00036 #include <QtCore/QProcess>
00037 #include <QtCore/QSettings>
00038 #include <QtCore/QTextStream>
00039 #include <QtDBus/QtDBus>
00040 #include <QtGui/QApplication>
00041 #include <QtGui/QClipboard>
00042 #include <QtGui/QStandardItemModel>
00043 #include <QtSql/QSqlDatabase>
00044 #include <QtSql/QSqlError>
00045
00046
00047
00048 #define AKONADI_CONTROL_SERVICE QLatin1String("org.freedesktop.Akonadi.Control")
00049 #define AKONADI_SERVER_SERVICE QLatin1String("org.freedesktop.Akonadi")
00050 #define AKONADI_SEARCH_SERVICE QLatin1String("org.kde.nepomuk.services.nepomukqueryservice")
00051
00052 using namespace Akonadi;
00053
00054 static QString makeLink( const QString &file )
00055 {
00056 return QString::fromLatin1( "<a href=\"%1\">%2</a>" ).arg( file, file );
00057 }
00058
00059 enum SelfTestRole {
00060 ResultTypeRole = Qt::UserRole,
00061 FileIncludeRole,
00062 ListDirectoryRole,
00063 EnvVarRole,
00064 SummaryRole,
00065 DetailsRole
00066 };
00067
00068 SelfTestDialog::SelfTestDialog(QWidget * parent) :
00069 KDialog( parent )
00070 {
00071 setCaption( i18n( "Akonadi Server Self-Test" ) );
00072 setButtons( Close | User1 | User2 );
00073 setButtonText( User1, i18n( "Save Report..." ) );
00074 setButtonIcon( User1, KIcon( QString::fromLatin1("document-save") ) );
00075 setButtonText( User2, i18n( "Copy Report to Clipboard" ) );
00076 setButtonIcon( User2, KIcon( QString::fromLatin1("edit-copy") ) );
00077 showButtonSeparator( true );
00078 ui.setupUi( mainWidget() );
00079
00080 mTestModel = new QStandardItemModel( this );
00081 ui.testView->setModel( mTestModel );
00082 connect( ui.testView->selectionModel(), SIGNAL(currentChanged(QModelIndex,QModelIndex)),
00083 SLOT(selectionChanged(QModelIndex)) );
00084 connect( ui.detailsLabel, SIGNAL(linkActivated(QString)), SLOT(linkActivated(QString)) );
00085
00086 connect( this, SIGNAL(user1Clicked()), SLOT(saveReport()) );
00087 connect( this, SIGNAL(user2Clicked()), SLOT(copyReport()) );
00088
00089 runTests();
00090 }
00091
00092 void SelfTestDialog::hideIntroduction()
00093 {
00094 ui.introductionLabel->hide();
00095 }
00096
00097 QStandardItem* SelfTestDialog::report( ResultType type, const KLocalizedString & summary, const KLocalizedString & details)
00098 {
00099 QStandardItem *item = new QStandardItem( summary.toString() );
00100 switch ( type ) {
00101 case Skip:
00102 item->setIcon( KIcon( QString::fromLatin1("dialog-ok") ) );
00103 break;
00104 case Success:
00105 item->setIcon( KIcon( QString::fromLatin1("dialog-ok-apply") ) );
00106 break;
00107 case Warning:
00108 item->setIcon( KIcon( QString::fromLatin1("dialog-warning") ) );
00109 break;
00110 case Error:
00111 default:
00112 item->setIcon( KIcon( QString::fromLatin1("dialog-error") ) );
00113 }
00114 item->setEditable( false );
00115 item->setWhatsThis( details.toString() );
00116 item->setData( type, ResultTypeRole );
00117 item->setData( summary.toString( 0 ), SummaryRole );
00118 item->setData( details.toString( 0 ), DetailsRole );
00119 mTestModel->appendRow( item );
00120 return item;
00121 }
00122
00123 void SelfTestDialog::selectionChanged(const QModelIndex &index )
00124 {
00125 if ( index.isValid() ) {
00126 ui.detailsLabel->setText( index.data( Qt::WhatsThisRole ).toString() );
00127 ui.detailsGroup->setEnabled( true );
00128 } else {
00129 ui.detailsLabel->setText( QString() );
00130 ui.detailsGroup->setEnabled( false );
00131 }
00132 }
00133
00134 void SelfTestDialog::runTests()
00135 {
00136 const QString driver = serverSetting( QLatin1String("General"), "Driver", QLatin1String("QMYSQL") ).toString();
00137 testSQLDriver();
00138 if (driver == QLatin1String( "QPSQL" )) {
00139 testPSQLServer();
00140 }
00141 else {
00142 testMySQLServer();
00143 testMySQLServerLog();
00144 testMySQLServerConfig();
00145 }
00146 testAkonadiCtl();
00147 testServerStatus();
00148 testSearchStatus();
00149 testProtocolVersion();
00150 testResources();
00151 testServerLog();
00152 testControlLog();
00153 }
00154
00155 QVariant SelfTestDialog::serverSetting(const QString & group, const char *key, const QVariant &def ) const
00156 {
00157 const QString serverConfigFile = XdgBaseDirs::akonadiServerConfigFile( XdgBaseDirs::ReadWrite );
00158 QSettings settings( serverConfigFile, QSettings::IniFormat );
00159 settings.beginGroup( group );
00160 return settings.value( QString::fromLatin1(key), def );
00161 }
00162
00163 bool SelfTestDialog::useStandaloneMysqlServer() const
00164 {
00165 const QString driver = serverSetting( QLatin1String("General"), "Driver", QLatin1String("QMYSQL") ).toString();
00166 if ( driver != QLatin1String( "QMYSQL" ) )
00167 return false;
00168 const bool startServer = serverSetting( driver, "StartServer", true ).toBool();
00169 if ( !startServer )
00170 return false;
00171 return true;
00172 }
00173
00174 bool SelfTestDialog::runProcess(const QString & app, const QStringList & args, QString & result) const
00175 {
00176 QProcess proc;
00177 proc.start( app, args );
00178 const bool rv = proc.waitForFinished();
00179 result.clear();
00180 result += QString::fromLocal8Bit( proc.readAllStandardError() );
00181 result += QString::fromLocal8Bit( proc.readAllStandardOutput() );
00182 return rv;
00183 }
00184
00185 void SelfTestDialog::testSQLDriver()
00186 {
00187 const QString driver = serverSetting( QLatin1String("General"), "Driver", QLatin1String("QMYSQL") ).toString();
00188 const QStringList availableDrivers = QSqlDatabase::drivers();
00189 const KLocalizedString detailsOk = ki18n( "The QtSQL driver '%1' is required by your current Akonadi server configuration and was found on your system." )
00190 .subs( driver );
00191 const KLocalizedString detailsFail = ki18n( "The QtSQL driver '%1' is required by your current Akonadi server configuration.\n"
00192 "The following drivers are installed: %2.\n"
00193 "Make sure the required driver is installed." )
00194 .subs( driver )
00195 .subs( availableDrivers.join( QLatin1String(", ") ) );
00196 QStandardItem *item = 0;
00197 if ( availableDrivers.contains( driver ) )
00198 item = report( Success, ki18n( "Database driver found." ), detailsOk );
00199 else
00200 item = report( Error, ki18n( "Database driver not found." ), detailsFail );
00201 item->setData( XdgBaseDirs::akonadiServerConfigFile( XdgBaseDirs::ReadWrite ), FileIncludeRole );
00202 }
00203
00204 void SelfTestDialog::testMySQLServer()
00205 {
00206 if ( !useStandaloneMysqlServer() ) {
00207 report( Skip, ki18n( "MySQL server executable not tested." ),
00208 ki18n( "The current configuration does not require an internal MySQL server." ) );
00209 return;
00210 }
00211
00212 const QString driver = serverSetting( QLatin1String("General"), "Driver", QLatin1String("QMYSQL") ).toString();
00213 const QString serverPath = serverSetting( driver, "ServerPath", QLatin1String("") ).toString();
00214
00215 const KLocalizedString details = ki18n( "You currently have configured Akonadi to use the MySQL server '%1'.\n"
00216 "Make sure you have the MySQL server installed, set the correct path and ensure you have the "
00217 "necessary read and execution rights on the server executable. The server executable is typically "
00218 "called 'mysqld', its locations varies depending on the distribution." ).subs( serverPath );
00219
00220 QFileInfo info( serverPath );
00221 if ( !info.exists() )
00222 report( Error, ki18n( "MySQL server not found." ), details );
00223 else if ( !info.isReadable() )
00224 report( Error, ki18n( "MySQL server not readable." ), details );
00225 else if ( !info.isExecutable() )
00226 report( Error, ki18n( "MySQL server not executable." ), details );
00227 else if ( !serverPath.contains( QLatin1String("mysqld") ) )
00228 report( Warning, ki18n( "MySQL found with unexpected name." ), details );
00229 else
00230 report( Success, ki18n( "MySQL server found." ), details );
00231
00232
00233 QString result;
00234 if ( runProcess( serverPath, QStringList() << QLatin1String( "--version" ), result ) ) {
00235 const KLocalizedString details = ki18n( "MySQL server found: %1" ).subs( result );
00236 report( Success, ki18n( "MySQL server is executable." ), details );
00237 } else {
00238 const KLocalizedString details = ki18n( "Executing the MySQL server '%1' failed with the following error message: '%2'" )
00239 .subs( serverPath ).subs( result );
00240 report( Error, ki18n( "Executing the MySQL server failed." ), details );
00241 }
00242 }
00243
00244 void SelfTestDialog::testMySQLServerLog()
00245 {
00246 if ( !useStandaloneMysqlServer() ) {
00247 report( Skip, ki18n( "MySQL server error log not tested." ),
00248 ki18n( "The current configuration does not require an internal MySQL server." ) );
00249 return;
00250 }
00251
00252 const QString logFileName = XdgBaseDirs::saveDir( "data", QLatin1String( "akonadi/db_data" ) )
00253 + QDir::separator() + QString::fromLatin1( "mysql.err" );
00254 const QFileInfo logFileInfo( logFileName );
00255 if ( !logFileInfo.exists() || logFileInfo.size() == 0 ) {
00256 report( Success, ki18n( "No current MySQL error log found." ),
00257 ki18n( "The MySQL server did not report any errors during this startup into '%1'." ).subs( logFileName ) );
00258 return;
00259 }
00260 QFile logFile( logFileName );
00261 if ( !logFile.open( QFile::ReadOnly | QFile::Text ) ) {
00262 report( Error, ki18n( "MySQL error log not readable." ),
00263 ki18n( "A MySQL server error log file was found but is not readable: %1" ).subs( makeLink( logFileName ) ) );
00264 return;
00265 }
00266 bool warningsFound = false;
00267 QStandardItem *item = 0;
00268 while ( !logFile.atEnd() ) {
00269 const QString line = QString::fromUtf8( logFile.readLine() );
00270 if ( line.contains( QLatin1String( "error" ), Qt::CaseInsensitive ) ) {
00271 item = report( Error, ki18n( "MySQL server log contains errors." ),
00272 ki18n( "The MySQL server error log file '%1' contains errors." ).subs( makeLink( logFileName ) ) );
00273 item->setData( logFileName, FileIncludeRole );
00274 return;
00275 }
00276 if ( !warningsFound && line.contains( QLatin1String( "warn" ), Qt::CaseInsensitive ) ) {
00277 warningsFound = true;
00278 }
00279 }
00280 if ( warningsFound ) {
00281 item = report( Warning, ki18n( "MySQL server log contains warnings." ),
00282 ki18n( "The MySQL server log file '%1' contains warnings." ).subs( makeLink( logFileName ) ) );
00283 } else {
00284 item = report( Success, ki18n( "MySQL server log contains no errors." ),
00285 ki18n( "The MySQL server log file '%1' does not contain any errors or warnings." )
00286 .subs( makeLink( logFileName ) ) );
00287 }
00288 item->setData( logFileName, FileIncludeRole );
00289
00290 logFile.close();
00291 }
00292
00293 void SelfTestDialog::testMySQLServerConfig()
00294 {
00295 if ( !useStandaloneMysqlServer() ) {
00296 report( Skip, ki18n( "MySQL server configuration not tested." ),
00297 ki18n( "The current configuration does not require an internal MySQL server." ) );
00298 return;
00299 }
00300
00301 QStandardItem *item = 0;
00302 const QString globalConfig = XdgBaseDirs::findResourceFile( "config", QLatin1String( "akonadi/mysql-global.conf" ) );
00303 const QFileInfo globalConfigInfo( globalConfig );
00304 if ( !globalConfig.isEmpty() && globalConfigInfo.exists() && globalConfigInfo.isReadable() ) {
00305 item = report( Success, ki18n( "MySQL server default configuration found." ),
00306 ki18n( "The default configuration for the MySQL server was found and is readable at %1." )
00307 .subs( makeLink( globalConfig ) ) );
00308 item->setData( globalConfig, FileIncludeRole );
00309 } else {
00310 report( Error, ki18n( "MySQL server default configuration not found." ),
00311 ki18n( "The default configuration for the MySQL server was not found or was not readable. "
00312 "Check your Akonadi installation is complete and you have all required access rights." ) );
00313 }
00314
00315 const QString localConfig = XdgBaseDirs::findResourceFile( "config", QLatin1String( "akonadi/mysql-local.conf" ) );
00316 const QFileInfo localConfigInfo( localConfig );
00317 if ( localConfig.isEmpty() || !localConfigInfo.exists() ) {
00318 report( Skip, ki18n( "MySQL server custom configuration not available." ),
00319 ki18n( "The custom configuration for the MySQL server was not found but is optional." ) );
00320 } else if ( localConfigInfo.exists() && localConfigInfo.isReadable() ) {
00321 item = report( Success, ki18n( "MySQL server custom configuration found." ),
00322 ki18n( "The custom configuration for the MySQL server was found and is readable at %1" )
00323 .subs( makeLink( localConfig ) ) );
00324 item->setData( localConfig, FileIncludeRole );
00325 } else {
00326 report( Error, ki18n( "MySQL server custom configuration not readable." ),
00327 ki18n( "The custom configuration for the MySQL server was found at %1 but is not readable. "
00328 "Check your access rights." ).subs( makeLink( localConfig ) ) );
00329 }
00330
00331 const QString actualConfig = XdgBaseDirs::saveDir( "data", QLatin1String( "akonadi" ) ) + QLatin1String("/mysql.conf");
00332 const QFileInfo actualConfigInfo( actualConfig );
00333 if ( actualConfig.isEmpty() || !actualConfigInfo.exists() || !actualConfigInfo.isReadable() ) {
00334 report( Error, ki18n( "MySQL server configuration not found or not readable." ),
00335 ki18n( "The MySQL server configuration was not found or is not readable." ) );
00336 } else {
00337 item = report( Success, ki18n( "MySQL server configuration is usable." ),
00338 ki18n( "The MySQL server configuration was found at %1 and is readable.").subs( makeLink( actualConfig ) ) );
00339 item->setData( actualConfig, FileIncludeRole );
00340 }
00341 }
00342
00343 void SelfTestDialog::testPSQLServer()
00344 {
00345 const QString dbname = serverSetting( QLatin1String( "QPSQL" ), "Name", QLatin1String( "akonadi" )).toString();
00346 const QString hostname = serverSetting( QLatin1String( "QPSQL" ), "Host", QLatin1String( "localhost" )).toString();
00347 const QString username = serverSetting( QLatin1String( "QPSQL" ), "User", QLatin1String( "akonadi" )).toString();
00348 const QString password = serverSetting( QLatin1String( "QPSQL" ), "Password", QLatin1String( "akonadi" )).toString();
00349 const int port = serverSetting( QLatin1String( "QPSQL" ), "Port", 5432).toInt();
00350
00351 QSqlDatabase db = QSqlDatabase::addDatabase( QLatin1String( "QPSQL" ) );
00352 db.setHostName( hostname );
00353 db.setDatabaseName( dbname );
00354 db.setUserName( username );
00355 db.setPassword( password );
00356 db.setPort( port );
00357
00358 if ( !db.open() ) {
00359 const KLocalizedString details = ki18n( db.lastError().text().toLatin1() );
00360 report( Error, ki18n( "Cannot connect to PostgreSQL server." ), details);
00361 }
00362 else {
00363 report( Success, ki18n( "PostgreSQL server found." ),
00364 ki18n( "The PostgreSQL server was found and connection is working."));
00365 }
00366 db.close();
00367 }
00368
00369 void SelfTestDialog::testAkonadiCtl()
00370 {
00371 const QString path = KStandardDirs::findExe( QLatin1String("akonadictl") );
00372 if ( path.isEmpty() ) {
00373 report( Error, ki18n( "akonadictl not found" ),
00374 ki18n( "The program 'akonadictl' needs to be accessible in $PATH. "
00375 "Make sure you have the Akonadi server installed." ) );
00376 return;
00377 }
00378 QString result;
00379 if ( runProcess( path, QStringList() << QLatin1String( "--version" ), result ) ) {
00380 report( Success, ki18n( "akonadictl found and usable" ),
00381 ki18n( "The program '%1' to control the Akonadi server was found "
00382 "and could be executed successfully.\nResult:\n%2" ).subs( path ).subs( result ) );
00383 } else {
00384 report( Error, ki18n( "akonadictl found but not usable" ),
00385 ki18n( "The program '%1' to control the Akonadi server was found "
00386 "but could not be executed successfully.\nResult:\n%2\n"
00387 "Make sure the Akonadi server is installed correctly." ).subs( path ).subs( result ) );
00388 }
00389 }
00390
00391 void SelfTestDialog::testServerStatus()
00392 {
00393 if ( QDBusConnection::sessionBus().interface()->isServiceRegistered( AKONADI_CONTROL_SERVICE ) ) {
00394 report( Success, ki18n( "Akonadi control process registered at D-Bus." ),
00395 ki18n( "The Akonadi control process is registered at D-Bus which typically indicates it is operational." ) );
00396 } else {
00397 report( Error, ki18n( "Akonadi control process not registered at D-Bus." ),
00398 ki18n( "The Akonadi control process is not registered at D-Bus which typically means it was not started "
00399 "or encountered a fatal error during startup." ) );
00400 }
00401
00402 if ( QDBusConnection::sessionBus().interface()->isServiceRegistered( AKONADI_SERVER_SERVICE ) ) {
00403 report( Success, ki18n( "Akonadi server process registered at D-Bus." ),
00404 ki18n( "The Akonadi server process is registered at D-Bus which typically indicates it is operational." ) );
00405 } else {
00406 report( Error, ki18n( "Akonadi server process not registered at D-Bus." ),
00407 ki18n( "The Akonadi server process is not registered at D-Bus which typically means it was not started "
00408 "or encountered a fatal error during startup." ) );
00409 }
00410 }
00411
00412 void SelfTestDialog::testSearchStatus()
00413 {
00414 bool searchAvailable = false;
00415 if ( QDBusConnection::sessionBus().interface()->isServiceRegistered( AKONADI_SEARCH_SERVICE ) ) {
00416 searchAvailable = true;
00417 report( Success, ki18n( "Nepomuk search service registered at D-Bus." ),
00418 ki18n( "The Nepomuk search service is registered at D-Bus which typically indicates it is operational." ) );
00419 } else {
00420 report( Error, ki18n( "Nepomuk search service not registered at D-Bus." ),
00421 ki18n( "The Nepomuk search service is not registered at D-Bus which typically means it was not started "
00422 "or encountered a fatal error during startup." ) );
00423 }
00424
00425 if ( searchAvailable ) {
00426
00427 QDBusInterface interface( QLatin1String( "org.kde.NepomukStorage" ), QLatin1String( "/nepomukstorage" ) );
00428 const QDBusReply<QString> reply = interface.call( QLatin1String( "usedSopranoBackend" ) );
00429 if ( reply.isValid() ) {
00430 const QString name = reply.value();
00431
00432
00433 if ( name.contains( QLatin1String( "redland" ) ) ) {
00434 report( Error, ki18n( "Nepomuk search service uses inappropriate backend." ),
00435 ki18n( "The Nepomuk search service uses the '%1' backend, which is not "
00436 "recommended for use with Akonadi." ).subs( name ) );
00437 } else {
00438 report( Success, ki18n( "Nepomuk search service uses an appropriate backend. " ),
00439 ki18n( "The Nepomuk search service uses one of the recommended backends." ) );
00440 }
00441 }
00442 }
00443 }
00444
00445 void SelfTestDialog::testProtocolVersion()
00446 {
00447 if ( Internal::serverProtocolVersion() < 0 ) {
00448 report( Skip, ki18n( "Protocol version check not possible." ),
00449 ki18n( "Without a connection to the server it is not possible to check if the protocol version meets the requirements." ) );
00450 return;
00451 }
00452 if ( Internal::serverProtocolVersion() < SessionPrivate::minimumProtocolVersion() ) {
00453 report( Error, ki18n( "Server protocol version is too old." ),
00454 ki18n( "The server protocol version is %1, but at least version %2 is required. "
00455 "Install a newer version of the Akonadi server." )
00456 .subs( Internal::serverProtocolVersion() )
00457 .subs( SessionPrivate::minimumProtocolVersion() ) );
00458 } else {
00459 report( Success, ki18n( "Server protocol version is recent enough." ),
00460 ki18n( "The server Protocol version is %1, which equal or newer than the required version %2." )
00461 .subs( Internal::serverProtocolVersion() )
00462 .subs( SessionPrivate::minimumProtocolVersion() ) );
00463 }
00464 }
00465
00466 void SelfTestDialog::testResources()
00467 {
00468 AgentType::List agentTypes = AgentManager::self()->types();
00469 bool resourceFound = false;
00470 foreach ( const AgentType &type, agentTypes ) {
00471 if ( type.capabilities().contains( QLatin1String("Resource") ) ) {
00472 resourceFound = true;
00473 break;
00474 }
00475 }
00476
00477 const QStringList pathList = XdgBaseDirs::findAllResourceDirs( "data", QLatin1String( "akonadi/agents" ) );
00478 QStandardItem *item = 0;
00479 if ( resourceFound ) {
00480 item = report( Success, ki18n( "Resource agents found." ), ki18n( "At least one resource agent has been found." ) );
00481 } else {
00482 item = report( Error, ki18n( "No resource agents found." ),
00483 ki18n( "No resource agents have been found, Akonadi is not usable without at least one. "
00484 "This usually means that no resource agents are installed or that there is a setup problem. "
00485 "The following paths have been searched: '%1'. "
00486 "The XDG_DATA_DIRS environment variable is set to '%2', make sure this includes all paths "
00487 "where Akonadi agents are installed to." )
00488 .subs( pathList.join( QLatin1String(" ") ) )
00489 .subs( QString::fromLocal8Bit( qgetenv( "XDG_DATA_DIRS" ) ) ) );
00490 }
00491 item->setData( pathList, ListDirectoryRole );
00492 item->setData( QByteArray( "XDG_DATA_DIRS" ), EnvVarRole );
00493 }
00494
00495 void Akonadi::SelfTestDialog::testServerLog()
00496 {
00497 QString serverLog = XdgBaseDirs::saveDir( "data", QLatin1String( "akonadi" ) )
00498 + QDir::separator() + QString::fromLatin1( "akonadiserver.error" );
00499 QFileInfo info( serverLog );
00500 if ( !info.exists() || info.size() <= 0 ) {
00501 report( Success, ki18n( "No current Akonadi server error log found." ),
00502 ki18n( "The Akonadi server did not report any errors during its current startup." ) );
00503 } else {
00504 QStandardItem *item = report( Error, ki18n( "Current Akonadi server error log found." ),
00505 ki18n( "The Akonadi server did report error during startup into %1." ).subs( makeLink( serverLog ) ) );
00506 item->setData( serverLog, FileIncludeRole );
00507 }
00508
00509 serverLog += QLatin1String(".old");
00510 info.setFile( serverLog );
00511 if ( !info.exists() || info.size() <= 0 ) {
00512 report( Success, ki18n( "No previous Akonadi server error log found." ),
00513 ki18n( "The Akonadi server did not report any errors during its previous startup." ) );
00514 } else {
00515 QStandardItem *item = report( Error, ki18n( "Previous Akonadi server error log found." ),
00516 ki18n( "The Akonadi server did report error during its previous startup into %1." ).subs( makeLink( serverLog ) ) );
00517 item->setData( serverLog, FileIncludeRole );
00518 }
00519 }
00520
00521 void SelfTestDialog::testControlLog()
00522 {
00523 QString controlLog = XdgBaseDirs::saveDir( "data", QLatin1String( "akonadi" ) )
00524 + QDir::separator() + QString::fromLatin1( "akonadi_control.error" );
00525 QFileInfo info( controlLog );
00526 if ( !info.exists() || info.size() <= 0 ) {
00527 report( Success, ki18n( "No current Akonadi control error log found." ),
00528 ki18n( "The Akonadi control process did not report any errors during its current startup." ) );
00529 } else {
00530 QStandardItem *item = report( Error, ki18n( "Current Akonadi control error log found." ),
00531 ki18n( "The Akonadi control process did report error during startup into %1." ).subs( makeLink( controlLog ) ) );
00532 item->setData( controlLog, FileIncludeRole );
00533 }
00534
00535 controlLog += QLatin1String(".old");
00536 info.setFile( controlLog );
00537 if ( !info.exists() || info.size() <= 0 ) {
00538 report( Success, ki18n( "No previous Akonadi control error log found." ),
00539 ki18n( "The Akonadi control process did not report any errors during its previous startup." ) );
00540 } else {
00541 QStandardItem *item = report( Error, ki18n( "Previous Akonadi control error log found." ),
00542 ki18n( "The Akonadi control process did report error during its previous startup into %1." ).subs( makeLink( controlLog ) ) );
00543 item->setData( controlLog, FileIncludeRole );
00544 }
00545 }
00546
00547
00548 QString SelfTestDialog::createReport()
00549 {
00550 QString result;
00551 QTextStream s( &result );
00552 s << "Akonadi Server Self-Test Report" << endl;
00553 s << "===============================" << endl;
00554
00555 for ( int i = 0; i < mTestModel->rowCount(); ++i ) {
00556 QStandardItem *item = mTestModel->item( i );
00557 s << endl;
00558 s << "Test " << (i + 1) << ": ";
00559 switch ( item->data( ResultTypeRole ).toInt() ) {
00560 case Skip:
00561 s << "SKIP"; break;
00562 case Success:
00563 s << "SUCCESS"; break;
00564 case Warning:
00565 s << "WARNING"; break;
00566 case Error:
00567 default:
00568 s << "ERROR"; break;
00569 }
00570 s << endl << "--------" << endl;
00571 s << endl;
00572 s << item->data( SummaryRole ).toString() << endl;
00573 s << "Details: " << item->data( DetailsRole ).toString() << endl;
00574 if ( item->data( FileIncludeRole ).isValid() ) {
00575 s << endl;
00576 const QString fileName = item->data( FileIncludeRole ).toString();
00577 QFile f( fileName );
00578 if ( f.open( QFile::ReadOnly ) ) {
00579 s << "File content of '" << fileName << "':" << endl;
00580 s << f.readAll() << endl;
00581 } else {
00582 s << "File '" << fileName << "' could not be opened" << endl;
00583 }
00584 }
00585 if ( item->data( ListDirectoryRole ).isValid() ) {
00586 s << endl;
00587 const QStringList pathList = item->data( ListDirectoryRole ).toStringList();
00588 if ( pathList.isEmpty() )
00589 s << "Directory list is empty." << endl;
00590 foreach ( const QString &path, pathList ) {
00591 s << "Directory listing of '" << path << "':" << endl;
00592 QDir dir( path );
00593 dir.setFilter( QDir::AllEntries | QDir::NoDotAndDotDot );
00594 foreach ( const QString &entry, dir.entryList() )
00595 s << entry << endl;
00596 }
00597 }
00598 if ( item->data( EnvVarRole ).isValid() ) {
00599 s << endl;
00600 const QByteArray envVarName = item->data( EnvVarRole ).toByteArray();
00601 const QByteArray envVarValue = qgetenv( envVarName );
00602 s << "Environment variable " << envVarName << " is set to '" << envVarValue << "'" << endl;
00603 }
00604 }
00605
00606 s << endl;
00607 s.flush();
00608 return result;
00609 }
00610
00611 void SelfTestDialog::saveReport()
00612 {
00613 const QString fileName = KFileDialog::getSaveFileName( KUrl(), QString(), this, i18n("Save Test Report") );
00614 if ( fileName.isEmpty() )
00615 return;
00616
00617 QFile file( fileName );
00618 if ( !file.open( QFile::ReadWrite ) ) {
00619 KMessageBox::error( this, i18n( "Could not open file '%1'", fileName ) );
00620 return;
00621 }
00622
00623 file.write( createReport().toUtf8() );
00624 file.close();
00625 }
00626
00627 void SelfTestDialog::copyReport()
00628 {
00629 QApplication::clipboard()->setText( createReport() );
00630 }
00631
00632 void SelfTestDialog::linkActivated(const QString & link)
00633 {
00634 KRun::runUrl( KUrl::fromPath( link ), QLatin1String("text/plain"), this );
00635 }
00636
00637
00638
00639 #include "selftestdialog_p.moc"