From a390fe0d9ddb66cfe1f876bed5f99c2a31e03b9a Mon Sep 17 00:00:00 2001 From: Konrad Rosenbaum Date: Sat, 9 Jul 2016 22:40:23 +0200 Subject: [PATCH] conversion to session manager: stage 1 session manager as external process --- Makefile | 5 +- basics.pri | 2 +- iface/msinterface.cpp | 24 +++- iface/msinterface.h | 8 ++ sesscli/scli.cpp | 201 +++++++++++++++++++++++++++++++++ sesscli/scli.h | 74 ++++++++++++ sesscli/scrand.h | 63 +++++++++++ sesscli/scrand.pri | 5 + sesscli/sesscli.pri | 5 + sesscli/sesscli.pro | 27 +++++ sessman/icon.qrc | 9 ++ sessman/login.cpp | 184 +++++++++++++++++++++++++++++++ sessman/login.h | 52 +++++++++ sessman/sessman.pro | 27 +++++ sessman/sman.cpp | 281 +++++++++++++++++++++++++++++++++++++++++++++++ sessman/sman.h | 59 ++++++++++ src/dialogs/dialogs.pri | 4 +- src/dialogs/login.cpp | 176 ----------------------------- src/dialogs/login.h | 45 -------- src/libs.pri | 3 + src/main.cpp | 21 +++- src/mwin/overview.cpp | 22 +++- src/mwin/overview.h | 4 +- 23 files changed, 1057 insertions(+), 244 deletions(-) create mode 100644 sesscli/scli.cpp create mode 100644 sesscli/scli.h create mode 100644 sesscli/scrand.h create mode 100644 sesscli/scrand.pri create mode 100644 sesscli/sesscli.pri create mode 100644 sesscli/sesscli.pro create mode 100644 sessman/icon.qrc create mode 100644 sessman/login.cpp create mode 100644 sessman/login.h create mode 100644 sessman/sessman.pro create mode 100644 sessman/sman.cpp create mode 100644 sessman/sman.h delete mode 100644 src/dialogs/login.cpp delete mode 100644 src/dialogs/login.h diff --git a/Makefile b/Makefile index 5aee58f..784629c 100644 --- a/Makefile +++ b/Makefile @@ -101,8 +101,11 @@ server: wob client: wob taurus tzone wbase cd iface && $(QMAKE) $(QMAKEFLAGS) && $(MAKE) + cd cfglib && $(QMAKE) $(QMAKEFLAGS) && $(MAKE) + cd sesscli && $(QMAKE) $(QMAKEFLAGS) && $(MAKE) cd src && $(QMAKE) $(QMAKEFLAGS) && $(MAKE) cd mainapp && $(QMAKE) $(QMAKEFLAGS) && $(MAKE) + cd sessman && $(QMAKE) $(QMAKEFLAGS) && $(MAKE) cd plugins && $(QMAKE) $(QMAKEFLAGS) && $(MAKE) taurus: @@ -130,7 +133,7 @@ lrelease: cd src && $(LREL) smoke.pro cd iface && $(LREL) iface.pro for i in `find plugins -name '*.pro'` ; do $(LREL) $$i ; done - mkdir -p bin && cp src/smoke*.qm iface/smoke*.qm bin + mkdir -p bin && cp src/smoke*.qm iface/smoke*.qm sessman/*.qm bin cp `find plugins -name '*.qm'` bin -$(MAKE) -C www/translations LREL=$(LREL) XPAT=$(XPAT) lrelease diff --git a/basics.pri b/basics.pri index b014e21..ea115eb 100644 --- a/basics.pri +++ b/basics.pri @@ -7,7 +7,7 @@ DESTDIR = $$PWD/bin # this is default on Windows, we have it here to have all systems behave the same way CONFIG += hide_symbols #use PRL files (link info) -CONFIG += create_prl link_prl +CONFIG += create_prl link_prl #put debug symbols in separate file (on linux) CONFIG += separate_debug_info #enable C++-11 features diff --git a/iface/msinterface.cpp b/iface/msinterface.cpp index 5a3fd1e..ece3e4d 100644 --- a/iface/msinterface.cpp +++ b/iface/msinterface.cpp @@ -106,16 +106,24 @@ bool MSInterface::login(QString username,QString passwd) { m_uname=username;m_passwd=passwd; MTLogin lg=MTLogin::query(username,passwd,m_host,m_hostkey); - if(lg.stage()==lg.Error) + if(lg.stage()==lg.Error){ MBoxWrapper::warning(tr("Warning"),tr("Login failed: %1").arg(tr(lg.errorString().toLatin1()))); - else - setSessionId(lg.getsessionid()); + return false; + } if(lg.stage()!=lg.Success)return false; //schedule re-login before session times out int msecs=QDateTime::currentDateTime().msecsTo(QDateTime::fromTime_t(lg.getvalidtill())); msecs-=120000;//do it 2 min before session runs out if(msecs>100000)//but only if it does not become annoying... - QTimer::singleShot(msecs,this,SLOT(relogin())); + QTimer::singleShot(msecs,this,SIGNAL(needRelogin())); + // initialize + return loginSession(m_uname,lg.getsessionid()); +} + +bool MSInterface::loginSession (QString username, QString sessionid ) +{ + m_uname=username; + setSessionId(sessionid); //get rights and roles MTGetMyRights mrt=MTGetMyRights::asyncQuery(); MTGetMyRoles mrl=MTGetMyRoles::asyncQuery(); @@ -130,7 +138,7 @@ bool MSInterface::login(QString username,QString passwd) // qDebug()<<"have flags"< allRights()const{return userrights;} @@ -117,6 +120,8 @@ class MSIFACE_EXPORT MSInterface:public MInterface public slots: /**logs into the server, returns true on success*/ bool login(QString username,QString passwd); + /**initializes with a known session ID, returns true on success*/ + bool loginSession(QString username,QString sessionid); /**logs out of the server*/ void logout(); /**refreshes the login*/ @@ -128,6 +133,9 @@ class MSIFACE_EXPORT MSInterface:public MInterface /**force template store to update its templates*/ void updateTemplates(); + signals: + /// emitted when a re-login would be necessary + void needRelogin(); private: QString profileid,m_sessid,m_uname,m_passwd,m_host,m_hostkey; mutable QListuserrights; diff --git a/sesscli/scli.cpp b/sesscli/scli.cpp new file mode 100644 index 0000000..5bf5751 --- /dev/null +++ b/sesscli/scli.cpp @@ -0,0 +1,201 @@ +// +// C++ Implementation: Session Client +// +// Description: Session Client Class - connects to a session manager (or even starts one) +// and enables exchange of session data. +// +// +// Author: Konrad Rosenbaum , (C) 2016 +// +// Copyright: See README/COPYING.GPL files that come with this distribution +// +// + +#include "scli.h" +#include "scrand.h" + +#include +#include +#include +#include +#include +#include +#include + +MSessionClient::MSessionClient() +{ + //check parameters for socket name + QString sms; + for(QString arg:qApp->arguments()) + if(arg.startsWith("-sms:")){ + sms=arg.mid(5); + break; + } + //none found: start session manager and wait for socket name + if(sms.isEmpty()){ + //generate reverse socket + QString key=QString::fromLatin1(MagicSmokeRandom::getRandomBytes(32).toHex()); + QLocalServer serv; + serv.setSocketOptions(QLocalServer::UserAccessOption); + serv.setMaxPendingConnections(1); + if(!serv.listen("magicsmoke-smr-"+key)){ + qDebug()<<"Ouch! Unable to create local socket server to communicate with Session Manager!"; + return; + } + //start process + qDebug()<<"Starting Session Manager..."; + if(!QProcess::startDetached(qApp->applicationDirPath()+"/magicsmoke-sessman", QStringList()<<"-slave:"+key)){ + qDebug()<<"Ouch! Unable to start session manager!"; + return; + } + //wait for connection + if(!serv.waitForNewConnection(10000)){ + qDebug()<<"Ouch! Session Manager did not respond!"; + return; + } + QLocalSocket *s=serv.nextPendingConnection(); + if(s==nullptr){ + qDebug()<<"Ouch! Session Manager did not connect back! Something is strange."; + return; + } + //read Session ID data + if(!s->waitForReadyRead(10000)){ + qDebug()<<"Ouch! Session Manager did not send a socket ID. Giving up."; + s->deleteLater(); + return; + } + sms=QString::fromLatin1(s->readLine().trimmed()); + s->deleteLater(); + serv.close(); + if(sms.size()!=64 || !QRegExp("[a-f0-9]+",Qt::CaseInsensitive).exactMatch(sms)){ + qDebug()<<"Ouch! Session Manager did not send a valid socket ID."<connectToServer("magicsmoke-sms-"+sms); + if(!s->waitForConnected(10000)){ + qDebug()<<"Ouch! Unable to connect to Session Manager!"; + s->deleteLater(); + return; + } + msocket=s; + connect(s,SIGNAL(readyRead()),this,SLOT(readSocket())); + connect(s,SIGNAL(disconnected()),this,SLOT(socketLost())); + connect(s,SIGNAL(error(QLocalSocket::LocalSocketError)),this,SLOT(socketLost())); + s->write("clientinit magicsmoke\n"); +} + +MSessionClient::~MSessionClient() +{ + if(msocket!=nullptr) + msocket->close(); +} + +bool MSessionClient::isConnected() const +{ + return msocket!=nullptr && msocket->isOpen(); +} + +QString MSessionClient::currentSessionId() const +{ + return msid; +} + +QString MSessionClient::currentProfileId() const +{ + return mprofile; +} + +QString MSessionClient::currentUsername() const +{ + return muser; +} + +bool MSessionClient::sessionIsAvailable() const +{ + return !msid.isEmpty(); +} + +bool MSessionClient::waitForSessionAvailable() +{ + if(!isConnected())return false; + if(!msid.isEmpty())return true; + //wait for signal + QEventLoop loop; + connect(this,SIGNAL(sessionIdChanged(QString)),&loop,SLOT(quit())); + connect(this,SIGNAL(managerLost()),&loop,SLOT(quit())); + loop.exec(); + return !msid.isEmpty(); +} + +QList< QPair< QString, QString > > MSessionClient::menuEntries() const +{ + if(!isConnected())return QList>(); + if(mmenu.isEmpty()){ + //get menu + QEventLoop loop; + connect(this,SIGNAL(menuChanged()),&loop,SLOT(quit())); + connect(this,SIGNAL(managerLost()),&loop,SLOT(quit())); + QTimer::singleShot(5000,&loop,SLOT(quit())); + msocket->write("getmenu\n"); + msocket->waitForBytesWritten(1000); + loop.exec(); + } + return mmenu; +} + +void MSessionClient::socketLost() +{ + qDebug()<<"Warning: Session Manager Socket lost!"; + if(msocket) + msocket->deleteLater(); + msocket=nullptr; + msid.clear(); + emit managerLost(); + emit sessionLost(); +} + +void MSessionClient::readSocket() +{ + while(msocket->canReadLine()){ + const QString line=QString::fromUtf8(msocket->readLine()).trimmed(); + const int pos=line.indexOf(' '); + const QString cmd=pos>0?line.left(pos):line; + const QString par=pos>0?line.mid(pos+1):QString(); + qDebug()<<"Received session manager command"<isOpen()) + msocket->write(QString("exec "+cmd+"\n").toUtf8()); +} + diff --git a/sesscli/scli.h b/sesscli/scli.h new file mode 100644 index 0000000..5a874ae --- /dev/null +++ b/sesscli/scli.h @@ -0,0 +1,74 @@ +// +// C++ Interface: Session Client +// +// Description: Session Client Class - connects to a session manager (or even starts one) +// and enables exchange of session data. +// +// +// Author: Konrad Rosenbaum , (C) 2016 +// +// Copyright: See README/COPYING.GPL files that come with this distribution +// +// + +#ifndef MAGICSMOKE_SC_SCLI_H +#define MAGICSMOKE_SC_SCLI_H + +#include +#include +#include +#include + +#ifdef MAGICSMOKE_SESSCLI_LIB_BUILD +# define MAGICSMOKE_SESSCLI_EXPORT Q_DECL_EXPORT +#else +# define MAGICSMOKE_SESSCLI_EXPORT Q_DECL_IMPORT +#endif + +class MAGICSMOKE_SESSCLI_EXPORT MSessionClient:public QObject +{ + Q_OBJECT +public: + ///Initialize the session client, make sure it is connected to the Session Manager + MSessionClient(); + ///Disconnect the session client. + virtual ~MSessionClient(); + + ///Wait until a session is available or the Session Manager quits. + ///\returns true if a session is available, false on error + virtual bool waitForSessionAvailable(); + + ///Returns true if there currently is a session available. + virtual bool sessionIsAvailable()const; + ///Returns the session id. + virtual QString currentSessionId()const; + ///Returns the profile id. + virtual QString currentProfileId()const; + ///Returns the user name + virtual QString currentUsername()const; + ///Returns true if the client is connected to a manager. If false there is something wrong. + virtual bool isConnected()const; + + virtual QList> menuEntries()const; + +public slots: + void execServerCommand(QString); + +signals: + void sessionIdChanged(QString sessionId); + void sessionLost(); + void managerLost(); + void menuChanged(); + +private slots: + void socketLost(); + void readSocket(); + +private: + QLocalSocket*msocket=nullptr; + QString msid,mprofile,muser; + QList> mmenu; +}; + + +#endif diff --git a/sesscli/scrand.h b/sesscli/scrand.h new file mode 100644 index 0000000..873c074 --- /dev/null +++ b/sesscli/scrand.h @@ -0,0 +1,63 @@ +// +// C++ Interface: Random Number Retriever +// +// +// Author: Konrad Rosenbaum , (C) 2016 +// +// Copyright: See README/COPYING.GPL files that come with this distribution +// +// + +#ifndef MAGICSMOKE_SC_RAND_H +#define MAGICSMOKE_SC_RAND_H + +#ifdef Q_OS_WIN32 +#include +#pragma comment(lib, "advapi32.lib") +#else +#include +#endif + +#include + +namespace MagicSmokeRandom { + +/// Get some crypto strength random bytes. May return less than requested. +inline QByteArray getRandomBytes(quint8 bytes) +{ + if(bytes==0)return QByteArray(); +#if defined(Q_OS_UNIX)||defined(Q_OS_LINUX)||defined(Q_OS_DARWIN) + //try urandom, then random + QFile fd("/dev/urandom"); + if(!fd.open(QIODevice::ReadOnly)){ + fd.setFileName("/dev/random"); + if(!fd.open(QIODevice::ReadOnly)){ + qDebug()<<"Unable to open /dev/{u}random - sorry."; + return QByteArray(); + } + } + QByteArray r=fd.read(bytes); + fd.close(); + return r; +#elif defined(Q_OS_WIN32) + BYTE data[256]; + HCRYPTPROV hCryptProv=0; + if (!::CryptAcquireContextW(&hProvider, 0, 0, PROV_RSA_FULL, CRYPT_VERIFYCONTEXT | CRYPT_SILENT)){ + qDebug()<<"Error: Unable to acquire crypto context."; + return QByteArray(); + } + QByteArray r; + if(::CryptGenRandom(hCryptProv,bytes,data)){ + r=QByteArray((const char*)data,bytes); + }else{ + qDebug()<<"Error: Unable to get random numbers from OS."; + } + if(!::CryptReleaseContext(hProvider, 0)) + qDebug()<<"Warning: unable to release crypto context!"; + return r; +#endif +} + +} + +#endif diff --git a/sesscli/scrand.pri b/sesscli/scrand.pri new file mode 100644 index 0000000..9d95f27 --- /dev/null +++ b/sesscli/scrand.pri @@ -0,0 +1,5 @@ +# Random Stuff + +win32 { +#LIBS += -ladvapi32 +} diff --git a/sesscli/sesscli.pri b/sesscli/sesscli.pri new file mode 100644 index 0000000..fe0ec37 --- /dev/null +++ b/sesscli/sesscli.pri @@ -0,0 +1,5 @@ +#Include PRI for the session client library + +LIBS += -lmagicsmoke-sesscli +INCLUDEPATH += $$PWD/../sesscli +QT += network diff --git a/sesscli/sesscli.pro b/sesscli/sesscli.pro new file mode 100644 index 0000000..9f7e5d4 --- /dev/null +++ b/sesscli/sesscli.pro @@ -0,0 +1,27 @@ +#Project File for MagicSmoke Session Client Library +# (c) Konrad Rosenbaum, 2016 + +#basics +TEMPLATE = lib +VERSION = 0.1 +TARGET = magicsmoke-sesscli + +include(../basics.pri) +include(scrand.pri) + +#Localization +TRANSLATIONS = \ + smoke-sc_de.ts \ + smoke-sc_de_SAX.ts \ + smoke-sc_en.ts + +#main source files +SOURCES = scli.cpp +HEADERS = scli.h +INCLUDEPATH += . + +#make sure exports are ok +DEFINES += MAGICSMOKE_SESSCLI_LIB_BUILD=1 + +#make sure the correct Qt DLLs are used +QT += network diff --git a/sessman/icon.qrc b/sessman/icon.qrc new file mode 100644 index 0000000..9be79e1 --- /dev/null +++ b/sessman/icon.qrc @@ -0,0 +1,9 @@ + + + + + + ../src/images/icon.png + + diff --git a/sessman/login.cpp b/sessman/login.cpp new file mode 100644 index 0000000..1566497 --- /dev/null +++ b/sessman/login.cpp @@ -0,0 +1,184 @@ +// +// C++ Implementation: mainwindow +// +// Description: +// +// +// Author: Konrad Rosenbaum , (C) 2007-2011 +// +// Copyright: See README/COPYING.GPL files that come with this distribution +// +// + +// #include "main.h" +#include "login.h" +#include "msinterface.h" +// #include "overview.h" +// #include "configdialog.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +MLogin::MLogin() +{ + setWindowTitle(tr("Magic Smoke Login")); + setWindowFlags(windowFlags()|Qt::Window|Qt::Dialog); + QVBoxLayout*vl; + setLayout(vl=new QVBoxLayout); + //create Menu Bar + QMenuBar*mb; + vl->setMenuBar(mb=new QMenuBar); + QMenu*m=mb->addMenu(tr("&File")); + m->addAction(tr("&Exit"),this,SLOT(close())); + m=mb->addMenu(tr("&Configure")); + m->addAction(tr("&Configuration..."),this,SLOT(configwin())); +// mb->addMenu(MApplication::helpMenu()); + + //create central widget + QGridLayout*gl; + vl->addLayout(gl=new QGridLayout,10); + QLabel*lab; + int lctr=0; + gl->addWidget(lab=new QLabel(tr("Profile:")),lctr,0); + lab->setAlignment(Qt::AlignRight); + gl->addWidget(profiles=new QComboBox,lctr,1); + connect(profiles,SIGNAL(currentIndexChanged(int)),this,SLOT(loadProfile())); + gl->addWidget(lab=new QLabel(tr("Username:")),++lctr,0); + lab->setAlignment(Qt::AlignRight); + gl->addWidget(username=new QLineEdit,lctr,1); + gl->addWidget(lab=new QLabel(tr("Password:")),++lctr,0); + lab->setAlignment(Qt::AlignRight); + gl->addWidget(password=new QLineEdit,lctr,1); + password->setEchoMode(QLineEdit::Password); + connect(password,SIGNAL(returnPressed()),this,SLOT(startLogin())); + connect(username,SIGNAL(returnPressed()),password,SLOT(setFocus())); + gl->setRowStretch(++lctr,10); + QHBoxLayout *hl=new QHBoxLayout; + gl->addLayout(hl,++lctr,0,1,2); + QPushButton*p; + hl->addStretch(10); + hl->addWidget(p=new QPushButton(tr("Login")),0); + vl->addLayout(hl=new QHBoxLayout); + resizer=new QSizeGrip(this); + connect(p,SIGNAL(clicked()),this,SLOT(startLogin())); + initProfiles(); + loadProfile(); + + //react to Escape button + QAction *act=new QAction(this); + act->setShortcut(Qt::Key_Escape); + connect(act,SIGNAL(triggered(bool)),this,SLOT(close())); + addAction(act); +} + +void MLogin::initProfiles() +{ + QSettings set; + set.beginGroup("profiles"); + QStringList prf=set.childGroups(); + profiles->clear(); + for(int i=0;iaddItem(set.value(prf[i]+"/name").toString(),prf[i]); + } +} + +void MLogin::loadProfile() +{ + QString key=profiles->itemData(profiles->currentIndex()).toString(); + QSettings set; + set.beginGroup("profiles/"+key); + username->setText(set.value("username").toString()); + username->setPlaceholderText(set.value("usernamehint").toString()); + password->setText(""); + if(username->text().isEmpty()) + username->setFocus(); + else + password->setFocus(); +} + +void MLogin::startLogin() +{ + qDebug()<<"Logging in..."; + //make it impossible for the user to interfere + setEnabled(false); + QProgressDialog pd(this); + pd.setWindowTitle(tr("Login")); + pd.setLabelText(tr("Logging in...")); + pd.setCancelButton(nullptr); + pd.setRange(0,100); + pd.setValue(10); + pd.setVisible(true); + //create request object + MSInterface *mw=new MSInterface(profiles->itemData(profiles->currentIndex()).toString()); + //check server version + if(!mw->checkServer()){ + //no need for messagebox: checkServer displays one if necessary + mw->deleteLater(); + setEnabled(true); + return; + } + pd.setValue(30); + //start login request + if(!mw->login(username->text(),password->text())){ + QMessageBox::warning(this,tr("Warning"),tr("Unable to log in.")); + mw->deleteLater(); + setEnabled(true); + return; + } + pd.setValue(70); + pd.setLabelText(tr("Getting data...")); + //initialize +// mw->initialize(); + pd.setValue(100); + //open window + emit loginSucceeded(); + + //make sure the application exits (only) after everything is cleaned up + qApp->setQuitOnLastWindowClosed(false); + + hide(); + + // make sure relogin works + connect(mw,SIGNAL(needRelogin()),this,SLOT(relogin())); +} + +void MLogin::configwin() +{ +// MConfigDialog cd; +// cd.exec(); +// initProfiles(); +// loadProfile(); +} + +void MLogin::resizeEvent(QResizeEvent* ) +{ + resizer->resize(resizer->sizeHint()); + if (isRightToLeft()) + resizer->move(rect().bottomLeft() -resizer->rect().bottomLeft()); + else + resizer->move(rect().bottomRight() -resizer->rect().bottomRight()); + resizer->raise(); +} + +void MLogin::relogin() +{ + MSInterface*ms=MSInterface::instance(); + if(ms==nullptr)return; + if(ms->relogin()) + emit loginSucceeded(); + else + emit lostSession(); +} diff --git a/sessman/login.h b/sessman/login.h new file mode 100644 index 0000000..1035fa2 --- /dev/null +++ b/sessman/login.h @@ -0,0 +1,52 @@ +// +// C++ Interface: mainwindow +// +// Description: +// +// +// Author: Konrad Rosenbaum , (C) 2007-2011 +// +// Copyright: See README/COPYING.GPL files that come with this distribution +// +// + +#ifndef MAGICSMOKE_LOGIN_H +#define MAGICSMOKE_LOGIN_H + +#include + +class QComboBox; +class QLineEdit; +class QSizeGrip; + +/**login and profile configuration window*/ +class MLogin:public QWidget +{ + Q_OBJECT + public: + MLogin(); + + private: + QString sessionid; + QLineEdit*username,*password; + QComboBox*profiles; + QSizeGrip*resizer; + + protected: + void resizeEvent(QResizeEvent *); + + private slots: + void initProfiles(); + void loadProfile(); + void startLogin(); + void configwin(); + + public slots: + void relogin(); + + signals: + void loginSucceeded(); + void lostSession(); +}; + +#endif diff --git a/sessman/sessman.pro b/sessman/sessman.pro new file mode 100644 index 0000000..6b628ce --- /dev/null +++ b/sessman/sessman.pro @@ -0,0 +1,27 @@ +#Project File for MagicSmoke Session Manager +# (c) Konrad Rosenbaum, 2016 + +#basics +TEMPLATE = app +VERSION = 0.1 +TARGET = magicsmoke-sessman + +include(../basics.pri) +include(../sesscli/scrand.pri) +include(../iface/iface.pri) + +#Localization +TRANSLATIONS = \ + smoke-sm_de.ts \ + smoke-sm_de_SAX.ts \ + smoke-sm_en.ts + +#main source files +SOURCES = sman.cpp login.cpp +HEADERS = sman.h login.h +INCLUDEPATH += . +DEPENDPATH += $$INCLUDEPATH +RESOURCES += icon.qrc + +#make sure the correct Qt DLLs are used +QT += network gui widgets diff --git a/sessman/sman.cpp b/sessman/sman.cpp new file mode 100644 index 0000000..3c77193 --- /dev/null +++ b/sessman/sman.cpp @@ -0,0 +1,281 @@ +// +// C++ Implementation: Session Manager +// +// +// Author: Konrad Rosenbaum , (C) 2016 +// +// Copyright: See README/COPYING.GPL files that come with this distribution +// +// + + +#include +#include +#include +#include +#include +#include +#include + +#include "sman.h" +#include "login.h" +#include "boxwrapper.h" + +#include "../sesscli/scrand.h" + +#include + +MSessionManager::MSessionManager ( QObject* parent ) : QObject ( parent ) +{ + // create socket + mkey=QString::fromLatin1(MagicSmokeRandom::getRandomBytes(32).toHex()); + if(mkey.size()<64){ + qDebug()<<"Error: unable to get a secure name for my server socket!"; + qApp->exit(1); + return; + } + QLocalServer *serv=new QLocalServer(this); + serv->setSocketOptions(QLocalServer::UserAccessOption); + if(!serv->listen("magicsmoke-sms-"+mkey)){ + qDebug()<<"Error: unable to open server socket -"<errorString(); + serv->deleteLater(); + return; + } + mserver=serv; + connect(mserver,SIGNAL(newConnection()),this,SLOT(newConnection())); + qDebug()<<"Server socket ready."; + + // check for slave socket + for(QString arg:qApp->arguments()){ + if(arg.startsWith("-slave:")){ + QString sk=arg.mid(7).trimmed(); + qDebug()<<"Attempting to communicate with slave"<setToolTip(tr("MagicSmoke Session Manager, waiting for login...")); + micon->show(); +} + +MSessionManager::~MSessionManager() +{ + if(mserver){ + mserver->close(); + mserver->deleteLater(); + mserver=nullptr; + } + MSInterface*ms=MSInterface::instance(); + if(ms){ + ms->logout(); + delete ms; + } + qApp->quit(); +} + + +bool MSessionManager::hasSession() const +{ + MSInterface*ms=MSInterface::instance(); + if(ms) + return !ms->sessionId().isEmpty(); + else + return false; +} + +QString MSessionManager::sessionId() const +{ + MSInterface*ms=MSInterface::instance(); + if(ms) + return ms->sessionId(); + else + return QString(); +} + +QString MSessionManager::username() const +{ + MSInterface*ms=MSInterface::instance(); + if(ms) + return ms->currentUser(); + else + return QString(); +} + +QString MSessionManager::profile() const +{ + MSInterface*ms=MSInterface::instance(); + if(ms) + return ms->profileId(); + else + return QString(); +} + +QString MSessionManager::profileName() const +{ + MSInterface*ms=MSInterface::instance(); + if(ms) + return ms->profileName(); + else + return QString(); +} + +bool MSessionManager::isActive() const +{ + return mserver!=nullptr && mserver->isListening(); +} + +void MSessionManager::newConnection() +{ + while(mserver->hasPendingConnections()){ + QLocalSocket*s=mserver->nextPendingConnection(); + if(!s)return; + s->setParent(this); + connect(s,SIGNAL(readyRead()),this,SLOT(readyRead())); + connect(s,SIGNAL(disconnected()),this,SLOT(socketClosed())); + connect(s,SIGNAL(error(QLocalSocket::LocalSocketError)),this,SLOT(socketClosed())); + connect(s,SIGNAL(destroyed(QObject*)),this,SLOT(socketLost(QObject*))); + mconnections.append(s); + } +} + +void MSessionManager::readyRead() +{ + //find socket + QLocalSocket*s=qobject_cast(sender()); + if(s==nullptr){ + qDebug()<<"Warning: readyRead called without socket caller."; + return; + } + if(!mconnections.contains(s)){ + qDebug()<<"Warning: readyRead called from unknown socket."; + return; + } + //get command + while(s->canReadLine()){ + const QString line=QString::fromUtf8(s->readLine().trimmed()); + if(line.isEmpty())continue; + const int pos=line.indexOf(' '); + const QString cmd=pos>0?line.left(pos):line; + const QString par=pos>0?line.mid(pos+1):QString(); + qDebug()<<"Received session client request"<deleteLater(); + return; + } + sendSessionInfo(s); + }else if(cmd=="getmenu") + sendMenu(s); + else if(cmd=="exec") + execCmd(par); + else + qDebug()<<"Warning: unknown client command"<(o); + if(s==nullptr){ + qDebug()<<"Warning: socketLost called without socket caller."; + return; + } + if(!mconnections.contains(s)){ + qDebug()<<"Warning: socketLost called from unknown socket."; + return; + } + //remove from list + mconnections.removeAll(s); + //was this the last? + if(mconnections.isEmpty()){ + qDebug()<<"This was the last connection. Quitting now."; + deleteLater(); + } +} + +void MSessionManager::sendMenu(QLocalSocket*s) +{ + s->write("newmenu\n"); + //TODO: send "menu command entry-text" for each entry + s->write("endmenu\n"); +} + +void MSessionManager::execCmd ( QString cmd) +{ + qDebug()<<"I wish I knew how to execute"<setProcessChannelMode(QProcess::ForwardedChannels); + p->start(qApp->applicationDirPath()+"/magicsmoke",QStringList()<<"-sms:"+mkey); +} + +void MSessionManager::loginSucceeded() +{ + micon->setToolTip(tr("MagicSmoke Session Manager, logged in as %1 at %2.").arg(username()).arg(profileName())); + if(mconnections.count()>0) + sendNewSessionId(); + else + execCmd("msmoke"); +} + +void MSessionManager::sendNewSessionId() +{ + qDebug()<<"Sending session data..."; + for(QLocalSocket*s:mconnections) + sendSessionInfo(s); +} + +void MSessionManager::sendSessionInfo ( QLocalSocket* s) +{ + if(sessionId().isEmpty())return; + s->write(QString("profile "+profile()+"\n").toUtf8()); + s->write(QString("user "+username()+"\n").toUtf8()); + s->write(QString("sid "+sessionId()+"\n").toLatin1()); + s->flush(); +} + +void MSessionManager::loginLost() +{ + for(QLocalSocket*s:mconnections) + s->write("closed\n"); +} + + +int main(int ac,char**av) +{ + QApplication app(ac,av); + //locate settings + app.setOrganizationName("MagicSmoke"); + app.setApplicationName("MagicSmoke2"); + + //set icon + app.setWindowIcon(QIcon(":/icon.png")); + + MSInterface::setAppDataDir("$BASE/.magicSmoke2"); + + MBoxWrapper::setWarning([](QString title,QString text){QMessageBox::warning(nullptr,title,text);}); + + + MSessionManager *sm=new MSessionManager(&app); + MLogin lw; + lw.connect(&lw,SIGNAL(loginSucceeded()),sm,SLOT(loginSucceeded())); + lw.connect(&lw,SIGNAL(lostSession()),sm,SLOT(loginLost())); + lw.show(); + + return app.exec(); +} diff --git a/sessman/sman.h b/sessman/sman.h new file mode 100644 index 0000000..a0cafab --- /dev/null +++ b/sessman/sman.h @@ -0,0 +1,59 @@ +// +// C++ Interface: Session Client +// +// Description: Session Client Class - connects to a session manager (or even starts one) +// and enables exchange of session data. +// +// +// Author: Konrad Rosenbaum , (C) 2016 +// +// Copyright: See README/COPYING.GPL files that come with this distribution +// +// + +#ifndef MAGICSMOKE_SC_SCLI_H +#define MAGICSMOKE_SC_SCLI_H + +#include +#include + +class QLocalServer; +class QLocalSocket; +class QSystemTrayIcon; + +class MSessionManager:public QObject +{ + Q_OBJECT +public: + explicit MSessionManager ( QObject* parent = 0 ); + virtual ~MSessionManager(); + + virtual bool isActive()const; + virtual bool hasSession()const; + virtual QString sessionId()const; + virtual QString username()const; + virtual QString profile()const; + virtual QString profileName()const; + +private slots: + void newConnection(); + void readyRead(); + void socketClosed(); + void socketLost(QObject*); + + void loginSucceeded(); + void sendNewSessionId(); + void loginLost(); +private: + QLocalServer*mserver=nullptr; + QListmconnections; + QString mkey; + QSystemTrayIcon*micon; + + void sendMenu(QLocalSocket*); + void execCmd(QString); + void sendSessionInfo(QLocalSocket*); +}; + + +#endif diff --git a/src/dialogs/dialogs.pri b/src/dialogs/dialogs.pri index 52975ea..ff60fff 100644 --- a/src/dialogs/dialogs.pri +++ b/src/dialogs/dialogs.pri @@ -3,7 +3,6 @@ HEADERS += \ dialogs/eventedit.h \ dialogs/eventsummary.h \ dialogs/orderwin.h \ - dialogs/login.h \ dialogs/shipping.h \ dialogs/customerdlg.h \ dialogs/checkdlg.h \ @@ -21,7 +20,6 @@ SOURCES += \ dialogs/eventedit.cpp \ dialogs/eventsummary.cpp \ dialogs/orderwin.cpp \ - dialogs/login.cpp \ dialogs/shipping.cpp \ dialogs/customerdlg.cpp \ dialogs/checkdlg.cpp \ @@ -35,4 +33,4 @@ SOURCES += \ RESOURCES += dialogs/dialogfiles.qrc -INCLUDEPATH += ./dialogs \ No newline at end of file +INCLUDEPATH += ./dialogs diff --git a/src/dialogs/login.cpp b/src/dialogs/login.cpp deleted file mode 100644 index b72330b..0000000 --- a/src/dialogs/login.cpp +++ /dev/null @@ -1,176 +0,0 @@ -// -// C++ Implementation: mainwindow -// -// Description: -// -// -// Author: Konrad Rosenbaum , (C) 2007-2011 -// -// Copyright: See README/COPYING.GPL files that come with this distribution -// -// - -#include "main.h" -#include "login.h" -#include "msinterface.h" -#include "overview.h" -#include "configdialog.h" - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -MLogin::MLogin() -{ - setWindowTitle(tr("Magic Smoke Login")); - setWindowFlags(windowFlags()|Qt::Window|Qt::Dialog); - QVBoxLayout*vl; - setLayout(vl=new QVBoxLayout); - //create Menu Bar - QMenuBar*mb; - vl->setMenuBar(mb=new QMenuBar); - QMenu*m=mb->addMenu(tr("&File")); - m->addAction(tr("&Exit"),this,SLOT(close())); - m=mb->addMenu(tr("&Configure")); - m->addAction(tr("&Configuration..."),this,SLOT(configwin())); - mb->addMenu(MApplication::helpMenu()); - - //create central widget - QGridLayout*gl; - vl->addLayout(gl=new QGridLayout,10); - QLabel*lab; - int lctr=0; - gl->addWidget(lab=new QLabel(tr("Profile:")),lctr,0); - lab->setAlignment(Qt::AlignRight); - gl->addWidget(profiles=new QComboBox,lctr,1); - connect(profiles,SIGNAL(currentIndexChanged(int)),this,SLOT(loadProfile())); - gl->addWidget(lab=new QLabel(tr("Username:")),++lctr,0); - lab->setAlignment(Qt::AlignRight); - gl->addWidget(username=new QLineEdit,lctr,1); - gl->addWidget(lab=new QLabel(tr("Password:")),++lctr,0); - lab->setAlignment(Qt::AlignRight); - gl->addWidget(password=new QLineEdit,lctr,1); - password->setEchoMode(QLineEdit::Password); - connect(password,SIGNAL(returnPressed()),this,SLOT(startLogin())); - connect(username,SIGNAL(returnPressed()),password,SLOT(setFocus())); - gl->setRowStretch(++lctr,10); - QHBoxLayout *hl=new QHBoxLayout; - gl->addLayout(hl,++lctr,0,1,2); - QPushButton*p; - hl->addStretch(10); - hl->addWidget(p=new QPushButton(tr("Login")),0); - vl->addLayout(hl=new QHBoxLayout); - resizer=new QSizeGrip(this); - connect(p,SIGNAL(clicked()),this,SLOT(startLogin())); - initProfiles(); - loadProfile(); - - //react to Escape button - QAction *act=new QAction(this); - act->setShortcut(Qt::Key_Escape); - connect(act,SIGNAL(triggered(bool)),this,SLOT(close())); - addAction(act); -} - -void MLogin::initProfiles() -{ - QSettings set; - set.beginGroup("profiles"); - QStringList prf=set.childGroups(); - profiles->clear(); - for(int i=0;iaddItem(set.value(prf[i]+"/name").toString(),prf[i]); - } -} - -void MLogin::loadProfile() -{ - QString key=profiles->itemData(profiles->currentIndex()).toString(); - QSettings set; - set.beginGroup("profiles/"+key); - username->setText(set.value("username").toString()); - username->setPlaceholderText(set.value("usernamehint").toString()); - password->setText(""); - if(username->text().isEmpty()) - username->setFocus(); - else - password->setFocus(); -} - -void MLogin::startLogin() -{ - //make it impossible for the user to interfere - setEnabled(false); - QProgressDialog pd(this); - pd.setWindowTitle(tr("Login")); - pd.setLabelText(tr("Logging in...")); - pd.setCancelButton(nullptr); - pd.setRange(-30,100); - pd.setValue(-30); - pd.setVisible(true); - //create request object - MSInterface *mw=new MSInterface(profiles->itemData(profiles->currentIndex()).toString()); - //check server version - if(!mw->checkServer()){ - //no need for messagebox: checkServer displays one if necessary - mw->deleteLater(); - setEnabled(true); - return; - } - pd.setValue(-20); - //start login request - if(!mw->login(username->text(),password->text())){ - QMessageBox::warning(this,tr("Warning"),tr("Unable to log in.")); - mw->deleteLater(); - setEnabled(true); - return; - } - pd.setValue(-10); - pd.setLabelText(tr("Getting data...")); - //initialize - mw->initialize(); - pd.setValue(0); - //open window - MOverview *mo=new MOverview(profiles->itemData(profiles->currentIndex()).toString(), - [&pd](int val,QString txt){ - pd.setValue(val); - pd.setLabelText(txt); - } - ); - mo->showRestored(); - - //make sure the application exits (only) after everything is cleaned up - qApp->setQuitOnLastWindowClosed(false); - connect(mw,SIGNAL(destroyed(QObject*)),qApp,SLOT(quit())); - - hide(); -} - -void MLogin::configwin() -{ - MConfigDialog cd; - cd.exec(); - initProfiles(); - loadProfile(); -} - -void MLogin::resizeEvent(QResizeEvent* ) -{ - resizer->resize(resizer->sizeHint()); - if (isRightToLeft()) - resizer->move(rect().bottomLeft() -resizer->rect().bottomLeft()); - else - resizer->move(rect().bottomRight() -resizer->rect().bottomRight()); - resizer->raise(); -} diff --git a/src/dialogs/login.h b/src/dialogs/login.h deleted file mode 100644 index 04d9d66..0000000 --- a/src/dialogs/login.h +++ /dev/null @@ -1,45 +0,0 @@ -// -// C++ Interface: mainwindow -// -// Description: -// -// -// Author: Konrad Rosenbaum , (C) 2007-2011 -// -// Copyright: See README/COPYING.GPL files that come with this distribution -// -// - -#ifndef MAGICSMOKE_LOGIN_H -#define MAGICSMOKE_LOGIN_H - -#include - -class QComboBox; -class QLineEdit; -class QSizeGrip; - -/**login and profile configuration window*/ -class MLogin:public QWidget -{ - Q_OBJECT - public: - MLogin(); - - private: - QString sessionid; - QLineEdit*username,*password; - QComboBox*profiles; - QSizeGrip*resizer; - - protected: - void resizeEvent(QResizeEvent *); - - private slots: - void initProfiles(); - void loadProfile(); - void startLogin(); - void configwin(); -}; - -#endif diff --git a/src/libs.pri b/src/libs.pri index 8e1aded..09bd6b8 100644 --- a/src/libs.pri +++ b/src/libs.pri @@ -9,6 +9,9 @@ include ($$PWD/../iface/iface.pri) # Aurora Updater library include($$PWD/../taurus/aurora.pri) +# Session Client library +include($$PWD/../sesscli/sesscli.pri) + #make sure the correct Qt DLLs are used QT += xml network script scripttools QT += widgets printsupport diff --git a/src/main.cpp b/src/main.cpp index f92e056..eb272f8 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -38,12 +38,15 @@ #include "hmac.h" #include "keygen.h" #include "main.h" -#include "login.h" +// #include "login.h" #include "msinterface.h" #include #include "misc.h" #include #include "boxwrapper.h" +#include "overview.h" + +#include QString choseLanguage(bool warn) { @@ -474,10 +477,22 @@ int MApplication::realmain(int argc,char**argv) //init app.initialize(); + MSessionClient sc; + sc.connect(&sc,SIGNAL(sessionLost()),&app,SLOT(quit())); + sc.connect(&sc,SIGNAL(managerLost()),&app,SLOT(quit())); + if(sc.waitForSessionAvailable()){ + MSInterface*ms=new MSInterface(sc.currentProfileId()); + ms->loginSession(sc.currentUsername(), sc.currentSessionId()); + MOverview *mo=new MOverview(sc.currentProfileId()); + mo->showRestored(); + }else{ + qDebug()<<"Unable to get session. Giving up."; + return 1; + } //open main window - MLogin mmw; - mmw.show(); + //MLogin mmw; + //mmw.show(); return app.exec(); } diff --git a/src/mwin/overview.cpp b/src/mwin/overview.cpp index 3fa34c1..237b672 100644 --- a/src/mwin/overview.cpp +++ b/src/mwin/overview.cpp @@ -51,6 +51,7 @@ #include #include #include +#include #include #include #include @@ -66,7 +67,7 @@ #include "MTBackup" #include "MTGetAllUsers" -MOverview::MOverview(QString pk,std::functioninitUpdate) +MOverview::MOverview(QString pk) :MTabWin(pk),entrancetab(nullptr) { rtimer.setInterval(QSettings().value("profiles/"+pk+"/refresh",300).toInt()*1000); @@ -139,19 +140,26 @@ MOverview::MOverview(QString pk,std::functioninitUpdate) ->setEnabled(req->hasRight(req->RBackup)); m2=m->addMenu(tr("&Configuration")); - m2->addAction(tr("&Change Language..."),this,SLOT(changeLang())); + m2->addAction(tr("&Change Language..."),this,SLOT(changeLang())); m2->addAction(tr("&Auto-Refresh settings..."),this,SLOT(setRefresh())); m2->addAction(tr("&Server Access settings..."),this,SLOT(webSettings())); m2->addAction(tr("&Display settings..."),this,SLOT(displaySettings())); m2->addAction(tr("&Label Printing settings..."),this,SLOT(labelSettings())); - m2->addAction(tr("&OpenOffice settings..."),this,SLOT(openOfficeSettings())); - m2->addAction(tr("&Barcode Scanner settings..."),this,SLOT(barcodeSettings())); + m2->addAction(tr("&OpenOffice settings..."),this,SLOT(openOfficeSettings())); + m2->addAction(tr("&Barcode Scanner settings..."),this,SLOT(barcodeSettings())); //make sure webrequest knows its settings webSettings(false); //Event tab - if(initUpdate)initUpdate(10,tr("Getting events...")); + QProgressDialog pd(this); + pd.setWindowTitle(tr("Login")); + pd.setLabelText(tr("Getting data...")); + pd.setCancelButton(nullptr); + pd.setRange(0,100); + pd.setValue(10); + pd.setVisible(true); + pd.setValue(10);pd.setLabelText(tr("Getting events...")); eventtab=new MEventsTab(pk); addTab(eventtab,tr("Events"),eventtab->menu()); @@ -167,13 +175,13 @@ MOverview::MOverview(QString pk,std::functioninitUpdate) connect(carttab,SIGNAL(requestFocus()), this,SLOT(switchToCartTab())); //Order List Tab - if(initUpdate)initUpdate(40,tr("Getting Orders...")); + pd.setValue(40);pd.setLabelText(tr("Getting Orders...")); ordertab=new MOrdersTab(pk); addTab(ordertab, tr("Order List"), ordertab->menu()); connect(ordertab,SIGNAL(selectEventIds(QList&)), eventtab,SLOT(selectEventIds(QList&)), Qt::DirectConnection); //Entrance Control Tab - if(initUpdate)initUpdate(70,tr("Getting Entrance Data...")); + pd.setValue(70);pd.setLabelText(tr("Getting Entrance Data...")); entrancetab=new MEntranceTab(pk); addTab(entrancetab,tr("Entrance"),entrancetab->menu()); connect(entrancetab,SIGNAL(askForFocus(QWidget*)),this,SLOT(setCurrentTab(QWidget*))); diff --git a/src/mwin/overview.h b/src/mwin/overview.h index f845e2b..512504b 100644 --- a/src/mwin/overview.h +++ b/src/mwin/overview.h @@ -21,8 +21,6 @@ #include "tabwin.h" -#include - class QAction; class QCheckBox; class QComboBox; @@ -49,7 +47,7 @@ class MOverview:public MTabWin Q_OBJECT public: /**construct the window with web-request/session handler and QSettings-key for current profile*/ - MOverview(QString,std::functioninitUpdate=nullptr); + MOverview(QString); ~MOverview(); protected: /**handle closing the window: close the session too*/ -- 1.7.2.5