signon
8.58
|
00001 /* 00002 * This file is part of signon 00003 * 00004 * Copyright (C) 2009-2010 Nokia Corporation. 00005 * 00006 * Contact: Alberto Mardegan <alberto.mardegan@canonical.com> 00007 * 00008 * This library is free software; you can redistribute it and/or 00009 * modify it under the terms of the GNU Lesser General Public License 00010 * version 2.1 as published by the Free Software Foundation. 00011 * 00012 * This library is distributed in the hope that it will be useful, but 00013 * WITHOUT ANY WARRANTY; without even the implied warranty of 00014 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 00015 * Lesser General Public License for more details. 00016 * 00017 * You should have received a copy of the GNU Lesser General Public 00018 * License along with this library; if not, write to the Free Software 00019 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 00020 * 02110-1301 USA 00021 */ 00022 #include <QProcess> 00023 #include <QUrl> 00024 #include <QTimer> 00025 #include <QBuffer> 00026 #include <QDataStream> 00027 #include <unistd.h> 00028 00029 #include "debug.h" 00030 #ifdef HAVE_LIBPROXY 00031 #include "my-network-proxy-factory.h" 00032 #endif 00033 #include "remotepluginprocess.h" 00034 00035 // signon-plugins-common 00036 #include "SignOn/blobiohandler.h" 00037 #include "SignOn/ipc.h" 00038 00039 using namespace SignOn; 00040 00041 namespace RemotePluginProcessNS { 00042 00043 static CancelEventThread *cancelThread = NULL; 00044 00045 /* ---------------------- RemotePluginProcess ---------------------- */ 00046 00047 RemotePluginProcess::RemotePluginProcess(QObject *parent): 00048 QObject(parent) 00049 { 00050 m_plugin = NULL; 00051 m_readnotifier = NULL; 00052 m_errnotifier = NULL; 00053 00054 qRegisterMetaType<SignOn::SessionData>("SignOn::SessionData"); 00055 qRegisterMetaType<QString>("QString"); 00056 } 00057 00058 RemotePluginProcess::~RemotePluginProcess() 00059 { 00060 delete m_plugin; 00061 delete m_readnotifier; 00062 delete m_errnotifier; 00063 00064 if (cancelThread) { 00065 cancelThread->quit(); 00066 cancelThread->wait(); 00067 delete cancelThread; 00068 } 00069 } 00070 00071 RemotePluginProcess * 00072 RemotePluginProcess::createRemotePluginProcess(QString &type, QObject *parent) 00073 { 00074 RemotePluginProcess *rpp = new RemotePluginProcess(parent); 00075 00076 //this is needed before plugin is initialized 00077 rpp->setupProxySettings(); 00078 00079 if (!rpp->loadPlugin(type) || 00080 !rpp->setupDataStreams() || 00081 rpp->m_plugin->type() != type) { 00082 delete rpp; 00083 return NULL; 00084 } 00085 return rpp; 00086 } 00087 00088 bool RemotePluginProcess::loadPlugin(QString &type) 00089 { 00090 TRACE() << " loading auth library for " << type; 00091 00092 QLibrary lib(getPluginName(type)); 00093 00094 if (!lib.load()) { 00095 qCritical() << QString("Failed to load %1 (reason: %2)") 00096 .arg(getPluginName(type)).arg(lib.errorString()); 00097 return false; 00098 } 00099 00100 TRACE() << "library loaded"; 00101 00102 typedef AuthPluginInterface* (*SsoAuthPluginInstanceF)(); 00103 SsoAuthPluginInstanceF instance = 00104 (SsoAuthPluginInstanceF)lib.resolve("auth_plugin_instance"); 00105 if (!instance) { 00106 qCritical() << QString("Failed to resolve init function in %1 " 00107 "(reason: %2)") 00108 .arg(getPluginName(type)).arg(lib.errorString()); 00109 return false; 00110 } 00111 00112 TRACE() << "constructor resolved"; 00113 00114 m_plugin = qobject_cast<AuthPluginInterface *>(instance()); 00115 00116 if (!m_plugin) { 00117 qCritical() << QString("Failed to cast object for %1 type") 00118 .arg(type); 00119 return false; 00120 } 00121 00122 connect(m_plugin, SIGNAL(result(const SignOn::SessionData&)), 00123 this, SLOT(result(const SignOn::SessionData&))); 00124 00125 connect(m_plugin, SIGNAL(store(const SignOn::SessionData&)), 00126 this, SLOT(store(const SignOn::SessionData&))); 00127 00128 connect(m_plugin, SIGNAL(error(const SignOn::Error &)), 00129 this, SLOT(error(const SignOn::Error &))); 00130 00131 connect(m_plugin, SIGNAL(userActionRequired(const SignOn::UiSessionData&)), 00132 this, SLOT(userActionRequired(const SignOn::UiSessionData&))); 00133 00134 connect(m_plugin, SIGNAL(refreshed(const SignOn::UiSessionData&)), 00135 this, SLOT(refreshed(const SignOn::UiSessionData&))); 00136 00137 connect(m_plugin, 00138 SIGNAL(statusChanged(const AuthPluginState, const QString&)), 00139 this, SLOT(statusChanged(const AuthPluginState, const QString&))); 00140 00141 m_plugin->setParent(this); 00142 00143 TRACE() << "plugin is fully initialized"; 00144 return true; 00145 } 00146 00147 bool RemotePluginProcess::setupDataStreams() 00148 { 00149 TRACE(); 00150 00151 m_inFile.open(STDIN_FILENO, QIODevice::ReadOnly); 00152 m_outFile.open(STDOUT_FILENO, QIODevice::WriteOnly); 00153 00154 m_readnotifier = new QSocketNotifier(STDIN_FILENO, QSocketNotifier::Read); 00155 m_errnotifier = new QSocketNotifier(STDIN_FILENO, 00156 QSocketNotifier::Exception); 00157 00158 connect(m_readnotifier, SIGNAL(activated(int)), this, SLOT(startTask())); 00159 connect(m_errnotifier, SIGNAL(activated(int)), 00160 this, SIGNAL(processStopped())); 00161 00162 if (!cancelThread) 00163 cancelThread = new CancelEventThread(m_plugin); 00164 00165 TRACE() << "cancel thread created"; 00166 00167 m_blobIOHandler = new BlobIOHandler(&m_inFile, &m_outFile, this); 00168 00169 connect(m_blobIOHandler, 00170 SIGNAL(dataReceived(const QVariantMap &)), 00171 this, 00172 SLOT(sessionDataReceived(const QVariantMap &))); 00173 00174 connect(m_blobIOHandler, 00175 SIGNAL(error()), 00176 this, 00177 SLOT(blobIOError())); 00178 00179 m_blobIOHandler->setReadChannelSocketNotifier(m_readnotifier); 00180 00181 return true; 00182 } 00183 00184 bool RemotePluginProcess::setupProxySettings() 00185 { 00186 TRACE(); 00187 00188 #ifdef HAVE_LIBPROXY 00189 /* Use a libproxy-based proxy factory; this code will no longer be 00190 * needed when https://bugreports.qt-project.org/browse/QTBUG-26295 00191 * is fixed. */ 00192 MyNetworkProxyFactory *proxyFactory = new MyNetworkProxyFactory(); 00193 QNetworkProxyFactory::setApplicationProxyFactory(proxyFactory); 00194 #endif 00195 00196 return true; 00197 } 00198 00199 void RemotePluginProcess::blobIOError() 00200 { 00201 error( 00202 Error(Error::InternalServer, 00203 QLatin1String("Failed to I/O session data to/from the signon daemon."))); 00204 connect(m_readnotifier, SIGNAL(activated(int)), this, SLOT(startTask())); 00205 } 00206 00207 void RemotePluginProcess::result(const SignOn::SessionData &data) 00208 { 00209 disableCancelThread(); 00210 QDataStream out(&m_outFile); 00211 QVariantMap resultDataMap; 00212 00213 foreach(QString key, data.propertyNames()) 00214 resultDataMap[key] = data.getProperty(key); 00215 00216 out << (quint32)PLUGIN_RESPONSE_RESULT; 00217 00218 m_blobIOHandler->sendData(resultDataMap); 00219 00220 m_outFile.flush(); 00221 } 00222 00223 void RemotePluginProcess::store(const SignOn::SessionData &data) 00224 { 00225 QDataStream out(&m_outFile); 00226 QVariantMap storeDataMap; 00227 00228 foreach(QString key, data.propertyNames()) 00229 storeDataMap[key] = data.getProperty(key); 00230 00231 out << (quint32)PLUGIN_RESPONSE_STORE; 00232 00233 m_blobIOHandler->sendData(storeDataMap); 00234 00235 m_outFile.flush(); 00236 } 00237 00238 void RemotePluginProcess::error(const SignOn::Error &err) 00239 { 00240 disableCancelThread(); 00241 00242 QDataStream out(&m_outFile); 00243 00244 out << (quint32)PLUGIN_RESPONSE_ERROR; 00245 out << (quint32)err.type(); 00246 out << err.message(); 00247 m_outFile.flush(); 00248 00249 TRACE() << "error is sent" << err.type() << " " << err.message(); 00250 } 00251 00252 void RemotePluginProcess::userActionRequired(const SignOn::UiSessionData &data) 00253 { 00254 TRACE(); 00255 disableCancelThread(); 00256 00257 QDataStream out(&m_outFile); 00258 QVariantMap resultDataMap; 00259 00260 foreach(QString key, data.propertyNames()) 00261 resultDataMap[key] = data.getProperty(key); 00262 00263 out << (quint32)PLUGIN_RESPONSE_UI; 00264 m_blobIOHandler->sendData(resultDataMap); 00265 m_outFile.flush(); 00266 } 00267 00268 void RemotePluginProcess::refreshed(const SignOn::UiSessionData &data) 00269 { 00270 TRACE(); 00271 disableCancelThread(); 00272 00273 QDataStream out(&m_outFile); 00274 QVariantMap resultDataMap; 00275 00276 foreach(QString key, data.propertyNames()) 00277 resultDataMap[key] = data.getProperty(key); 00278 00279 m_readnotifier->setEnabled(true); 00280 00281 out << (quint32)PLUGIN_RESPONSE_REFRESHED; 00282 00283 m_blobIOHandler->sendData(resultDataMap); 00284 00285 m_outFile.flush(); 00286 } 00287 00288 void RemotePluginProcess::statusChanged(const AuthPluginState state, 00289 const QString &message) 00290 { 00291 TRACE(); 00292 QDataStream out(&m_outFile); 00293 00294 out << (quint32)PLUGIN_RESPONSE_SIGNAL; 00295 out << (quint32)state; 00296 out << message; 00297 00298 m_outFile.flush(); 00299 } 00300 00301 QString RemotePluginProcess::getPluginName(const QString &type) 00302 { 00303 QString dirName = qgetenv("SSO_PLUGINS_DIR"); 00304 if (dirName.isEmpty()) 00305 dirName = QDir::cleanPath(SIGNOND_PLUGINS_DIR); 00306 QString fileName = dirName + 00307 QDir::separator() + 00308 QString(SIGNON_PLUGIN_PREFIX) + 00309 type + 00310 QString(SIGNON_PLUGIN_SUFFIX); 00311 00312 return fileName; 00313 } 00314 00315 void RemotePluginProcess::type() 00316 { 00317 QDataStream out(&m_outFile); 00318 out << m_plugin->type(); 00319 } 00320 00321 void RemotePluginProcess::mechanisms() 00322 { 00323 QDataStream out(&m_outFile); 00324 QStringList mechanisms = m_plugin->mechanisms(); 00325 QVariant mechsVar = mechanisms; 00326 out << mechsVar; 00327 } 00328 00329 void RemotePluginProcess::process() 00330 { 00331 QDataStream in(&m_inFile); 00332 00333 00334 in >> m_currentMechanism; 00335 00336 int processBlobSize = -1; 00337 in >> processBlobSize; 00338 00339 disconnect(m_readnotifier, SIGNAL(activated(int)), this, SLOT(startTask())); 00340 00341 m_currentOperation = PLUGIN_OP_PROCESS; 00342 m_blobIOHandler->receiveData(processBlobSize); 00343 } 00344 00345 void RemotePluginProcess::userActionFinished() 00346 { 00347 QDataStream in(&m_inFile); 00348 int processBlobSize = -1; 00349 in >> processBlobSize; 00350 00351 disconnect(m_readnotifier, SIGNAL(activated(int)), this, SLOT(startTask())); 00352 00353 m_currentOperation = PLUGIN_OP_PROCESS_UI; 00354 m_blobIOHandler->receiveData(processBlobSize); 00355 } 00356 00357 void RemotePluginProcess::refresh() 00358 { 00359 QDataStream in(&m_inFile); 00360 int processBlobSize = -1; 00361 in >> processBlobSize; 00362 00363 disconnect(m_readnotifier, SIGNAL(activated(int)), this, SLOT(startTask())); 00364 00365 m_currentOperation = PLUGIN_OP_REFRESH; 00366 m_blobIOHandler->receiveData(processBlobSize); 00367 } 00368 00369 void RemotePluginProcess::sessionDataReceived(const QVariantMap &sessionDataMap) 00370 { 00371 enableCancelThread(); 00372 TRACE() << "The cancel thread is started"; 00373 00374 if (m_currentOperation == PLUGIN_OP_PROCESS) { 00375 SessionData inData(sessionDataMap); 00376 m_plugin->process(inData, m_currentMechanism); 00377 m_currentMechanism.clear(); 00378 00379 } else if(m_currentOperation == PLUGIN_OP_PROCESS_UI) { 00380 UiSessionData inData(sessionDataMap); 00381 m_plugin->userActionFinished(inData); 00382 00383 } else if(m_currentOperation == PLUGIN_OP_REFRESH) { 00384 UiSessionData inData(sessionDataMap); 00385 m_plugin->refresh(inData); 00386 00387 } else { 00388 TRACE() << "Wrong operation code."; 00389 error(Error(Error::InternalServer, 00390 QLatin1String("Plugin process - invalid operation code."))); 00391 } 00392 00393 m_currentOperation = PLUGIN_OP_STOP; 00394 connect(m_readnotifier, SIGNAL(activated(int)), this, SLOT(startTask())); 00395 } 00396 00397 void RemotePluginProcess::enableCancelThread() 00398 { 00399 QEventLoop loop; 00400 connect(cancelThread, 00401 SIGNAL(started()), 00402 &loop, 00403 SLOT(quit())); 00404 00405 m_readnotifier->setEnabled(false); 00406 QTimer::singleShot(0.5*1000, &loop, SLOT(quit())); 00407 cancelThread->start(); 00408 loop.exec(); 00409 QThread::yieldCurrentThread(); 00410 } 00411 00412 void RemotePluginProcess::disableCancelThread() 00413 { 00414 if (!cancelThread->isRunning()) 00415 return; 00416 00423 cancelThread->quit(); 00424 00425 TRACE() << "Before the isFinished loop "; 00426 00427 int i = 0; 00428 while (!cancelThread->isFinished()) { 00429 cancelThread->quit(); 00430 TRACE() << "Internal iteration " << i++; 00431 usleep(0.005 * 1000000); 00432 } 00433 00434 if (!cancelThread->wait(500)) { 00435 BLAME() << "Cannot disable cancel thread"; 00436 int i; 00437 for (i = 0; i < 5; i++) { 00438 usleep(0.01 * 1000000); 00439 if (cancelThread->wait(500)) 00440 break; 00441 } 00442 00443 if (i == 5) { 00444 BLAME() << "Cannot do anything with cancel thread"; 00445 cancelThread->terminate(); 00446 cancelThread->wait(); 00447 } 00448 } 00449 00450 m_readnotifier->setEnabled(true); 00451 } 00452 00453 void RemotePluginProcess::startTask() 00454 { 00455 quint32 opcode = PLUGIN_OP_STOP; 00456 bool is_stopped = false; 00457 00458 QDataStream in(&m_inFile); 00459 in >> opcode; 00460 00461 switch (opcode) { 00462 case PLUGIN_OP_CANCEL: 00463 { 00464 m_plugin->cancel(); break; 00465 //still do not have clear understanding 00466 //of the cancelation-stop mechanism 00467 //is_stopped = true; 00468 } 00469 break; 00470 case PLUGIN_OP_TYPE: 00471 type(); 00472 break; 00473 case PLUGIN_OP_MECHANISMS: 00474 mechanisms(); 00475 break; 00476 case PLUGIN_OP_PROCESS: 00477 process(); 00478 break; 00479 case PLUGIN_OP_PROCESS_UI: 00480 userActionFinished(); 00481 break; 00482 case PLUGIN_OP_REFRESH: 00483 refresh(); 00484 break; 00485 case PLUGIN_OP_STOP: 00486 is_stopped = true; 00487 break; 00488 default: 00489 { 00490 qCritical() << " unknown operation code: " << opcode; 00491 is_stopped = true; 00492 } 00493 break; 00494 }; 00495 00496 TRACE() << "operation is completed"; 00497 00498 if (!is_stopped) { 00499 if (!m_outFile.flush()) 00500 is_stopped = true; 00501 } 00502 00503 if (is_stopped) 00504 { 00505 m_plugin->abort(); 00506 emit processStopped(); 00507 } 00508 } 00509 00510 CancelEventThread::CancelEventThread(AuthPluginInterface *plugin) 00511 { 00512 m_plugin = plugin; 00513 m_cancelNotifier = 0; 00514 } 00515 00516 CancelEventThread::~CancelEventThread() 00517 { 00518 delete m_cancelNotifier; 00519 } 00520 00521 void CancelEventThread::run() 00522 { 00523 if (!m_cancelNotifier) { 00524 m_cancelNotifier = new QSocketNotifier(STDIN_FILENO, 00525 QSocketNotifier::Read); 00526 connect(m_cancelNotifier, SIGNAL(activated(int)), 00527 this, SLOT(cancel()), Qt::DirectConnection); 00528 } 00529 00530 m_cancelNotifier->setEnabled(true); 00531 exec(); 00532 m_cancelNotifier->setEnabled(false); 00533 } 00534 00535 void CancelEventThread::cancel() 00536 { 00537 char buf[4]; 00538 memset(buf, 0, 4); 00539 int n = 0; 00540 00541 if (!(n = read(STDIN_FILENO, buf, 4))) { 00542 qCritical() << "Cannot read from cancel socket"; 00543 return; 00544 } 00545 00546 /* 00547 * Read the actual value of 00548 * */ 00549 QByteArray ba(buf, 4); 00550 quint32 opcode; 00551 QDataStream ds(ba); 00552 ds >> opcode; 00553 00554 if (opcode != PLUGIN_OP_CANCEL) 00555 qCritical() << "wrong operation code: breakage of remotepluginprocess " 00556 "threads synchronization: " << opcode; 00557 00558 m_plugin->cancel(); 00559 } 00560 00561 } //namespace RemotePluginProcessNS 00562