akonadi
selftestdialog.cpp
00001 /* 00002 Copyright (c) 2008 Volker Krause <vkrause@kde.org> 00003 00004 This library is free software; you can redistribute it and/or modify it 00005 under the terms of the GNU Library General Public License as published by 00006 the Free Software Foundation; either version 2 of the License, or (at your 00007 option) any later version. 00008 00009 This library is distributed in the hope that it will be useful, but WITHOUT 00010 ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 00011 FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public 00012 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 the 00016 Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 00017 02110-1301, USA. 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 // @cond PRIVATE 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(QModelIndex,QModelIndex)), 00085 SLOT(selectionChanged(QModelIndex)) ); 00086 connect( ui.detailsLabel, SIGNAL(linkActivated(QString)), SLOT(linkActivated(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(); // ### default? 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 // be extra sure and get the server version while we are at it 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 // check which backend is used 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 // put blacklisted backends here 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 // @endcond 00667 00668 #include "selftestdialog_p.moc"
This file is part of the KDE documentation.
Documentation copyright © 1996-2012 The KDE developers.
Generated on Mon May 14 2012 04:52:59 by doxygen 1.7.5 written by Dimitri van Heesch, © 1997-2006
Documentation copyright © 1996-2012 The KDE developers.
Generated on Mon May 14 2012 04:52:59 by doxygen 1.7.5 written by Dimitri van Heesch, © 1997-2006
KDE's Doxygen guidelines are available online.