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