signon  8.40
remotepluginprocess.cpp
Go to the documentation of this file.
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