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