conversion to session manager: stage 1 session manager as external process
authorKonrad Rosenbaum <konrad@silmor.de>
Sat, 9 Jul 2016 20:40:23 +0000 (22:40 +0200)
committerKonrad Rosenbaum <konrad@silmor.de>
Sat, 9 Jul 2016 20:40:23 +0000 (22:40 +0200)
21 files changed:
Makefile
basics.pri
iface/msinterface.cpp
iface/msinterface.h
sesscli/scli.cpp [new file with mode: 0644]
sesscli/scli.h [new file with mode: 0644]
sesscli/scrand.h [new file with mode: 0644]
sesscli/scrand.pri [new file with mode: 0644]
sesscli/sesscli.pri [new file with mode: 0644]
sesscli/sesscli.pro [new file with mode: 0644]
sessman/icon.qrc [new file with mode: 0644]
sessman/login.cpp [moved from src/dialogs/login.cpp with 85% similarity]
sessman/login.h [moved from src/dialogs/login.h with 88% similarity]
sessman/sessman.pro [new file with mode: 0644]
sessman/sman.cpp [new file with mode: 0644]
sessman/sman.h [new file with mode: 0644]
src/dialogs/dialogs.pri
src/libs.pri
src/main.cpp
src/mwin/overview.cpp
src/mwin/overview.h

index 5aee58f..784629c 100644 (file)
--- 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
 
index b014e21..ea115eb 100644 (file)
@@ -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
index 5a3fd1e..ece3e4d 100644 (file)
@@ -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"<<userflags;
        userroles=mrl.getrole();
 //     for(int i=0;i<userroles.size();i++)qDebug("have role %s",userroles[i].toLatin1().data());
-       
+
        return true;
 }
 
@@ -296,4 +304,10 @@ QUrl MSInterface::parentUrl() const
        QString path=parent.path();
        if(path==QString() || path.endsWith('/'))return parent;
        parent.setPath(path.left(path.lastIndexOf('/')+1));
+       return parent;
+}
+
+QString MSInterface::profileName() const
+{
+       return QSettings().value("profiles/"+profileid+"/name").toString();
 }
index 7ce3475..84c2332 100644 (file)
@@ -89,6 +89,9 @@ class MSIFACE_EXPORT MSInterface:public MInterface
                
                /**returns the profile ID of this session*/
                QString profileId()const{return profileid;}
+
+               ///returns the human readable name of the profile
+               QString profileName()const;
                
                ///return all rights of the current user
                QList<Right> 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 QList<Right>userrights;
diff --git a/sesscli/scli.cpp b/sesscli/scli.cpp
new file mode 100644 (file)
index 0000000..5bf5751
--- /dev/null
@@ -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 <konrad@silmor.de>, (C) 2016
+//
+// Copyright: See README/COPYING.GPL files that come with this distribution
+//
+//
+
+#include "scli.h"
+#include "scrand.h"
+
+#include <QCoreApplication>
+#include <QEventLoop>
+#include <QLocalServer>
+#include <QProcess>
+#include <QRegExp>
+#include <QStringList>
+#include <QTimer>
+
+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."<<sms;
+                       return;
+               }
+               qDebug()<<"Got session socket ID from Session Manager.";
+       }
+       //open socket
+       QLocalSocket *s=new QLocalSocket(this);
+       s->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<QPair<QString,QString>>();
+       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"<<cmd<<"with param"<<(cmd!="sid"?par:"(censored)");
+               if(cmd=="sid"){
+                       msid=par;
+                       qDebug()<<"Received new Session ID.";
+                       emit sessionIdChanged(msid);
+               }else if(cmd=="profile"){
+                       qDebug()<<"Using session profile"<<par;
+                       mprofile=par;
+               }else if(cmd=="user"){
+                       qDebug()<<"Username"<<par;
+                       muser=par;
+               }else if(cmd=="newmenu"){
+                       mmenu.clear();
+               }else if(cmd=="menu"){
+               }else if(cmd=="endmenu"){
+                       qDebug()<<"New Menu received from Session Manager!";
+                       emit menuChanged();
+               }else if(cmd=="closed"){
+                       msid.clear();
+                       qDebug()<<"Warning: Session lost!";
+                       emit sessionLost();
+               }else if(cmd=="quit"){
+                       socketLost();
+               }else
+                       qDebug()<<"Warning: unknown session manager signal"<<cmd<<"encountered. Ignoring it.";
+       }
+}
+
+void MSessionClient::execServerCommand ( QString cmd)
+{
+       if(msocket && msocket->isOpen())
+               msocket->write(QString("exec "+cmd+"\n").toUtf8());
+}
+
diff --git a/sesscli/scli.h b/sesscli/scli.h
new file mode 100644 (file)
index 0000000..5a874ae
--- /dev/null
@@ -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 <konrad@silmor.de>, (C) 2016
+//
+// Copyright: See README/COPYING.GPL files that come with this distribution
+//
+//
+
+#ifndef MAGICSMOKE_SC_SCLI_H
+#define MAGICSMOKE_SC_SCLI_H
+
+#include <QObject>
+#include <QList>
+#include <QPair>
+#include <QLocalSocket>
+
+#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<QPair<QString,QString>> 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<QPair<QString,QString>> mmenu;
+};
+
+
+#endif
diff --git a/sesscli/scrand.h b/sesscli/scrand.h
new file mode 100644 (file)
index 0000000..873c074
--- /dev/null
@@ -0,0 +1,63 @@
+//
+// C++ Interface: Random Number Retriever
+//
+//
+// Author: Konrad Rosenbaum <konrad@silmor.de>, (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 <windows.h>
+#pragma comment(lib, "advapi32.lib")
+#else
+#include <QFile>
+#endif
+
+#include <QByteArray>
+
+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 (file)
index 0000000..9d95f27
--- /dev/null
@@ -0,0 +1,5 @@
+# Random Stuff
+
+win32 {
+#LIBS += -ladvapi32
+}
diff --git a/sesscli/sesscli.pri b/sesscli/sesscli.pri
new file mode 100644 (file)
index 0000000..fe0ec37
--- /dev/null
@@ -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 (file)
index 0000000..9f7e5d4
--- /dev/null
@@ -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 (file)
index 0000000..9be79e1
--- /dev/null
@@ -0,0 +1,9 @@
+<!DOCTYPE RCC>
+<!-- Copyright: (c) Konrad Rosenbaum, 2007-2011
+See COPYING.GPL for details. -->
+<RCC version="1.0">
+    <qresource>
+        <!-- main app icon -->
+        <file alias="icon.png">../src/images/icon.png</file>
+    </qresource>
+</RCC>
similarity index 85%
rename from src/dialogs/login.cpp
rename to sessman/login.cpp
index b72330b..1566497 100644 (file)
 //
 //
 
-#include "main.h"
+// #include "main.h"
 #include "login.h"
 #include "msinterface.h"
-#include "overview.h"
-#include "configdialog.h"
+// #include "overview.h"
+// #include "configdialog.h"
 
 #include <QApplication>
 #include <QByteArray>
 #include <QComboBox>
+#include <QDebug>
 #include <QGridLayout>
 #include <QHBoxLayout>
 #include <QLabel>
@@ -44,7 +45,7 @@ MLogin::MLogin()
        m->addAction(tr("&Exit"),this,SLOT(close()));
        m=mb->addMenu(tr("&Configure"));
        m->addAction(tr("&Configuration..."),this,SLOT(configwin()));
-       mb->addMenu(MApplication::helpMenu());
+//     mb->addMenu(MApplication::helpMenu());
        
        //create central widget
        QGridLayout*gl;
@@ -110,14 +111,15 @@ void MLogin::loadProfile()
 
 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(-30,100);
-        pd.setValue(-30);
+        pd.setRange(0,100);
+        pd.setValue(10);
         pd.setVisible(true);
        //create request object
        MSInterface *mw=new MSInterface(profiles->itemData(profiles->currentIndex()).toString());
@@ -128,7 +130,7 @@ void MLogin::startLogin()
                setEnabled(true);
                return;
        }
-       pd.setValue(-20);
+       pd.setValue(30);
        //start login request
        if(!mw->login(username->text(),password->text())){
                QMessageBox::warning(this,tr("Warning"),tr("Unable to log in."));
@@ -136,33 +138,29 @@ void MLogin::startLogin()
                setEnabled(true);
                return;
        }
-       pd.setValue(-10);
+       pd.setValue(70);
         pd.setLabelText(tr("Getting data..."));
        //initialize
-       mw->initialize();
-        pd.setValue(0);
+//     mw->initialize();
+        pd.setValue(100);
        //open window
-       MOverview *mo=new MOverview(profiles->itemData(profiles->currentIndex()).toString(), 
-                                    [&pd](int val,QString txt){
-                                            pd.setValue(val);
-                                            pd.setLabelText(txt);
-                                    }
-                                );
-       mo->showRestored();
+       emit loginSucceeded();
        
        //make sure the application exits (only) after everything is cleaned up
        qApp->setQuitOnLastWindowClosed(false);
-       connect(mw,SIGNAL(destroyed(QObject*)),qApp,SLOT(quit()));
        
        hide();
+
+       // make sure relogin works
+       connect(mw,SIGNAL(needRelogin()),this,SLOT(relogin()));
 }
 
 void MLogin::configwin()
 {
-       MConfigDialog cd;
-       cd.exec();
-       initProfiles();
-       loadProfile();
+//     MConfigDialog cd;
+//     cd.exec();
+//     initProfiles();
+//     loadProfile();
 }
 
 void MLogin::resizeEvent(QResizeEvent* )
@@ -174,3 +172,13 @@ void MLogin::resizeEvent(QResizeEvent* )
                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();
+}
similarity index 88%
rename from src/dialogs/login.h
rename to sessman/login.h
index 04d9d66..1035fa2 100644 (file)
@@ -40,6 +40,13 @@ class MLogin:public QWidget
                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 (file)
index 0000000..6b628ce
--- /dev/null
@@ -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 (file)
index 0000000..3c77193
--- /dev/null
@@ -0,0 +1,281 @@
+//
+// C++ Implementation: Session Manager
+//
+//
+// Author: Konrad Rosenbaum <konrad@silmor.de>, (C) 2016
+//
+// Copyright: See README/COPYING.GPL files that come with this distribution
+//
+//
+
+
+#include <QApplication>
+#include <QDebug>
+#include <QLocalServer>
+#include <QLocalSocket>
+#include <QMessageBox>
+#include <QSystemTrayIcon>
+#include <QProcess>
+
+#include "sman.h"
+#include "login.h"
+#include "boxwrapper.h"
+
+#include "../sesscli/scrand.h"
+
+#include <msinterface.h>
+
+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 -"<<serv->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"<<sk;
+                       QLocalSocket ls;
+                       ls.connectToServer("magicsmoke-smr-"+sk);
+                       if(!ls.waitForConnected(5000)){
+                               qDebug()<<"Unable to connect to slave.";
+                               continue;
+                       }
+                       ls.write(mkey.toLatin1()+"\n");
+                       ls.waitForBytesWritten(1000);
+                       ls.close();
+               }
+       }
+
+       //create systray icon
+       micon=new QSystemTrayIcon(QIcon(":/icon.png"),this);
+       micon->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<QLocalSocket*>(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"<<cmd<<"with param"<<par;
+               if(cmd=="clientinit"){
+                       if(par!="magicsmoke"){
+                               qDebug()<<"Warning: clientinit from non magicsmoke! Very strange.";
+                               s->deleteLater();
+                               return;
+                       }
+                       sendSessionInfo(s);
+               }else if(cmd=="getmenu")
+                       sendMenu(s);
+               else if(cmd=="exec")
+                       execCmd(par);
+               else
+                       qDebug()<<"Warning: unknown client command"<<cmd;
+       }
+}
+
+void MSessionManager::socketClosed()
+{
+       socketLost(sender());
+}
+
+void MSessionManager::socketLost ( QObject* o)
+{
+       QLocalSocket*s=qobject_cast<QLocalSocket*>(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"<<cmd;
+       QProcess *p=new QProcess;
+       p->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 (file)
index 0000000..a0cafab
--- /dev/null
@@ -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 <konrad@silmor.de>, (C) 2016
+//
+// Copyright: See README/COPYING.GPL files that come with this distribution
+//
+//
+
+#ifndef MAGICSMOKE_SC_SCLI_H
+#define MAGICSMOKE_SC_SCLI_H
+
+#include <QObject>
+#include <QList>
+
+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;
+       QList<QLocalSocket*>mconnections;
+       QString mkey;
+       QSystemTrayIcon*micon;
+
+       void sendMenu(QLocalSocket*);
+       void execCmd(QString);
+       void sendSessionInfo(QLocalSocket*);
+};
+
+
+#endif
index 52975ea..ff60fff 100644 (file)
@@ -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
index 8e1aded..09bd6b8 100644 (file)
@@ -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
index f92e056..eb272f8 100644 (file)
 #include "hmac.h"
 #include "keygen.h"
 #include "main.h"
-#include "login.h"
+// #include "login.h"
 #include "msinterface.h"
 #include <ELAM/Engine>
 #include "misc.h"
 #include <Aurora>
 #include "boxwrapper.h"
+#include "overview.h"
+
+#include <scli.h>
 
 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();
 }
index 3fa34c1..237b672 100644 (file)
@@ -51,6 +51,7 @@
 #include <QMenu>
 #include <QMenuBar>
 #include <QMessageBox>
+#include <QProgressDialog>
 #include <QPushButton>
 #include <QSettings>
 #include <QSpinBox>
@@ -66,7 +67,7 @@
 #include "MTBackup"
 #include "MTGetAllUsers"
 
-MOverview::MOverview(QString pk,std::function<void(int,QString)>initUpdate)
+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::function<void(int,QString)>initUpdate)
         ->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::function<void(int,QString)>initUpdate)
        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<int>&)), eventtab,SLOT(selectEventIds(QList<int>&)), 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*)));
index f845e2b..512504b 100644 (file)
@@ -21,8 +21,6 @@
 
 #include "tabwin.h"
 
-#include <functional>
-
 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::function<void(int,QString)>initUpdate=nullptr);
+               MOverview(QString);
                ~MOverview();
        protected:
                /**handle closing the window: close the session too*/