signon  8.42
remotepluginprocess.cpp
Go to the documentation of this file.
1 /*
2  * This file is part of signon
3  *
4  * Copyright (C) 2009-2010 Nokia Corporation.
5  *
6  * Contact: Alberto Mardegan <alberto.mardegan@canonical.com>
7  *
8  * This library is free software; you can redistribute it and/or
9  * modify it under the terms of the GNU Lesser General Public License
10  * version 2.1 as published by the Free Software Foundation.
11  *
12  * This library is distributed in the hope that it will be useful, but
13  * WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15  * Lesser General Public License for more details.
16  *
17  * You should have received a copy of the GNU Lesser General Public
18  * License along with this library; if not, write to the Free Software
19  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
20  * 02110-1301 USA
21  */
22 #include <QNetworkProxy>
23 #include <QProcess>
24 #include <QUrl>
25 #include <QTimer>
26 #include <QBuffer>
27 #include <QDataStream>
28 #include <unistd.h>
29 
30 #ifdef HAVE_GCONF
31 #include <gq/GConfItem>
32 #endif
33 
34 #include "debug.h"
35 #include "remotepluginprocess.h"
36 
37 // signon-plugins-common
38 #include "SignOn/blobiohandler.h"
39 #include "SignOn/ipc.h"
40 
41 using namespace SignOn;
42 
43 namespace RemotePluginProcessNS {
44 
45 static CancelEventThread *cancelThread = NULL;
46 
47 /* ---------------------- RemotePluginProcess ---------------------- */
48 
49 RemotePluginProcess::RemotePluginProcess(QObject *parent):
50  QObject(parent)
51 {
52  m_plugin = NULL;
53  m_readnotifier = NULL;
54  m_errnotifier = NULL;
55 
56  qRegisterMetaType<SignOn::SessionData>("SignOn::SessionData");
57  qRegisterMetaType<QString>("QString");
58 }
59 
61 {
62  delete m_plugin;
63  delete m_readnotifier;
64  delete m_errnotifier;
65 
66  if (cancelThread) {
67  cancelThread->quit();
68  cancelThread->wait();
69  delete cancelThread;
70  }
71 }
72 
74 RemotePluginProcess::createRemotePluginProcess(QString &type, QObject *parent)
75 {
76  RemotePluginProcess *rpp = new RemotePluginProcess(parent);
77 
78  //this is needed before plugin is initialized
79  rpp->setupProxySettings();
80 
81  if (!rpp->loadPlugin(type) ||
82  !rpp->setupDataStreams() ||
83  rpp->m_plugin->type() != type) {
84  delete rpp;
85  return NULL;
86  }
87  return rpp;
88 }
89 
91 {
92  TRACE() << " loading auth library for " << type;
93 
94  QLibrary lib(getPluginName(type));
95 
96  if (!lib.load()) {
97  qCritical() << QString("Failed to load %1 (reason: %2)")
98  .arg(getPluginName(type)).arg(lib.errorString());
99  return false;
100  }
101 
102  TRACE() << "library loaded";
103 
104  typedef AuthPluginInterface* (*SsoAuthPluginInstanceF)();
105  SsoAuthPluginInstanceF instance =
106  (SsoAuthPluginInstanceF)lib.resolve("auth_plugin_instance");
107  if (!instance) {
108  qCritical() << QString("Failed to resolve init function in %1 "
109  "(reason: %2)")
110  .arg(getPluginName(type)).arg(lib.errorString());
111  return false;
112  }
113 
114  TRACE() << "constructor resolved";
115 
116  m_plugin = qobject_cast<AuthPluginInterface *>(instance());
117 
118  if (!m_plugin) {
119  qCritical() << QString("Failed to cast object for %1 type")
120  .arg(type);
121  return false;
122  }
123 
124  connect(m_plugin, SIGNAL(result(const SignOn::SessionData&)),
125  this, SLOT(result(const SignOn::SessionData&)));
126 
127  connect(m_plugin, SIGNAL(store(const SignOn::SessionData&)),
128  this, SLOT(store(const SignOn::SessionData&)));
129 
130  connect(m_plugin, SIGNAL(error(const SignOn::Error &)),
131  this, SLOT(error(const SignOn::Error &)));
132 
133  connect(m_plugin, SIGNAL(userActionRequired(const SignOn::UiSessionData&)),
134  this, SLOT(userActionRequired(const SignOn::UiSessionData&)));
135 
136  connect(m_plugin, SIGNAL(refreshed(const SignOn::UiSessionData&)),
137  this, SLOT(refreshed(const SignOn::UiSessionData&)));
138 
139  connect(m_plugin,
140  SIGNAL(statusChanged(const AuthPluginState, const QString&)),
141  this, SLOT(statusChanged(const AuthPluginState, const QString&)));
142 
143  m_plugin->setParent(this);
144 
145  TRACE() << "plugin is fully initialized";
146  return true;
147 }
148 
150 {
151  TRACE();
152 
153  m_inFile.open(STDIN_FILENO, QIODevice::ReadOnly);
154  m_outFile.open(STDOUT_FILENO, QIODevice::WriteOnly);
155 
156  m_readnotifier = new QSocketNotifier(STDIN_FILENO, QSocketNotifier::Read);
157  m_errnotifier = new QSocketNotifier(STDIN_FILENO,
158  QSocketNotifier::Exception);
159 
160  connect(m_readnotifier, SIGNAL(activated(int)), this, SLOT(startTask()));
161  connect(m_errnotifier, SIGNAL(activated(int)),
162  this, SIGNAL(processStopped()));
163 
164  if (!cancelThread)
165  cancelThread = new CancelEventThread(m_plugin);
166 
167  TRACE() << "cancel thread created";
168 
169  m_blobIOHandler = new BlobIOHandler(&m_inFile, &m_outFile, this);
170 
171  connect(m_blobIOHandler,
172  SIGNAL(dataReceived(const QVariantMap &)),
173  this,
174  SLOT(sessionDataReceived(const QVariantMap &)));
175 
176  connect(m_blobIOHandler,
177  SIGNAL(error()),
178  this,
179  SLOT(blobIOError()));
180 
181  m_blobIOHandler->setReadChannelSocketNotifier(m_readnotifier);
182 
183  return true;
184 }
185 
187 {
188  TRACE();
189  //set application default proxy
190  QNetworkProxy networkProxy = QNetworkProxy::applicationProxy();
191 
192 #ifdef HAVE_GCONF
193  //get proxy settings from GConf
194  GConfItem *hostItem = new GConfItem("/system/http_proxy/host");
195  if (hostItem->value().canConvert(QVariant::String)) {
196  QString host = hostItem->value().toString();
197  GConfItem *portItem = new GConfItem("/system/http_proxy/port");
198  uint port = portItem->value().toUInt();
199  networkProxy = QNetworkProxy(QNetworkProxy::HttpProxy,
200  host, port);
201  delete portItem;
202  }
203  delete hostItem;
204 #endif
205 
206  //get system env for proxy
207  QString proxy = qgetenv("http_proxy");
208  if (!proxy.isEmpty()) {
209  QUrl proxyUrl(proxy);
210  if (!proxyUrl.host().isEmpty()) {
211  networkProxy = QNetworkProxy(QNetworkProxy::HttpProxy,
212  proxyUrl.host(),
213  proxyUrl.port(),
214  proxyUrl.userName(),
215  proxyUrl.password());
216  }
217  }
218 
219  //add other proxy types here
220 
221  TRACE() << networkProxy.hostName() << ":" << networkProxy.port();
222  QNetworkProxy::setApplicationProxy(networkProxy);
223  return true;
224 }
225 
226 void RemotePluginProcess::blobIOError()
227 {
228  error(
229  Error(Error::InternalServer,
230  QLatin1String("Failed to I/O session data to/from the signon daemon.")));
231  connect(m_readnotifier, SIGNAL(activated(int)), this, SLOT(startTask()));
232 }
233 
234 void RemotePluginProcess::result(const SignOn::SessionData &data)
235 {
236  disableCancelThread();
237  QDataStream out(&m_outFile);
238  QVariantMap resultDataMap;
239 
240  foreach(QString key, data.propertyNames())
241  resultDataMap[key] = data.getProperty(key);
242 
243  out << (quint32)PLUGIN_RESPONSE_RESULT;
244 
245  m_blobIOHandler->sendData(resultDataMap);
246 
247  m_outFile.flush();
248 }
249 
250 void RemotePluginProcess::store(const SignOn::SessionData &data)
251 {
252  QDataStream out(&m_outFile);
253  QVariantMap storeDataMap;
254 
255  foreach(QString key, data.propertyNames())
256  storeDataMap[key] = data.getProperty(key);
257 
258  out << (quint32)PLUGIN_RESPONSE_STORE;
259 
260  m_blobIOHandler->sendData(storeDataMap);
261 
262  m_outFile.flush();
263 }
264 
265 void RemotePluginProcess::error(const SignOn::Error &err)
266 {
267  disableCancelThread();
268 
269  QDataStream out(&m_outFile);
270 
271  out << (quint32)PLUGIN_RESPONSE_ERROR;
272  out << (quint32)err.type();
273  out << err.message();
274  m_outFile.flush();
275 
276  TRACE() << "error is sent" << err.type() << " " << err.message();
277 }
278 
279 void RemotePluginProcess::userActionRequired(const SignOn::UiSessionData &data)
280 {
281  TRACE();
282  disableCancelThread();
283 
284  QDataStream out(&m_outFile);
285  QVariantMap resultDataMap;
286 
287  foreach(QString key, data.propertyNames())
288  resultDataMap[key] = data.getProperty(key);
289 
290  out << (quint32)PLUGIN_RESPONSE_UI;
291  m_blobIOHandler->sendData(resultDataMap);
292  m_outFile.flush();
293 }
294 
295 void RemotePluginProcess::refreshed(const SignOn::UiSessionData &data)
296 {
297  TRACE();
298  disableCancelThread();
299 
300  QDataStream out(&m_outFile);
301  QVariantMap resultDataMap;
302 
303  foreach(QString key, data.propertyNames())
304  resultDataMap[key] = data.getProperty(key);
305 
306  m_readnotifier->setEnabled(true);
307 
308  out << (quint32)PLUGIN_RESPONSE_REFRESHED;
309 
310  m_blobIOHandler->sendData(resultDataMap);
311 
312  m_outFile.flush();
313 }
314 
315 void RemotePluginProcess::statusChanged(const AuthPluginState state,
316  const QString &message)
317 {
318  TRACE();
319  QDataStream out(&m_outFile);
320 
321  out << (quint32)PLUGIN_RESPONSE_SIGNAL;
322  out << (quint32)state;
323  out << message;
324 
325  m_outFile.flush();
326 }
327 
328 QString RemotePluginProcess::getPluginName(const QString &type)
329 {
330  QString dirName = qgetenv("SSO_PLUGINS_DIR");
331  if (dirName.isEmpty())
332  dirName = QDir::cleanPath(SIGNOND_PLUGINS_DIR);
333  QString fileName = dirName +
334  QDir::separator() +
335  QString(SIGNON_PLUGIN_PREFIX) +
336  type +
337  QString(SIGNON_PLUGIN_SUFFIX);
338 
339  return fileName;
340 }
341 
342 void RemotePluginProcess::type()
343 {
344  QDataStream out(&m_outFile);
345  out << m_plugin->type();
346 }
347 
348 void RemotePluginProcess::mechanisms()
349 {
350  QDataStream out(&m_outFile);
351  QStringList mechanisms = m_plugin->mechanisms();
352  QVariant mechsVar = mechanisms;
353  out << mechsVar;
354 }
355 
356 void RemotePluginProcess::process()
357 {
358  QDataStream in(&m_inFile);
359 
360 
361  in >> m_currentMechanism;
362 
363  int processBlobSize = -1;
364  in >> processBlobSize;
365 
366  disconnect(m_readnotifier, SIGNAL(activated(int)), this, SLOT(startTask()));
367 
368  m_currentOperation = PLUGIN_OP_PROCESS;
369  m_blobIOHandler->receiveData(processBlobSize);
370 }
371 
372 void RemotePluginProcess::userActionFinished()
373 {
374  QDataStream in(&m_inFile);
375  int processBlobSize = -1;
376  in >> processBlobSize;
377 
378  disconnect(m_readnotifier, SIGNAL(activated(int)), this, SLOT(startTask()));
379 
380  m_currentOperation = PLUGIN_OP_PROCESS_UI;
381  m_blobIOHandler->receiveData(processBlobSize);
382 }
383 
384 void RemotePluginProcess::refresh()
385 {
386  QDataStream in(&m_inFile);
387  int processBlobSize = -1;
388  in >> processBlobSize;
389 
390  disconnect(m_readnotifier, SIGNAL(activated(int)), this, SLOT(startTask()));
391 
392  m_currentOperation = PLUGIN_OP_REFRESH;
393  m_blobIOHandler->receiveData(processBlobSize);
394 }
395 
396 void RemotePluginProcess::sessionDataReceived(const QVariantMap &sessionDataMap)
397 {
398  enableCancelThread();
399  TRACE() << "The cancel thread is started";
400 
401  if (m_currentOperation == PLUGIN_OP_PROCESS) {
402  SessionData inData(sessionDataMap);
403  m_plugin->process(inData, m_currentMechanism);
404  m_currentMechanism.clear();
405 
406  } else if(m_currentOperation == PLUGIN_OP_PROCESS_UI) {
407  UiSessionData inData(sessionDataMap);
408  m_plugin->userActionFinished(inData);
409 
410  } else if(m_currentOperation == PLUGIN_OP_REFRESH) {
411  UiSessionData inData(sessionDataMap);
412  m_plugin->refresh(inData);
413 
414  } else {
415  TRACE() << "Wrong operation code.";
416  error(Error(Error::InternalServer,
417  QLatin1String("Plugin process - invalid operation code.")));
418  }
419 
420  m_currentOperation = PLUGIN_OP_STOP;
421  connect(m_readnotifier, SIGNAL(activated(int)), this, SLOT(startTask()));
422 }
423 
424 void RemotePluginProcess::enableCancelThread()
425 {
426  QEventLoop loop;
427  connect(cancelThread,
428  SIGNAL(started()),
429  &loop,
430  SLOT(quit()));
431 
432  m_readnotifier->setEnabled(false);
433  QTimer::singleShot(0.5*1000, &loop, SLOT(quit()));
434  cancelThread->start();
435  loop.exec();
436  QThread::yieldCurrentThread();
437 }
438 
439 void RemotePluginProcess::disableCancelThread()
440 {
441  if (!cancelThread->isRunning())
442  return;
443 
450  cancelThread->quit();
451 
452  TRACE() << "Before the isFinished loop ";
453 
454  int i = 0;
455  while (!cancelThread->isFinished()) {
456  cancelThread->quit();
457  TRACE() << "Internal iteration " << i++;
458  usleep(0.005 * 1000000);
459  }
460 
461  if (!cancelThread->wait(500)) {
462  BLAME() << "Cannot disable cancel thread";
463  int i;
464  for (i = 0; i < 5; i++) {
465  usleep(0.01 * 1000000);
466  if (cancelThread->wait(500))
467  break;
468  }
469 
470  if (i == 5) {
471  BLAME() << "Cannot do anything with cancel thread";
472  cancelThread->terminate();
473  cancelThread->wait();
474  }
475  }
476 
477  m_readnotifier->setEnabled(true);
478 }
479 
481 {
482  quint32 opcode = PLUGIN_OP_STOP;
483  bool is_stopped = false;
484 
485  QDataStream in(&m_inFile);
486  in >> opcode;
487 
488  switch (opcode) {
489  case PLUGIN_OP_CANCEL:
490  {
491  m_plugin->cancel(); break;
492  //still do not have clear understanding
493  //of the cancelation-stop mechanism
494  //is_stopped = true;
495  }
496  break;
497  case PLUGIN_OP_TYPE:
498  type();
499  break;
500  case PLUGIN_OP_MECHANISMS:
501  mechanisms();
502  break;
503  case PLUGIN_OP_PROCESS:
504  process();
505  break;
506  case PLUGIN_OP_PROCESS_UI:
507  userActionFinished();
508  break;
509  case PLUGIN_OP_REFRESH:
510  refresh();
511  break;
512  case PLUGIN_OP_STOP:
513  is_stopped = true;
514  break;
515  default:
516  {
517  qCritical() << " unknown operation code: " << opcode;
518  is_stopped = true;
519  }
520  break;
521  };
522 
523  TRACE() << "operation is completed";
524 
525  if (!is_stopped) {
526  if (!m_outFile.flush())
527  is_stopped = true;
528  }
529 
530  if (is_stopped)
531  {
532  m_plugin->abort();
533  emit processStopped();
534  }
535 }
536 
537 CancelEventThread::CancelEventThread(AuthPluginInterface *plugin)
538 {
539  m_plugin = plugin;
540  m_cancelNotifier = 0;
541 }
542 
544 {
545  delete m_cancelNotifier;
546 }
547 
549 {
550  if (!m_cancelNotifier) {
551  m_cancelNotifier = new QSocketNotifier(STDIN_FILENO,
552  QSocketNotifier::Read);
553  connect(m_cancelNotifier, SIGNAL(activated(int)),
554  this, SLOT(cancel()), Qt::DirectConnection);
555  }
556 
557  m_cancelNotifier->setEnabled(true);
558  exec();
559  m_cancelNotifier->setEnabled(false);
560 }
561 
563 {
564  char buf[4];
565  memset(buf, 0, 4);
566  int n = 0;
567 
568  if (!(n = read(STDIN_FILENO, buf, 4))) {
569  qCritical() << "Cannot read from cancel socket";
570  return;
571  }
572 
573  /*
574  * Read the actual value of
575  * */
576  QByteArray ba(buf, 4);
577  quint32 opcode;
578  QDataStream ds(ba);
579  ds >> opcode;
580 
581  if (opcode != PLUGIN_OP_CANCEL)
582  qCritical() << "wrong operation code: breakage of remotepluginprocess "
583  "threads synchronization: " << opcode;
584 
585  m_plugin->cancel();
586 }
587 
588 } //namespace RemotePluginProcessNS
589