#include "client.h"
+#include "scli.h"
+
#include <QFormLayout>
#include <QBoxLayout>
#include <QCheckBox>
#include <QPushButton>
#include <QComboBox>
+#include <QSpinBox>
+#include <QLineEdit>
+#include <QSettings>
#define GROUP "PrintAtHome/Client"
#define TIMER GROUP "/timermin"
MPClientConfig::MPClientConfig(QWidget* parent)
+:QDialog(parent)
{
+ setWindowTitle(tr("Print@Home Client Configuration"));
QVBoxLayout*vl;
setLayout(vl=new QVBoxLayout);
QFormLayout*fl;
vl->addLayout(fl=new QFormLayout);
+ //check Interval
fl->addRow(tr("Check Interval in Minutes:"),mtimer=new QSpinBox);
mtimer->setRange(1,10080);
mtimer->setValue(timerInMinutes());
+ //profile
fl->addRow("",msetprofile=new QCheckBox(tr("Preselect Profile")));
fl->addRow(tr("Profile:"),mprofile=new QComboBox);
connect(msetprofile,SIGNAL(clicked(bool)),mprofile,SLOT(setEnabled(bool)));
msetprofile->setChecked(preselectProfile());
- //TODO: get profile names
+ //get profile names
+ auto prfs=MSessionClient::instance()->profiles();
+ const QString cprf=preselectedProfileId();
+ int cprfn=-1;
+ for(auto prf:prfs){
+ if(prf.first==cprf)cprfn=mprofile->count();
+ mprofile->addItem(prf.second,prf.first);
+ }
+ mprofile->setCurrentIndex(cprfn);
+ //user
+ fl->addRow("",msetuser=new QCheckBox(tr("Set User Name")));
+ fl->addRow(tr("User Name:"),musername=new QLineEdit);
+ musername->setText(preselectedUserName());
+ connect(msetuser,SIGNAL(clicked(bool)),musername,SLOT(setEnabled(bool)));
+ msetuser->setChecked(preselectUser());
+ //password
+ fl->addRow("",mautologin=new QCheckBox(tr("Auto-Login")));
+ fl->addRow(tr("Password:"),mpassword=new QLineEdit);
+ connect(mautologin,SIGNAL(clicked(bool)),mpassword,SLOT(setEnabled(bool)));
+ mautologin->setChecked(autoLogin());
+ mpassword->setEchoMode(QLineEdit::Password);
+ mpassword->setText(password());
+
+ vl->addSpacing(15);
+ QHBoxLayout*hl;
+ vl->addLayout(hl=new QHBoxLayout);
+ hl->addStretch(1);
+ QPushButton*p;
+ hl->addWidget(p=new QPushButton(tr("&OK")));
+ connect(p,SIGNAL(clicked()),this,SLOT(save()),Qt::DirectConnection);
+ connect(p,SIGNAL(clicked()),this,SLOT(accept()),Qt::QueuedConnection);
+ hl->addWidget(p=new QPushButton(tr("&Cancel")));
+ connect(p,SIGNAL(clicked()),this,SLOT(reject()));
}
+//note: this is NOT an encryption algorithm - it merely masks against accidental reading, it is trivial to reverse
+static inline QByteArray maskPwd(QString pwd)
+{
+ QByteArray r=pwd.toUtf8();
+ int m=0x58;
+ for(int i=0;i<r.size();i++){
+ r[i]=r[i]^(char)m;
+ m++;
+ }
+ return r.toBase64();
+}
+
+//note 2: this is how trivial it is...
+// maybe we should use TRIPLE-rot13? ;-)
+static inline QString unmaskPwd(QByteArray ms)
+{
+ ms=QByteArray::fromBase64(ms);
+ int m=0x58;
+ for(int i=0;i<ms.size();i++){
+ ms[i]=ms[i]^(char)m;
+ m++;
+ }
+ return QString::fromUtf8(ms);
+}
+
void MPClientConfig::save()
{
+ QSettings set;
+ set.setValue(SETPROFILE,msetprofile->isChecked());
+ set.setValue(TIMER,mtimer->value());
+ set.setValue(PROFILE,mprofile->currentData());
+ set.setValue(SETUSER,msetuser->isChecked());
+ set.setValue(USERNAME,musername->text());
+ set.setValue(AUTOLOGIN,mautologin->isChecked());
+ set.setValue(PASSWD,maskPwd(mpassword->text()));
+}
+
+bool MPClientConfig::preselectProfile()
+{
+ return QSettings().value(SETPROFILE,false).toBool();
+}
+int MPClientConfig::timerInMinutes()
+{
+ return QSettings().value(TIMER,60).toInt();
+}
+
+QString MPClientConfig::preselectedProfileId()
+{
+ return QSettings().value(PROFILE).toString();
+}
+
+bool MPClientConfig::preselectUser()
+{
+ return QSettings().value(SETUSER,false).toBool();
+}
+
+QString MPClientConfig::preselectedUserName()
+{
+ return QSettings().value(USERNAME).toString();
+}
+
+bool MPClientConfig::autoLogin()
+{
+ return QSettings().value(AUTOLOGIN,false).toBool();
+}
+
+QString MPClientConfig::password()
+{
+ return unmaskPwd(QSettings().value(PASSWD).toByteArray());
+}
+
+void MPClientConfig::performAutoLogin(MSessionClient&sc)
+{
+ sc.waitForReady();
+ if(preselectProfile())
+ sc.setProfile(preselectedProfileId());
+ if(preselectUser())
+ sc.login(preselectedUserName(),password(),autoLogin());
}
#include <QDialog>
+class QCheckBox;
+class QLineEdit;
+class QComboBox;
+class QSpinBox;
+class MSessionClient;
+
class MPClientConfig:public QDialog
{
Q_OBJECT
static int timerInMinutes();
static bool preselectProfile();
- static QString preselectedProfileName();
+ static QString preselectedProfileId();
static bool preselectUser();
static QString preselectedUserName();
static bool autoLogin();
static QString password();
+ static void performAutoLogin(MSessionClient&);
+
public slots:
void save();
private:
QSpinBox*mtimer;
- QCheckBox*msetprofile,msetuser,mautologin;
+ QCheckBox*msetprofile,*msetuser,*mautologin;
QLineEdit*musername,*mpassword;
QComboBox*mprofile;
#include <WTransaction>
#include "pah.h"
+#include "client.h"
#include <QIcon>
#include <QMenu>
void PrintIcon::clientConfig()
{
-
+ MPClientConfig cc;
+ cc.exec();
}
//init
app.initialize();
+ app.setQuitOnLastWindowClosed(false);
//get session
MSessionClient sc;
sc.connect(&sc,SIGNAL(sessionLost()),&app,SLOT(quit()));
sc.connect(&sc,SIGNAL(managerLost()),&app,SLOT(quit()));
+ MPClientConfig::performAutoLogin(sc);
if(sc.waitForSessionAvailable()){
WTransaction::setLogPrefix("P@H-T");
MSInterface*ms=new MSInterface(sc.currentProfileId());
include(../commonlib/commonlib.pri)
#sources
-SOURCES += pah.cpp
-HEADERS += pah.h
+SOURCES += pah.cpp client.cpp
+HEADERS += pah.h client.h
RESOURCES += files.qrc
return !msid.isEmpty();
}
+bool MSessionClient::waitForReady()
+{
+ if(!isConnected())return false;
+ if(misready)return true;
+ //wait for signal
+ QEventLoop loop;
+ connect(this,SIGNAL(readyReceived()),&loop,SLOT(quit()));
+ connect(this,SIGNAL(managerLost()),&loop,SLOT(quit()));
+ loop.exec();
+ return misready;
+}
+
QList< QPair< QString, QString > > MSessionClient::menuEntries(bool force) const
{
if(!isConnected())return QList<QPair<QString,QString>>();
mdefaultprofile.clear();
}else if(cmd=="haveprofile"){
const int pos=par.indexOf(' ');
- if(pos<2){
- qDebug()<<"Warning: received invalid haveprofile command. Ignoring it.";
+ if(pos<1){
+ qDebug()<<"Warning: received invalid haveprofile command. Ignoring it. Info Parameter: "<<par;
continue;
}
mprofiles.append(QPair<QString,QString>(par.left(pos),par.mid(pos+1)));
}else if(cmd=="defaultprofile"){
- mdefaultprofile=par;
+ mdefaultprofile=par.trimmed();
}else if(cmd=="endprofiles"){
qDebug()<<"Received new list of profiles.";
emit profilesChanged();
emit sessionLost();
}else if(cmd=="quit"){
socketLost();
+ }else if(cmd=="ready"){
+ misready=true;
+ emit readyReceived();
}else
qDebug()<<"Warning: unknown session manager signal"<<cmd<<"encountered. Ignoring it.";
}
msocket->write(QString("setprofile %1\n").arg(pn).toUtf8());
}
-void MSessionClient::login(QString user,QString password)
+void MSessionClient::login(QString user,QString password,bool attemptLogin)
{
if(msocket && msocket->isOpen())
- msocket->write(QString("setuser %1\nsetpasswd %2\nlogin\n")
+ msocket->write(QString("setuser %1\nsetpasswd %2\n%3")
.arg(user)
.arg(QString::fromLatin1(password.toUtf8().toBase64()))
+ .arg(attemptLogin?"login\n":"")
.toUtf8());
}
///Wait until a session is available or the Session Manager quits.
///\returns true if a session is available, false on error
virtual bool waitForSessionAvailable();
+
+ ///Wait until the session manager is ready to receive commands.
+ virtual bool waitForReady();
///Returns true if there currently is a session available.
virtual bool sessionIsAvailable()const;
public slots:
void execServerCommand(QString);
void setProfile(QString);
- void login(QString user,QString password);
+ void login(QString user,QString password,bool attemptLogin=true);
signals:
void sessionIdChanged(QString sessionId);
void managerLost();
void menuChanged();
void profilesChanged();
+ void readyReceived();
private slots:
void socketLost();
QLocalSocket*msocket=nullptr;
QString msid,mprofile,muser,mdefaultprofile;
QList<QPair<QString,QString>> mmenu,mprofiles;
+ bool misready=false;
};
emit lostSession();
}
+void MLogin::setProfile(QString p)
+{
+ bool ok;
+ int n=p.toInt(&ok);
+ if(!ok)return;
+ profiles->setCurrentIndex(n);
+}
+
+void MLogin::setUsername(QString u)
+{
+ username->setText(u);
+}
+
+void MLogin::setPassword(QString pw)
+{
+ password->setText(pw);
+}
+
void MLogin::clientConfig()
{
MClientConfig cc(this);
private slots:
void initProfiles();
void loadProfile();
- void startLogin();
void initClients(MSessionManager*);
public slots:
void configwin();
void relogin();
void clientConfig();
+ void setProfile(QString);
+ void setUsername(QString);
+ void setPassword(QString);
+ void startLogin();
signals:
void loginSucceeded();
connect(s,SIGNAL(error(QLocalSocket::LocalSocketError)),this,SLOT(socketClosed()));
connect(s,SIGNAL(destroyed(QObject*)),this,SLOT(socketLost(QObject*)));
mconnections.append(s);
+ if(misready)s->write("ready\n");
}
}
s->write("closed\n");
}
+void MSessionManager::setReady()
+{
+ if(misready)return;
+ misready=true;
+ for(QLocalSocket*s:mconnections)
+ s->write("ready\n");
+}
+
QList<QPair< QString, QString >> MSessionManager::menuItems() const
{
static QList<QPair<QString,QString>> items;
lw.connect(&lw,SIGNAL(loginSucceeded()),sm,SLOT(loginSucceeded()));
lw.connect(&lw,SIGNAL(lostSession()),sm,SLOT(loginLost()));
lw.connect(sm,SIGNAL(openConfig()),&lw,SLOT(configwin()));
+ lw.connect(sm,SIGNAL(setProfile(QString)),&lw,SLOT(setProfile(QString)));
+ lw.connect(sm,SIGNAL(setUsername(QString)),&lw,SLOT(setUsername(QString)));
+ lw.connect(sm,SIGNAL(setPassword(QString)),&lw,SLOT(setPassword(QString)));
+ lw.connect(sm,SIGNAL(startLogin()),&lw,SLOT(startLogin()));
lw.show();
+
+ sm->setReady();
return app.exec();
}
virtual QList<QPair<QString,QString>> menuItems()const;
static MSessionManager*instance();
+
+ void setReady();
private slots:
void newConnection();
QString mkey;
QSystemTrayIcon*micon;
bool mhaveslave=false;
+ bool misready=false;
void sendMenu(QLocalSocket*);
void sendProfiles(QLocalSocket*);
<Property name="timezone" type="astring">Olsen database time zone name</Property>
<!-- <Property name="TZtransitions" type="List:TimeTransition">list of all precalculated transitions in this timezone, how far into the future calculations go depends on the server, usually at the moment till 2037</Property> -->
</Class>
+
+ <Class name="KeyValuePair">
+ <Property name="key" type="string"/>
+ <Property name="value" type="string"/>
+ <Property name="isnull" type="bool"/>
+ </Class>
</Wolf>
<DataBase
instance="db" schema="dbScheme" defaultUpdating="yes"
configTable="config" configKeyColumn="ckey" configValueColumn="cval"
- version="01.07" versionRow="MagicSmokeVersion">
+ version="01.08" versionRow="MagicSmokeVersion">
<AuditTables>
<Column name="audittime" type="int64">Time at which the change was made.
<Call lang="php" method="time()"/>
<Doc>Documents that refer to a specific order. E.g. invoices, Print@Home tickets, etc.</Doc>
<Column name="fileid" type="seq64" primarykey="yes"/>
<Column name="orderid" type="int32" foreignkey="order:orderid" notnull="yes"/>
- <Column name="filename" type="string" notnull="yes"/>
+ <Column name="filename" type="string" notnull="yes">
+ The file name shown to the customer, not necessarily related to the template.
+ </Column>
<Column name="mtime" type="int64" notnull="yes">The time when the file was last modified.</Column>
+ <Column name="rtime" type="int64" notnull="no">The time when the file was last retrieved by the customer.</Column>
<Column name="content" type="blob"/>
+ <Column name="visible" type="bool" notnull="yes">
+ Flag whether the document is internal to the theater (false) or visible to the customer (true).
+ </Column>
<Unique>orderid,filename</Unique>
</Table>
</Wolf>
<Call lang="php" method="WOOrder::getOrderDocument($this);"/>
<Output>
<Var name="content" type="blob"/>
+ <Var name="visible" type="bool"/>
+ <Var name="mtime" type="int64"/>
+ <Var name="rtime" type="int64"/>
</Output>
</Transaction>
<Transaction name="SetOrderDocument" update="yes">
<Var name="orderid" type="int"/>
<Var name="filename" type="string"/>
<Var name="content" type="blob"/>
+ <Var name="visible" type="bool"/>
</Input>
<Call lang="php" method="WOOrder::getOrderDocument($this);"/>
<Output/>
</Input>
<Call lang="php" method=";"/><!-- TODO -->
<Output/>
+ </Transaction>
+
+ <Transaction name="GetPrintAtHomeSettings" update="no">
+ <Doc>Gets all settings pertaining to Print@Home</Doc>
+ <Input/>
+ <Call lang="php" method=";"/><!-- TODO -->
+ <Output>
+ <Var name="settings" type="List:KeyValuePair"/>
+ </Output>
+ </Transaction>
+ <Transaction name="SetPrintAtHomeSettings" update="yes">
+ <Doc>Overrides settings for Print@Home, use isnull=true to delete entries.</Doc>
+ <Input>
+ <Var name="settings" type="List:KeyValuePair"/>
+ </Input>
+ <Call lang="php" method=";"/><!-- TODO -->
+ <Output/>
</Transaction>
</Wolf>