Merge pull request #1101 from ctrlaltca/registration

[WIP] In-client Registration support
This commit is contained in:
woogerboy21 2015-05-30 13:49:04 -04:00
commit 28ccec711e
54 changed files with 3669 additions and 66 deletions

View file

@ -12,6 +12,7 @@ SET(cockatrice_SOURCES
src/dlg_connect.cpp
src/dlg_create_token.cpp
src/dlg_edit_tokens.cpp
src/dlg_register.cpp
src/abstractclient.cpp
src/remoteclient.cpp
src/main.cpp

View file

@ -29,9 +29,10 @@ enum ClientStatus {
StatusDisconnected,
StatusDisconnecting,
StatusConnecting,
StatusAwaitingWelcome,
StatusRegistering,
StatusActivating,
StatusLoggingIn,
StatusLoggedIn
StatusLoggedIn,
};
class AbstractClient : public QObject {
@ -59,6 +60,9 @@ signals:
void buddyListReceived(const QList<ServerInfo_User> &buddyList);
void ignoreListReceived(const QList<ServerInfo_User> &ignoreList);
void replayAddedEventReceived(const Event_ReplayAdded &event);
void registerAccepted();
void registerAcceptedNeedsActivate();
void activateAccepted();
void sigQueuePendingCommand(PendingCommand *pend);
private:
@ -71,7 +75,8 @@ protected slots:
void processProtocolItem(const ServerMessage &item);
protected:
QMap<int, PendingCommand *> pendingCommands;
QString userName, password;
QString userName, password, email, country, realName, token;
int gender;
void setStatus(ClientStatus _status);
int getNewCmdId() { return nextCmdId++; }
virtual void sendCommandContainer(const CommandContainer &cont) = 0;

View file

@ -0,0 +1,355 @@
#include <QSettings>
#include <QLabel>
#include <QCheckBox>
#include <QGridLayout>
#include <QHBoxLayout>
#include <QDialogButtonBox>
#include <QDebug>
#include "dlg_register.h"
#include "pb/serverinfo_user.pb.h"
DlgRegister::DlgRegister(QWidget *parent)
: QDialog(parent)
{
QSettings settings;
settings.beginGroup("server");
hostLabel = new QLabel(tr("&Host:"));
hostEdit = new QLineEdit(settings.value("hostname", "cockatrice.woogerworks.com").toString());
hostLabel->setBuddy(hostEdit);
portLabel = new QLabel(tr("&Port:"));
portEdit = new QLineEdit(settings.value("port", "4747").toString());
portLabel->setBuddy(portEdit);
playernameLabel = new QLabel(tr("Player &name:"));
playernameEdit = new QLineEdit(settings.value("playername", "Player").toString());
playernameLabel->setBuddy(playernameEdit);
passwordLabel = new QLabel(tr("P&assword:"));
passwordEdit = new QLineEdit(settings.value("password").toString());
passwordLabel->setBuddy(passwordEdit);
passwordEdit->setEchoMode(QLineEdit::Password);
emailLabel = new QLabel(tr("Email:"));
emailEdit = new QLineEdit();
emailLabel->setBuddy(emailEdit);
genderLabel = new QLabel(tr("Gender:"));
genderEdit = new QComboBox();
genderLabel->setBuddy(genderEdit);
genderEdit->insertItem(0, QIcon(":/resources/genders/unknown.svg"), tr("Undefined"));
genderEdit->insertItem(1, QIcon(":/resources/genders/male.svg"), tr("Male"));
genderEdit->insertItem(2, QIcon(":/resources/genders/female.svg"), tr("Female"));
genderEdit->setCurrentIndex(0);
countryLabel = new QLabel(tr("Country:"));
countryEdit = new QComboBox();
countryLabel->setBuddy(countryEdit);
countryEdit->insertItem(0, tr("Undefined"));
countryEdit->addItem(QPixmap(":/resources/countries/ad.svg"), "ad");
countryEdit->addItem(QIcon(":/resources/countries/ae.svg"), "ae");
countryEdit->addItem(QIcon(":/resources/countries/af.svg"), "af");
countryEdit->addItem(QIcon(":/resources/countries/ag.svg"), "ag");
countryEdit->addItem(QIcon(":/resources/countries/ai.svg"), "ai");
countryEdit->addItem(QIcon(":/resources/countries/al.svg"), "al");
countryEdit->addItem(QIcon(":/resources/countries/am.svg"), "am");
countryEdit->addItem(QIcon(":/resources/countries/ao.svg"), "ao");
countryEdit->addItem(QIcon(":/resources/countries/aq.svg"), "aq");
countryEdit->addItem(QIcon(":/resources/countries/ar.svg"), "ar");
countryEdit->addItem(QIcon(":/resources/countries/as.svg"), "as");
countryEdit->addItem(QIcon(":/resources/countries/at.svg"), "at");
countryEdit->addItem(QIcon(":/resources/countries/au.svg"), "au");
countryEdit->addItem(QIcon(":/resources/countries/aw.svg"), "aw");
countryEdit->addItem(QIcon(":/resources/countries/ax.svg"), "ax");
countryEdit->addItem(QIcon(":/resources/countries/az.svg"), "az");
countryEdit->addItem(QIcon(":/resources/countries/ba.svg"), "ba");
countryEdit->addItem(QIcon(":/resources/countries/bb.svg"), "bb");
countryEdit->addItem(QIcon(":/resources/countries/bd.svg"), "bd");
countryEdit->addItem(QIcon(":/resources/countries/be.svg"), "be");
countryEdit->addItem(QIcon(":/resources/countries/bf.svg"), "bf");
countryEdit->addItem(QIcon(":/resources/countries/bg.svg"), "bg");
countryEdit->addItem(QIcon(":/resources/countries/bh.svg"), "bh");
countryEdit->addItem(QIcon(":/resources/countries/bi.svg"), "bi");
countryEdit->addItem(QIcon(":/resources/countries/bj.svg"), "bj");
countryEdit->addItem(QIcon(":/resources/countries/bl.svg"), "bl");
countryEdit->addItem(QIcon(":/resources/countries/bm.svg"), "bm");
countryEdit->addItem(QIcon(":/resources/countries/bn.svg"), "bn");
countryEdit->addItem(QIcon(":/resources/countries/bo.svg"), "bo");
countryEdit->addItem(QIcon(":/resources/countries/bq.svg"), "bq");
countryEdit->addItem(QIcon(":/resources/countries/br.svg"), "br");
countryEdit->addItem(QIcon(":/resources/countries/bs.svg"), "bs");
countryEdit->addItem(QIcon(":/resources/countries/bt.svg"), "bt");
countryEdit->addItem(QIcon(":/resources/countries/bv.svg"), "bv");
countryEdit->addItem(QIcon(":/resources/countries/bw.svg"), "bw");
countryEdit->addItem(QIcon(":/resources/countries/by.svg"), "by");
countryEdit->addItem(QIcon(":/resources/countries/bz.svg"), "bz");
countryEdit->addItem(QIcon(":/resources/countries/ca.svg"), "ca");
countryEdit->addItem(QIcon(":/resources/countries/cc.svg"), "cc");
countryEdit->addItem(QIcon(":/resources/countries/cd.svg"), "cd");
countryEdit->addItem(QIcon(":/resources/countries/cf.svg"), "cf");
countryEdit->addItem(QIcon(":/resources/countries/cg.svg"), "cg");
countryEdit->addItem(QIcon(":/resources/countries/ch.svg"), "ch");
countryEdit->addItem(QIcon(":/resources/countries/ci.svg"), "ci");
countryEdit->addItem(QIcon(":/resources/countries/ck.svg"), "ck");
countryEdit->addItem(QIcon(":/resources/countries/cl.svg"), "cl");
countryEdit->addItem(QIcon(":/resources/countries/cm.svg"), "cm");
countryEdit->addItem(QIcon(":/resources/countries/cn.svg"), "cn");
countryEdit->addItem(QIcon(":/resources/countries/co.svg"), "co");
countryEdit->addItem(QIcon(":/resources/countries/cr.svg"), "cr");
countryEdit->addItem(QIcon(":/resources/countries/cu.svg"), "cu");
countryEdit->addItem(QIcon(":/resources/countries/cv.svg"), "cv");
countryEdit->addItem(QIcon(":/resources/countries/cw.svg"), "cw");
countryEdit->addItem(QIcon(":/resources/countries/cx.svg"), "cx");
countryEdit->addItem(QIcon(":/resources/countries/cy.svg"), "cy");
countryEdit->addItem(QIcon(":/resources/countries/cz.svg"), "cz");
countryEdit->addItem(QIcon(":/resources/countries/de.svg"), "de");
countryEdit->addItem(QIcon(":/resources/countries/dj.svg"), "dj");
countryEdit->addItem(QIcon(":/resources/countries/dk.svg"), "dk");
countryEdit->addItem(QIcon(":/resources/countries/dm.svg"), "dm");
countryEdit->addItem(QIcon(":/resources/countries/do.svg"), "do");
countryEdit->addItem(QIcon(":/resources/countries/dz.svg"), "dz");
countryEdit->addItem(QIcon(":/resources/countries/ec.svg"), "ec");
countryEdit->addItem(QIcon(":/resources/countries/ee.svg"), "ee");
countryEdit->addItem(QIcon(":/resources/countries/eg.svg"), "eg");
countryEdit->addItem(QIcon(":/resources/countries/eh.svg"), "eh");
countryEdit->addItem(QIcon(":/resources/countries/er.svg"), "er");
countryEdit->addItem(QIcon(":/resources/countries/es.svg"), "es");
countryEdit->addItem(QIcon(":/resources/countries/et.svg"), "et");
countryEdit->addItem(QIcon(":/resources/countries/fi.svg"), "fi");
countryEdit->addItem(QIcon(":/resources/countries/fj.svg"), "fj");
countryEdit->addItem(QIcon(":/resources/countries/fk.svg"), "fk");
countryEdit->addItem(QIcon(":/resources/countries/fm.svg"), "fm");
countryEdit->addItem(QIcon(":/resources/countries/fo.svg"), "fo");
countryEdit->addItem(QIcon(":/resources/countries/fr.svg"), "fr");
countryEdit->addItem(QIcon(":/resources/countries/ga.svg"), "ga");
countryEdit->addItem(QIcon(":/resources/countries/gb.svg"), "gb");
countryEdit->addItem(QIcon(":/resources/countries/gd.svg"), "gd");
countryEdit->addItem(QIcon(":/resources/countries/ge.svg"), "ge");
countryEdit->addItem(QIcon(":/resources/countries/gf.svg"), "gf");
countryEdit->addItem(QIcon(":/resources/countries/gg.svg"), "gg");
countryEdit->addItem(QIcon(":/resources/countries/gh.svg"), "gh");
countryEdit->addItem(QIcon(":/resources/countries/gi.svg"), "gi");
countryEdit->addItem(QIcon(":/resources/countries/gl.svg"), "gl");
countryEdit->addItem(QIcon(":/resources/countries/gm.svg"), "gm");
countryEdit->addItem(QIcon(":/resources/countries/gn.svg"), "gn");
countryEdit->addItem(QIcon(":/resources/countries/gp.svg"), "gp");
countryEdit->addItem(QIcon(":/resources/countries/gq.svg"), "gq");
countryEdit->addItem(QIcon(":/resources/countries/gr.svg"), "gr");
countryEdit->addItem(QIcon(":/resources/countries/gs.svg"), "gs");
countryEdit->addItem(QIcon(":/resources/countries/gt.svg"), "gt");
countryEdit->addItem(QIcon(":/resources/countries/gu.svg"), "gu");
countryEdit->addItem(QIcon(":/resources/countries/gw.svg"), "gw");
countryEdit->addItem(QIcon(":/resources/countries/gy.svg"), "gy");
countryEdit->addItem(QIcon(":/resources/countries/hk.svg"), "hk");
countryEdit->addItem(QIcon(":/resources/countries/hm.svg"), "hm");
countryEdit->addItem(QIcon(":/resources/countries/hn.svg"), "hn");
countryEdit->addItem(QIcon(":/resources/countries/hr.svg"), "hr");
countryEdit->addItem(QIcon(":/resources/countries/ht.svg"), "ht");
countryEdit->addItem(QIcon(":/resources/countries/hu.svg"), "hu");
countryEdit->addItem(QIcon(":/resources/countries/id.svg"), "id");
countryEdit->addItem(QIcon(":/resources/countries/ie.svg"), "ie");
countryEdit->addItem(QIcon(":/resources/countries/il.svg"), "il");
countryEdit->addItem(QIcon(":/resources/countries/im.svg"), "im");
countryEdit->addItem(QIcon(":/resources/countries/in.svg"), "in");
countryEdit->addItem(QIcon(":/resources/countries/io.svg"), "io");
countryEdit->addItem(QIcon(":/resources/countries/iq.svg"), "iq");
countryEdit->addItem(QIcon(":/resources/countries/ir.svg"), "ir");
countryEdit->addItem(QIcon(":/resources/countries/is.svg"), "is");
countryEdit->addItem(QIcon(":/resources/countries/it.svg"), "it");
countryEdit->addItem(QIcon(":/resources/countries/je.svg"), "je");
countryEdit->addItem(QIcon(":/resources/countries/jm.svg"), "jm");
countryEdit->addItem(QIcon(":/resources/countries/jo.svg"), "jo");
countryEdit->addItem(QIcon(":/resources/countries/jp.svg"), "jp");
countryEdit->addItem(QIcon(":/resources/countries/ke.svg"), "ke");
countryEdit->addItem(QIcon(":/resources/countries/kg.svg"), "kg");
countryEdit->addItem(QIcon(":/resources/countries/kh.svg"), "kh");
countryEdit->addItem(QIcon(":/resources/countries/ki.svg"), "ki");
countryEdit->addItem(QIcon(":/resources/countries/km.svg"), "km");
countryEdit->addItem(QIcon(":/resources/countries/kn.svg"), "kn");
countryEdit->addItem(QIcon(":/resources/countries/kp.svg"), "kp");
countryEdit->addItem(QIcon(":/resources/countries/kr.svg"), "kr");
countryEdit->addItem(QIcon(":/resources/countries/kw.svg"), "kw");
countryEdit->addItem(QIcon(":/resources/countries/ky.svg"), "ky");
countryEdit->addItem(QIcon(":/resources/countries/kz.svg"), "kz");
countryEdit->addItem(QIcon(":/resources/countries/la.svg"), "la");
countryEdit->addItem(QIcon(":/resources/countries/lb.svg"), "lb");
countryEdit->addItem(QIcon(":/resources/countries/lc.svg"), "lc");
countryEdit->addItem(QIcon(":/resources/countries/li.svg"), "li");
countryEdit->addItem(QIcon(":/resources/countries/lk.svg"), "lk");
countryEdit->addItem(QIcon(":/resources/countries/lr.svg"), "lr");
countryEdit->addItem(QIcon(":/resources/countries/ls.svg"), "ls");
countryEdit->addItem(QIcon(":/resources/countries/lt.svg"), "lt");
countryEdit->addItem(QIcon(":/resources/countries/lu.svg"), "lu");
countryEdit->addItem(QIcon(":/resources/countries/lv.svg"), "lv");
countryEdit->addItem(QIcon(":/resources/countries/ly.svg"), "ly");
countryEdit->addItem(QIcon(":/resources/countries/ma.svg"), "ma");
countryEdit->addItem(QIcon(":/resources/countries/mc.svg"), "mc");
countryEdit->addItem(QIcon(":/resources/countries/md.svg"), "md");
countryEdit->addItem(QIcon(":/resources/countries/me.svg"), "me");
countryEdit->addItem(QIcon(":/resources/countries/mf.svg"), "mf");
countryEdit->addItem(QIcon(":/resources/countries/mg.svg"), "mg");
countryEdit->addItem(QIcon(":/resources/countries/mh.svg"), "mh");
countryEdit->addItem(QIcon(":/resources/countries/mk.svg"), "mk");
countryEdit->addItem(QIcon(":/resources/countries/ml.svg"), "ml");
countryEdit->addItem(QIcon(":/resources/countries/mm.svg"), "mm");
countryEdit->addItem(QIcon(":/resources/countries/mn.svg"), "mn");
countryEdit->addItem(QIcon(":/resources/countries/mo.svg"), "mo");
countryEdit->addItem(QIcon(":/resources/countries/mp.svg"), "mp");
countryEdit->addItem(QIcon(":/resources/countries/mq.svg"), "mq");
countryEdit->addItem(QIcon(":/resources/countries/mr.svg"), "mr");
countryEdit->addItem(QIcon(":/resources/countries/ms.svg"), "ms");
countryEdit->addItem(QIcon(":/resources/countries/mt.svg"), "mt");
countryEdit->addItem(QIcon(":/resources/countries/mu.svg"), "mu");
countryEdit->addItem(QIcon(":/resources/countries/mv.svg"), "mv");
countryEdit->addItem(QIcon(":/resources/countries/mw.svg"), "mw");
countryEdit->addItem(QIcon(":/resources/countries/mx.svg"), "mx");
countryEdit->addItem(QIcon(":/resources/countries/my.svg"), "my");
countryEdit->addItem(QIcon(":/resources/countries/mz.svg"), "mz");
countryEdit->addItem(QIcon(":/resources/countries/na.svg"), "na");
countryEdit->addItem(QIcon(":/resources/countries/nc.svg"), "nc");
countryEdit->addItem(QIcon(":/resources/countries/ne.svg"), "ne");
countryEdit->addItem(QIcon(":/resources/countries/nf.svg"), "nf");
countryEdit->addItem(QIcon(":/resources/countries/ng.svg"), "ng");
countryEdit->addItem(QIcon(":/resources/countries/ni.svg"), "ni");
countryEdit->addItem(QIcon(":/resources/countries/nl.svg"), "nl");
countryEdit->addItem(QIcon(":/resources/countries/no.svg"), "no");
countryEdit->addItem(QIcon(":/resources/countries/np.svg"), "np");
countryEdit->addItem(QIcon(":/resources/countries/nr.svg"), "nr");
countryEdit->addItem(QIcon(":/resources/countries/nu.svg"), "nu");
countryEdit->addItem(QIcon(":/resources/countries/nz.svg"), "nz");
countryEdit->addItem(QIcon(":/resources/countries/om.svg"), "om");
countryEdit->addItem(QIcon(":/resources/countries/pa.svg"), "pa");
countryEdit->addItem(QIcon(":/resources/countries/pe.svg"), "pe");
countryEdit->addItem(QIcon(":/resources/countries/pf.svg"), "pf");
countryEdit->addItem(QIcon(":/resources/countries/pg.svg"), "pg");
countryEdit->addItem(QIcon(":/resources/countries/ph.svg"), "ph");
countryEdit->addItem(QIcon(":/resources/countries/pk.svg"), "pk");
countryEdit->addItem(QIcon(":/resources/countries/pl.svg"), "pl");
countryEdit->addItem(QIcon(":/resources/countries/pm.svg"), "pm");
countryEdit->addItem(QIcon(":/resources/countries/pn.svg"), "pn");
countryEdit->addItem(QIcon(":/resources/countries/pr.svg"), "pr");
countryEdit->addItem(QIcon(":/resources/countries/ps.svg"), "ps");
countryEdit->addItem(QIcon(":/resources/countries/pt.svg"), "pt");
countryEdit->addItem(QIcon(":/resources/countries/pw.svg"), "pw");
countryEdit->addItem(QIcon(":/resources/countries/py.svg"), "py");
countryEdit->addItem(QIcon(":/resources/countries/qa.svg"), "qa");
countryEdit->addItem(QIcon(":/resources/countries/re.svg"), "re");
countryEdit->addItem(QIcon(":/resources/countries/ro.svg"), "ro");
countryEdit->addItem(QIcon(":/resources/countries/rs.svg"), "rs");
countryEdit->addItem(QIcon(":/resources/countries/ru.svg"), "ru");
countryEdit->addItem(QIcon(":/resources/countries/rw.svg"), "rw");
countryEdit->addItem(QIcon(":/resources/countries/sa.svg"), "sa");
countryEdit->addItem(QIcon(":/resources/countries/sb.svg"), "sb");
countryEdit->addItem(QIcon(":/resources/countries/sc.svg"), "sc");
countryEdit->addItem(QIcon(":/resources/countries/sd.svg"), "sd");
countryEdit->addItem(QIcon(":/resources/countries/se.svg"), "se");
countryEdit->addItem(QIcon(":/resources/countries/sg.svg"), "sg");
countryEdit->addItem(QIcon(":/resources/countries/sh.svg"), "sh");
countryEdit->addItem(QIcon(":/resources/countries/si.svg"), "si");
countryEdit->addItem(QIcon(":/resources/countries/sj.svg"), "sj");
countryEdit->addItem(QIcon(":/resources/countries/sk.svg"), "sk");
countryEdit->addItem(QIcon(":/resources/countries/sl.svg"), "sl");
countryEdit->addItem(QIcon(":/resources/countries/sm.svg"), "sm");
countryEdit->addItem(QIcon(":/resources/countries/sn.svg"), "sn");
countryEdit->addItem(QIcon(":/resources/countries/so.svg"), "so");
countryEdit->addItem(QIcon(":/resources/countries/sr.svg"), "sr");
countryEdit->addItem(QIcon(":/resources/countries/ss.svg"), "ss");
countryEdit->addItem(QIcon(":/resources/countries/st.svg"), "st");
countryEdit->addItem(QIcon(":/resources/countries/sv.svg"), "sv");
countryEdit->addItem(QIcon(":/resources/countries/sx.svg"), "sx");
countryEdit->addItem(QIcon(":/resources/countries/sy.svg"), "sy");
countryEdit->addItem(QIcon(":/resources/countries/sz.svg"), "sz");
countryEdit->addItem(QIcon(":/resources/countries/tc.svg"), "tc");
countryEdit->addItem(QIcon(":/resources/countries/td.svg"), "td");
countryEdit->addItem(QIcon(":/resources/countries/tf.svg"), "tf");
countryEdit->addItem(QIcon(":/resources/countries/tg.svg"), "tg");
countryEdit->addItem(QIcon(":/resources/countries/th.svg"), "th");
countryEdit->addItem(QIcon(":/resources/countries/tj.svg"), "tj");
countryEdit->addItem(QIcon(":/resources/countries/tk.svg"), "tk");
countryEdit->addItem(QIcon(":/resources/countries/tl.svg"), "tl");
countryEdit->addItem(QIcon(":/resources/countries/tm.svg"), "tm");
countryEdit->addItem(QIcon(":/resources/countries/tn.svg"), "tn");
countryEdit->addItem(QIcon(":/resources/countries/to.svg"), "to");
countryEdit->addItem(QIcon(":/resources/countries/tr.svg"), "tr");
countryEdit->addItem(QIcon(":/resources/countries/tt.svg"), "tt");
countryEdit->addItem(QIcon(":/resources/countries/tv.svg"), "tv");
countryEdit->addItem(QIcon(":/resources/countries/tw.svg"), "tw");
countryEdit->addItem(QIcon(":/resources/countries/tz.svg"), "tz");
countryEdit->addItem(QIcon(":/resources/countries/ua.svg"), "ua");
countryEdit->addItem(QIcon(":/resources/countries/ug.svg"), "ug");
countryEdit->addItem(QIcon(":/resources/countries/um.svg"), "um");
countryEdit->addItem(QIcon(":/resources/countries/us.svg"), "us");
countryEdit->addItem(QIcon(":/resources/countries/uy.svg"), "uy");
countryEdit->addItem(QIcon(":/resources/countries/uz.svg"), "uz");
countryEdit->addItem(QIcon(":/resources/countries/va.svg"), "va");
countryEdit->addItem(QIcon(":/resources/countries/vc.svg"), "vc");
countryEdit->addItem(QIcon(":/resources/countries/ve.svg"), "ve");
countryEdit->addItem(QIcon(":/resources/countries/vg.svg"), "vg");
countryEdit->addItem(QIcon(":/resources/countries/vi.svg"), "vi");
countryEdit->addItem(QIcon(":/resources/countries/vn.svg"), "vn");
countryEdit->addItem(QIcon(":/resources/countries/vu.svg"), "vu");
countryEdit->addItem(QIcon(":/resources/countries/wf.svg"), "wf");
countryEdit->addItem(QIcon(":/resources/countries/ws.svg"), "ws");
countryEdit->addItem(QIcon(":/resources/countries/ye.svg"), "ye");
countryEdit->addItem(QIcon(":/resources/countries/yt.svg"), "yt");
countryEdit->addItem(QIcon(":/resources/countries/za.svg"), "za");
countryEdit->addItem(QIcon(":/resources/countries/zm.svg"), "zm");
countryEdit->addItem(QIcon(":/resources/countries/zw.svg"), "zw");
countryEdit->setCurrentIndex(0);
realnameLabel = new QLabel(tr("Real name:"));
realnameEdit = new QLineEdit();
realnameLabel->setBuddy(realnameEdit);
QGridLayout *grid = new QGridLayout;
grid->addWidget(hostLabel, 0, 0);
grid->addWidget(hostEdit, 0, 1);
grid->addWidget(portLabel, 1, 0);
grid->addWidget(portEdit, 1, 1);
grid->addWidget(playernameLabel, 2, 0);
grid->addWidget(playernameEdit, 2, 1);
grid->addWidget(passwordLabel, 3, 0);
grid->addWidget(passwordEdit, 3, 1);
grid->addWidget(emailLabel, 4, 0);
grid->addWidget(emailEdit, 4, 1);
grid->addWidget(genderLabel, 5, 0);
grid->addWidget(genderEdit, 5, 1);
grid->addWidget(countryLabel, 6, 0);
grid->addWidget(countryEdit, 6, 1);
grid->addWidget(realnameLabel, 7, 0);
grid->addWidget(realnameEdit, 7, 1);
QDialogButtonBox *buttonBox = new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel);
connect(buttonBox, SIGNAL(accepted()), this, SLOT(actOk()));
connect(buttonBox, SIGNAL(rejected()), this, SLOT(actCancel()));
QVBoxLayout *mainLayout = new QVBoxLayout;
mainLayout->addLayout(grid);
mainLayout->addWidget(buttonBox);
setLayout(mainLayout);
setWindowTitle(tr("Register to server"));
setFixedHeight(sizeHint().height());
setMinimumWidth(300);
}
void DlgRegister::actOk()
{
QSettings settings;
settings.beginGroup("server");
settings.setValue("hostname", hostEdit->text());
settings.setValue("port", portEdit->text());
settings.setValue("playername", playernameEdit->text());
// always save the password so it will be picked up by the connect dialog
settings.setValue("password", passwordEdit->text());
settings.endGroup();
accept();
}
void DlgRegister::actCancel()
{
reject();
}

View file

@ -0,0 +1,33 @@
#ifndef DLG_REGISTER_H
#define DLG_REGISTER_H
#include <QDialog>
#include <QLineEdit>
#include <QComboBox>
class QLabel;
class QPushButton;
class QCheckBox;
class DlgRegister : public QDialog {
Q_OBJECT
public:
DlgRegister(QWidget *parent = 0);
QString getHost() const { return hostEdit->text(); }
int getPort() const { return portEdit->text().toInt(); }
QString getPlayerName() const { return playernameEdit->text(); }
QString getPassword() const { return passwordEdit->text(); }
QString getEmail() const { return emailEdit->text(); }
int getGender() const { return genderEdit->currentIndex() - 1; }
QString getCountry() const { return genderEdit->currentIndex() == 0 ? "" : countryEdit->currentText(); }
QString getRealName() const { return realnameEdit->text(); }
private slots:
void actOk();
void actCancel();
private:
QLabel *hostLabel, *portLabel, *playernameLabel, *passwordLabel, *emailLabel, *genderLabel, *countryLabel, *realnameLabel;
QLineEdit *hostEdit, *portEdit, *playernameEdit, *passwordEdit, *emailEdit, *realnameEdit;
QComboBox *genderEdit, *countryEdit;
};
#endif

View file

@ -6,6 +6,8 @@
#include "pb/commands.pb.h"
#include "pb/session_commands.pb.h"
#include "pb/response_login.pb.h"
#include "pb/response_register.pb.h"
#include "pb/response_activate.pb.h"
#include "pb/server_message.pb.h"
#include "pb/event_server_identification.pb.h"
@ -28,6 +30,8 @@ RemoteClient::RemoteClient(QObject *parent)
connect(this, SIGNAL(connectionClosedEventReceived(Event_ConnectionClosed)), this, SLOT(processConnectionClosedEvent(Event_ConnectionClosed)));
connect(this, SIGNAL(sigConnectToServer(QString, unsigned int, QString, QString)), this, SLOT(doConnectToServer(QString, unsigned int, QString, QString)));
connect(this, SIGNAL(sigDisconnectFromServer()), this, SLOT(doDisconnectFromServer()));
connect(this, SIGNAL(sigRegisterToServer(QString, unsigned int, QString, QString, QString, int, QString, QString)), this, SLOT(doRegisterToServer(QString, unsigned int, QString, QString, QString, int, QString, QString)));
connect(this, SIGNAL(sigActivateToServer(QString)), this, SLOT(doActivateToServer(QString)));
}
RemoteClient::~RemoteClient()
@ -52,8 +56,6 @@ void RemoteClient::slotConnected()
sendCommandContainer(CommandContainer());
getNewCmdId();
// end of hack
setStatus(StatusAwaitingWelcome);
}
void RemoteClient::processServerIdentificationEvent(const Event_ServerIdentification &event)
@ -63,6 +65,42 @@ void RemoteClient::processServerIdentificationEvent(const Event_ServerIdentifica
setStatus(StatusDisconnecting);
return;
}
if(getStatus() == StatusRegistering)
{
Command_Register cmdRegister;
cmdRegister.set_user_name(userName.toStdString());
cmdRegister.set_password(password.toStdString());
cmdRegister.set_email(email.toStdString());
cmdRegister.set_gender((ServerInfo_User_Gender) gender);
cmdRegister.set_country(country.toStdString());
cmdRegister.set_real_name(realName.toStdString());
PendingCommand *pend = prepareSessionCommand(cmdRegister);
connect(pend, SIGNAL(finished(Response, CommandContainer, QVariant)), this, SLOT(registerResponse(Response)));
sendCommand(pend);
return;
}
if(getStatus() == StatusActivating)
{
Command_Activate cmdActivate;
cmdActivate.set_user_name(userName.toStdString());
cmdActivate.set_token(token.toStdString());
PendingCommand *pend = prepareSessionCommand(cmdActivate);
connect(pend, SIGNAL(finished(Response, CommandContainer, QVariant)), this, SLOT(activateResponse(Response)));
sendCommand(pend);
return;
}
doLogin();
}
void RemoteClient::doLogin()
{
setStatus(StatusLoggingIn);
Command_Login cmdLogin;
@ -101,6 +139,38 @@ void RemoteClient::loginResponse(const Response &response)
}
}
void RemoteClient::registerResponse(const Response &response)
{
const Response_Register &resp = response.GetExtension(Response_Register::ext);
switch(response.response_code())
{
case Response::RespRegistrationAccepted:
emit registerAccepted();
doLogin();
break;
case Response::RespRegistrationAcceptedNeedsActivation:
emit registerAcceptedNeedsActivate();
doLogin();
break;
default:
emit registerError(response.response_code(), QString::fromStdString(resp.denied_reason_str()), resp.denied_end_time());
setStatus(StatusDisconnecting);
doDisconnectFromServer();
break;
}
}
void RemoteClient::activateResponse(const Response &response)
{
if (response.response_code() == Response::RespActivationAccepted) {
emit activateAccepted();
doLogin();
} else {
emit activateError();
}
}
void RemoteClient::readData()
{
lastDataReceived = timeRunning;
@ -171,10 +241,40 @@ void RemoteClient::doConnectToServer(const QString &hostname, unsigned int port,
userName = _userName;
password = _password;
lastHostname = hostname;
lastPort = port;
socket->connectToHost(hostname, port);
setStatus(StatusConnecting);
}
void RemoteClient::doRegisterToServer(const QString &hostname, unsigned int port, const QString &_userName, const QString &_password, const QString &_email, const int _gender, const QString &_country, const QString &_realname)
{
doDisconnectFromServer();
userName = _userName;
password = _password;
email = _email;
gender = _gender;
country = _country;
realName = _realname;
lastHostname = hostname;
lastPort = port;
socket->connectToHost(hostname, port);
setStatus(StatusRegistering);
}
void RemoteClient::doActivateToServer(const QString &_token)
{
doDisconnectFromServer();
token = _token;
socket->connectToHost(lastHostname, lastPort);
setStatus(StatusActivating);
}
void RemoteClient::doDisconnectFromServer()
{
timer->stop();
@ -225,6 +325,16 @@ void RemoteClient::connectToServer(const QString &hostname, unsigned int port, c
emit sigConnectToServer(hostname, port, _userName, _password);
}
void RemoteClient::registerToServer(const QString &hostname, unsigned int port, const QString &_userName, const QString &_password, const QString &_email, const int _gender, const QString &_country, const QString &_realname)
{
emit sigRegisterToServer(hostname, port, _userName, _password, _email, _gender, _country, _realname);
}
void RemoteClient::activateToServer(const QString &_token)
{
emit sigActivateToServer(_token);
}
void RemoteClient::disconnectFromServer()
{
emit sigDisconnectFromServer();

View file

@ -12,10 +12,14 @@ signals:
void maxPingTime(int seconds, int maxSeconds);
void serverTimeout();
void loginError(Response::ResponseCode resp, QString reasonStr, quint32 endTime);
void registerError(Response::ResponseCode resp, QString reasonStr, quint32 endTime);
void activateError();
void socketError(const QString &errorString);
void protocolVersionMismatch(int clientVersion, int serverVersion);
void protocolError();
void sigConnectToServer(const QString &hostname, unsigned int port, const QString &_userName, const QString &_password);
void sigRegisterToServer(const QString &hostname, unsigned int port, const QString &_userName, const QString &_password, const QString &_email, const int _gender, const QString &_country, const QString &_realname);
void sigActivateToServer(const QString &_token);
void sigDisconnectFromServer();
private slots:
void slotConnected();
@ -25,8 +29,14 @@ private slots:
void processServerIdentificationEvent(const Event_ServerIdentification &event);
void processConnectionClosedEvent(const Event_ConnectionClosed &event);
void loginResponse(const Response &response);
void registerResponse(const Response &response);
void activateResponse(const Response &response);
void doConnectToServer(const QString &hostname, unsigned int port, const QString &_userName, const QString &_password);
void doRegisterToServer(const QString &hostname, unsigned int port, const QString &_userName, const QString &_password, const QString &_email, const int _gender, const QString &_country, const QString &_realname);
void doLogin();
void doDisconnectFromServer();
void doActivateToServer(const QString &_token);
private:
static const int maxTimeout = 10;
int timeRunning, lastDataReceived;
@ -38,6 +48,8 @@ private:
QTimer *timer;
QTcpSocket *socket;
QString lastHostname;
int lastPort;
protected slots:
void sendCommandContainer(const CommandContainer &cont);
public:
@ -45,6 +57,8 @@ public:
~RemoteClient();
QString peerName() const { return socket->peerName(); }
void connectToServer(const QString &hostname, unsigned int port, const QString &_userName, const QString &_password);
void registerToServer(const QString &hostname, unsigned int port, const QString &_userName, const QString &_password, const QString &_email, const int _gender, const QString &_country, const QString &_realname);
void activateToServer(const QString &_token);
void disconnectFromServer();
};

View file

@ -34,6 +34,7 @@
#include "main.h"
#include "window_main.h"
#include "dlg_connect.h"
#include "dlg_register.h"
#include "dlg_settings.h"
#include "tab_supervisor.h"
#include "remoteclient.h"
@ -97,21 +98,22 @@ void MainWindow::statusChanged(ClientStatus _status)
{
setClientStatusTitle();
switch (_status) {
case StatusConnecting:
break;
case StatusDisconnected:
tabSupervisor->stop();
aSinglePlayer->setEnabled(true);
aConnect->setEnabled(true);
aRegister->setEnabled(true);
aDisconnect->setEnabled(false);
break;
case StatusLoggingIn:
aSinglePlayer->setEnabled(false);
aConnect->setEnabled(false);
aRegister->setEnabled(false);
aDisconnect->setEnabled(true);
break;
case StatusConnecting:
case StatusRegistering:
case StatusLoggedIn:
break;
default:
break;
}
@ -122,6 +124,21 @@ void MainWindow::userInfoReceived(const ServerInfo_User &info)
tabSupervisor->start(info);
}
void MainWindow::registerAccepted()
{
QMessageBox::information(this, tr("Success"), tr("Registration accepted.\nWill now login."));
}
void MainWindow::registerAcceptedNeedsActivate()
{
// nothing
}
void MainWindow::activateAccepted()
{
QMessageBox::information(this, tr("Success"), tr("Account activation accepted.\nWill now login."));
}
// Actions
void MainWindow::actConnect()
@ -131,6 +148,24 @@ void MainWindow::actConnect()
client->connectToServer(dlg.getHost(), dlg.getPort(), dlg.getPlayerName(), dlg.getPassword());
}
void MainWindow::actRegister()
{
DlgRegister dlg(this);
if (dlg.exec())
{
client->registerToServer(
dlg.getHost(),
dlg.getPort(),
dlg.getPlayerName(),
dlg.getPassword(),
dlg.getEmail(),
dlg.getGender(),
dlg.getCountry(),
dlg.getRealName()
);
}
}
void MainWindow::actDisconnect()
{
client->disconnectFromServer();
@ -144,6 +179,7 @@ void MainWindow::actSinglePlayer()
return;
aConnect->setEnabled(false);
aRegister->setEnabled(false);
aSinglePlayer->setEnabled(false);
localServer = new LocalServer(this);
@ -191,6 +227,7 @@ void MainWindow::localGameEnded()
localServer = 0;
aConnect->setEnabled(true);
aRegister->setEnabled(true);
aSinglePlayer->setEnabled(true);
}
@ -269,11 +306,74 @@ void MainWindow::loginError(Response::ResponseCode r, QString reasonStr, quint32
QMessageBox::critical(this, tr("Error"), tr("Invalid username.\nYou may only use A-Z, a-z, 0-9, _, ., and - in your username."));
break;
case Response::RespRegistrationRequired:
QMessageBox::critical(this, tr("Error"), tr("This server requires user registration."));
if (QMessageBox::question(this, tr("Error"), tr("This server requires user registration. Do you want to register now?"), QMessageBox::Yes | QMessageBox::No) == QMessageBox::Yes) {
actRegister();
}
break;
case Response::RespAccountNotActivated: {
bool ok = false;
QString token = QInputDialog::getText(this, tr("Account activation"), tr("Your account has not been activated yet.\n You need to provide the activation token received in the activation email"), QLineEdit::Normal, QString(), &ok);
if(ok && !token.isEmpty())
{
client->activateToServer(token);
return;
}
client->disconnectFromServer();
break;
}
default:
QMessageBox::critical(this, tr("Error"), tr("Unknown login error: %1").arg(static_cast<int>(r)));
break;
}
actConnect();
}
void MainWindow::registerError(Response::ResponseCode r, QString reasonStr, quint32 endTime)
{
switch (r) {
case Response::RespRegistrationDisabled:
QMessageBox::critical(this, tr("Registration denied"), tr("Registration is currently disabled on this server"));
break;
case Response::RespUserAlreadyExists:
QMessageBox::critical(this, tr("Registration denied"), tr("There is already an existing account with the same user name."));
break;
case Response::RespEmailRequiredToRegister:
QMessageBox::critical(this, tr("Registration denied"), tr("It's mandatory to specify a valid email address when registering."));
break;
case Response::RespTooManyRequests:
QMessageBox::critical(this, tr("Registration denied"), tr("Too many registration attempts from your IP address."));
break;
case Response::RespPasswordTooShort:
QMessageBox::critical(this, tr("Registration denied"), tr("Password too short."));
break;
case Response::RespUserIsBanned: {
QString bannedStr;
if (endTime)
bannedStr = tr("You are banned until %1.").arg(QDateTime::fromTime_t(endTime).toString());
else
bannedStr = tr("You are banned indefinitely.");
if (!reasonStr.isEmpty())
bannedStr.append("\n\n" + reasonStr);
QMessageBox::critical(this, tr("Error"), bannedStr);
break;
}
case Response::RespUsernameInvalid:
QMessageBox::critical(this, tr("Error"), tr("Invalid username.\nYou may only use A-Z, a-z, 0-9, _, ., and - in your username."));
break;
case Response::RespRegistrationFailed:
QMessageBox::critical(this, tr("Error"), tr("Registration failed for a technical problem on the server."));
break;
default:
QMessageBox::critical(this, tr("Error"), tr("Unknown login error: %1").arg(static_cast<int>(r)));
}
actRegister();
}
void MainWindow::activateError()
{
QMessageBox::critical(this, tr("Error"), tr("Account activation failed"));
client->disconnectFromServer();
actConnect();
}
@ -295,6 +395,7 @@ void MainWindow::setClientStatusTitle()
{
switch (client->getStatus()) {
case StatusConnecting: setWindowTitle(appName + " - " + tr("Connecting to %1...").arg(client->peerName())); break;
case StatusRegistering: setWindowTitle(appName + " - " + tr("Registering to %1 as %2...").arg(client->peerName()).arg(client->getUserName())); break;
case StatusDisconnected: setWindowTitle(appName + " - " + tr("Disconnected")); break;
case StatusLoggingIn: setWindowTitle(appName + " - " + tr("Connected, logging in at %1").arg(client->peerName())); break;
case StatusLoggedIn: setWindowTitle(appName + " - " + tr("Logged in as %1 at %2").arg(client->getUserName()).arg(client->peerName())); break;
@ -313,6 +414,7 @@ void MainWindow::retranslateUi()
aDeckEditor->setText(tr("&Deck editor"));
aFullScreen->setText(tr("&Full screen"));
aFullScreen->setShortcut(QKeySequence("Ctrl+F"));
aRegister->setText(tr("&Register to server..."));
aSettings->setText(tr("&Settings..."));
aExit->setText(tr("&Exit"));
@ -344,6 +446,8 @@ void MainWindow::createActions()
aFullScreen = new QAction(this);
aFullScreen->setCheckable(true);
connect(aFullScreen, SIGNAL(toggled(bool)), this, SLOT(actFullScreen(bool)));
aRegister = new QAction(this);
connect(aRegister, SIGNAL(triggered()), this, SLOT(actRegister()));
aSettings = new QAction(this);
connect(aSettings, SIGNAL(triggered()), this, SLOT(actSettings()));
aExit = new QAction(this);
@ -376,6 +480,7 @@ void MainWindow::createMenus()
cockatriceMenu = menuBar()->addMenu(QString());
cockatriceMenu->addAction(aConnect);
cockatriceMenu->addAction(aDisconnect);
cockatriceMenu->addAction(aRegister);
cockatriceMenu->addSeparator();
cockatriceMenu->addAction(aSinglePlayer);
cockatriceMenu->addAction(aWatchReplay);
@ -408,7 +513,13 @@ MainWindow::MainWindow(QWidget *parent)
connect(client, SIGNAL(statusChanged(ClientStatus)), this, SLOT(statusChanged(ClientStatus)));
connect(client, SIGNAL(protocolVersionMismatch(int, int)), this, SLOT(protocolVersionMismatch(int, int)));
connect(client, SIGNAL(userInfoChanged(const ServerInfo_User &)), this, SLOT(userInfoReceived(const ServerInfo_User &)), Qt::BlockingQueuedConnection);
connect(client, SIGNAL(registerAccepted()), this, SLOT(registerAccepted()));
connect(client, SIGNAL(registerAcceptedNeedsActivate()), this, SLOT(registerAcceptedNeedsActivate()));
connect(client, SIGNAL(registerError(Response::ResponseCode, QString, quint32)), this, SLOT(registerError(Response::ResponseCode, QString, quint32)));
connect(client, SIGNAL(activateAccepted()), this, SLOT(activateAccepted()));
connect(client, SIGNAL(activateError()), this, SLOT(activateError()));
clientThread = new QThread(this);
client->moveToThread(clientThread);
clientThread->start();

View file

@ -43,9 +43,14 @@ private slots:
void processServerShutdownEvent(const Event_ServerShutdown &event);
void serverTimeout();
void loginError(Response::ResponseCode r, QString reasonStr, quint32 endTime);
void registerError(Response::ResponseCode r, QString reasonStr, quint32 endTime);
void activateError();
void socketError(const QString &errorStr);
void protocolVersionMismatch(int localVersion, int remoteVersion);
void userInfoReceived(const ServerInfo_User &userInfo);
void registerAccepted();
void registerAcceptedNeedsActivate();
void activateAccepted();
void localGameEnded();
void pixmapCacheSizeChanged(int newSizeInMBs);
@ -55,6 +60,7 @@ private slots:
void actWatchReplay();
void actDeckEditor();
void actFullScreen(bool checked);
void actRegister();
void actSettings();
void actExit();
@ -82,7 +88,7 @@ private:
QList<QMenu *> tabMenus;
QMenu *cockatriceMenu, *helpMenu;
QAction *aConnect, *aDisconnect, *aSinglePlayer, *aWatchReplay, *aDeckEditor, *aFullScreen, *aSettings, *aExit,
*aAbout, *aCheckCardUpdates;
*aAbout, *aCheckCardUpdates, *aRegister;
TabSupervisor *tabSupervisor;
QMenu *trayIconMenu;

View file

@ -114,6 +114,7 @@ SET(PROTO_FILES
isl_message.proto
moderator_commands.proto
move_card_to_zone.proto
response_activate.proto
response_deck_download.proto
response_deck_list.proto
response_deck_upload.proto
@ -123,6 +124,7 @@ SET(PROTO_FILES
response_join_room.proto
response_list_users.proto
response_login.proto
response_register.proto
response_replay_download.proto
response_replay_list.proto
response.proto

View file

@ -24,6 +24,17 @@ message Response {
RespAccessDenied = 20;
RespUsernameInvalid = 21;
RespRegistrationRequired = 22;
RespRegistrationAccepted = 23; // Server agrees to process client's registration request
RespUserAlreadyExists = 24; // Client attempted to register a name which is already registered
RespEmailRequiredToRegister = 25; // Server requires email to register accounts but client did not provide one
RespTooManyRequests = 26; // Server refused to complete command because client has sent too many too quickly
RespPasswordTooShort = 27; // Server requires a decent password
RespAccountNotActivated = 28; // Client attempted to log into a registered username but the account hasn't been activated
RespRegistrationDisabled = 29; // Server does not allow clients to register
RespRegistrationFailed = 30; // Server accepted a reg request but failed to perform the registration
RespActivationAccepted = 31; // Server accepted a reg user activation token
RespActivationFailed = 32; // Server didn't accept a reg user activation token
RespRegistrationAcceptedNeedsActivation = 33; // Server accepted cient registration, but it will need token activation
}
enum ResponseType {
JOIN_ROOM = 1000;
@ -35,6 +46,8 @@ message Response {
DECK_LIST = 1006;
DECK_DOWNLOAD = 1007;
DECK_UPLOAD = 1008;
REGISTER = 1009;
ACTIVATE = 1010;
REPLAY_LIST = 1100;
REPLAY_DOWNLOAD = 1101;
}

View file

@ -0,0 +1,7 @@
import "response.proto";
message Response_Activate {
extend Response {
optional Response_Activate ext = 1010;
}
}

View file

@ -0,0 +1,9 @@
import "response.proto";
message Response_Register {
extend Response {
optional Response_Register ext = 1009;
}
optional string denied_reason_str = 1;
optional uint64 denied_end_time = 2;
}

View file

@ -1,3 +1,5 @@
import "serverinfo_user.proto";
message SessionCommand {
enum SessionCommandType {
PING = 1000;
@ -16,6 +18,8 @@ message SessionCommand {
DECK_UPLOAD = 1013;
LIST_ROOMS = 1014;
JOIN_ROOM = 1015;
REGISTER = 1016;
ACTIVATE = 1017;
REPLAY_LIST = 1100;
REPLAY_DOWNLOAD = 1101;
REPLAY_MODIFY_MATCH = 1102;
@ -94,3 +98,32 @@ message Command_JoinRoom {
}
optional uint32 room_id = 1;
}
// User wants to register a new account
message Command_Register {
extend SessionCommand {
optional Command_Register ext = 1016;
}
// User name client wants to register
required string user_name = 1;
// Hashed password to be inserted into database
required string password = 2;
// Email address of the client for user validation
optional string email = 3;
// Gender of the user
optional ServerInfo_User.Gender gender = 4;
// Country code of the user. 2 letter ISO format
optional string country = 5;
optional string real_name = 6;
}
// User wants to activate an account
message Command_Activate {
extend SessionCommand {
optional Command_Activate ext = 1017;
}
// User name client wants to activate
required string user_name = 1;
// Activation token
required string token = 2;
}

View file

@ -113,7 +113,7 @@ AuthenticationResult Server::loginUser(Server_ProtocolHandler *session, QString
QWriteLocker locker(&clientsLock);
AuthenticationResult authState = databaseInterface->checkUserPassword(session, name, password, reasonStr, secondsLeft);
if ((authState == NotLoggedIn) || (authState == UserIsBanned || authState == UsernameInvalid))
if (authState == NotLoggedIn || authState == UserIsBanned || authState == UsernameInvalid || authState == UserIsInactive)
return authState;
ServerInfo_User data = databaseInterface->getUserData(name, true);
@ -140,7 +140,7 @@ AuthenticationResult Server::loginUser(Server_ProtocolHandler *session, QString
QString tempName = name;
int i = 0;
while (users.contains(tempName) || databaseInterface->userExists(tempName) || databaseInterface->userSessionExists(tempName))
while (users.contains(tempName) || databaseInterface->activeUserExists(tempName) || databaseInterface->userSessionExists(tempName))
tempName = name + "_" + QString::number(++i);
name = tempName;
data.set_name(name.toStdString());

View file

@ -7,6 +7,7 @@
#include <QMultiMap>
#include <QMutex>
#include <QReadWriteLock>
#include "pb/commands.pb.h"
#include "pb/serverinfo_user.pb.h"
#include "server_player_reference.h"
@ -27,7 +28,7 @@ class GameEventContainer;
class CommandContainer;
class Command_JoinGame;
enum AuthenticationResult { NotLoggedIn = 0, PasswordRight = 1, UnknownUser = 2, WouldOverwriteOldSession = 3, UserIsBanned = 4, UsernameInvalid = 5, RegistrationRequired = 6 };
enum AuthenticationResult { NotLoggedIn, PasswordRight, UnknownUser, WouldOverwriteOldSession, UserIsBanned, UsernameInvalid, RegistrationRequired, UserIsInactive };
class Server : public QObject
{
@ -44,6 +45,7 @@ public:
~Server();
void setThreaded(bool _threaded) { threaded = _threaded; }
AuthenticationResult loginUser(Server_ProtocolHandler *session, QString &name, const QString &password, QString &reason, int &secondsLeft);
const QMap<int, Server_Room *> &getRooms() { return rooms; }
Server_AbstractUserInterface *findUser(const QString &userName) const;

View file

@ -13,6 +13,8 @@ public:
: QObject(parent) { }
virtual AuthenticationResult checkUserPassword(Server_ProtocolHandler *handler, const QString &user, const QString &password, QString &reasonStr, int &secondsLeft) = 0;
virtual bool checkUserIsBanned(const QString & /* ipAddress */, const QString & /* userName */, QString & /* banReason */, int & /* banSecondsRemaining */) { return false; }
virtual bool activeUserExists(const QString & /* user */) { return false; }
virtual bool userExists(const QString & /* user */) { return false; }
virtual QMap<QString, ServerInfo_User> getBuddyList(const QString & /* name */) { return QMap<QString, ServerInfo_User>(); }
virtual QMap<QString, ServerInfo_User> getIgnoreList(const QString & /* name */) { return QMap<QString, ServerInfo_User>(); }
@ -23,6 +25,7 @@ public:
virtual DeckList *getDeckFromDatabase(int /* deckId */, int /* userId */) { return 0; }
virtual qint64 startSession(const QString & /* userName */, const QString & /* address */) { return 0; }
virtual bool usernameIsValid(const QString & /*userName */) { return true; };
public slots:
virtual void endSession(qint64 /* sessionId */ ) { }
public:
@ -35,9 +38,12 @@ public:
virtual bool userSessionExists(const QString & /* userName */) { return false; }
virtual bool getRequireRegistration() { return false; }
virtual bool registerUser(const QString & /* userName */, const QString & /* realName */, ServerInfo_User_Gender const & /* gender */, const QString & /* password */, const QString & /* emailAddress */, const QString & /* country */, bool /* active = false */) { return false; }
virtual bool activateUser(const QString & /* userName */, const QString & /* token */) { return false; }
enum LogMessage_TargetType { MessageTargetRoom, MessageTargetGame, MessageTargetChat, MessageTargetIslRoom };
virtual void logMessage(const int /* senderId */, const QString & /* senderName */, const QString & /* senderIp */, const QString & /* logMessage */, LogMessage_TargetType /* targetType */, const int /* targetId */, const QString & /* targetName */) { };
bool checkUserIsBanned(Server_ProtocolHandler *session, QString &banReason, int &banSecondsRemaining);
};
#endif

View file

@ -134,6 +134,10 @@ Response::ResponseCode Server_ProtocolHandler::processSessionCommandContainer(co
SessionCommand debugSc(sc);
debugSc.MutableExtension(Command_Login::ext)->clear_password();
logDebugMessage(QString::fromStdString(debugSc.ShortDebugString()));
} else if (num == SessionCommand::REGISTER) {
SessionCommand logSc(sc);
logSc.MutableExtension(Command_Register::ext)->clear_password();
logDebugMessage(QString::fromStdString(logSc.ShortDebugString()));
} else
logDebugMessage(QString::fromStdString(sc.ShortDebugString()));
}
@ -386,6 +390,7 @@ Response::ResponseCode Server_ProtocolHandler::cmdLogin(const Command_Login &cmd
case WouldOverwriteOldSession: return Response::RespWouldOverwriteOldSession;
case UsernameInvalid: return Response::RespUsernameInvalid;
case RegistrationRequired: return Response::RespRegistrationRequired;
case UserIsInactive: return Response::RespAccountNotActivated;
default: authState = res;
}

View file

@ -28,6 +28,7 @@ class AdminCommand;
class Command_Ping;
class Command_Login;
class Command_Register;
class Command_Message;
class Command_ListUsers;
class Command_GetGamesOfUser;
@ -98,6 +99,7 @@ public:
void sendProtocolItem(const SessionEvent &item);
void sendProtocolItem(const GameEventContainer &item);
void sendProtocolItem(const RoomEvent &item);
};
#endif

View file

@ -15,6 +15,18 @@ SET(servatrice_SOURCES
src/settingscache.cpp
src/isl_interface.cpp
${VERSION_STRING_CPP}
src/smtp/emailaddress.cpp
src/smtp/mimeattachment.cpp
src/smtp/mimecontentformatter.cpp
src/smtp/mimefile.cpp
src/smtp/mimehtml.cpp
src/smtp/mimeinlinefile.cpp
src/smtp/mimemessage.cpp
src/smtp/mimemultipart.cpp
src/smtp/mimepart.cpp
src/smtp/mimetext.cpp
src/smtp/quotedprintable.cpp
src/smtp/smtpclient.cpp
)
set(servatrice_RESOURCES servatrice.qrc)

1
servatrice/scripts/.gitignore vendored Normal file
View file

@ -0,0 +1 @@
pypb/

View file

@ -54,6 +54,50 @@ password=123456
; Accept only registered users? default is 0 (accept unregistered users)
regonly=0
[registration]
; Servatrice can process registration requests to add new users on the fly.
; Enable this feature? Default false.
;enabled=false
; Require users to provide an email address in order to register. Newly registered users will receive an
; activation token by email, and will be required to input back this token on cockatrice at the first login
; to get their account activated. Default true.
;requireemail=true
[smtp]
; Connectin type: currently supported method are "tcp", "ssl" and "tls"
connection=tcp
; Auth type: currently supported method are "plain" and "login"
auth=plain
; Hostname or IP addres of the smtp server
host=localhost
; Smtp port number of the smtp server. Usual values are 25 or 587 for tcp, 465 for ssl
port=25
; Username: this typically matches the "from" email address
username=root@localhost
; Password for the username
password=foobar
; Sender email address: the "from" email address
email=root@localhost
; Sender email name
name="Cockatrice server"
; Email subject
subject="Cockatrice server account activation token"
; Email body. You can use these tags here: %username %token
; They will be substituted with the actual values in the email
;
body="Hi %username, thank our for registering on our Cockatrice server\r\nHere's the activation token you need to supply for activatin your account:\r\n\r\n%token\r\n\r\nHappy gaming!"
[database]

View file

@ -4,10 +4,11 @@
#include <stdio.h>
#include <string.h>
#include <gcrypt.h>
#else
#include <QCryptographicHash>
#endif
#include <QCryptographicHash>
#include "rng_sfmt.h"
void PasswordHasher::initialize()
{
#if QT_VERSION < 0x050000
@ -51,3 +52,25 @@ QString PasswordHasher::computeHash(const QString &password, const QString &salt
return hashedPass;
}
#endif
QString PasswordHasher::generateRandomSalt(const int len)
{
static const char alphanum[] =
"0123456789"
"ABCDEFGHIJKLMNOPQRSTUVWXYZ"
"abcdefghijklmnopqrstuvwxyz";
QString ret;
int size = sizeof(alphanum) - 1;
for (int i = 0; i < len; ++i) {
ret.append(alphanum[rng->rand(0, size)]);
}
return ret;
}
QString PasswordHasher::generateActivationToken()
{
return QCryptographicHash::hash(generateRandomSalt().toUtf8(), QCryptographicHash::Md5).toBase64().left(16);
}

View file

@ -7,6 +7,8 @@ class PasswordHasher {
public:
static void initialize();
static QString computeHash(const QString &password, const QString &salt);
static QString generateRandomSalt(const int len = 16);
static QString generateActivationToken();
};
#endif

View file

@ -160,6 +160,13 @@ bool Servatrice::initServer()
authenticationMethod = AuthenticationNone;
}
bool registrationEnabled = settingsCache->value("registration/enabled", false).toBool();
bool requireEmailForRegistration = settingsCache->value("registration/requireemail", true).toBool();
qDebug() << "Registration enabled: " << registrationEnabled;
if (registrationEnabled)
qDebug() << "Require email address to register: " << requireEmailForRegistration;
QString dbTypeStr = settingsCache->value("database/type").toString();
if (dbTypeStr == "mysql")
databaseType = DatabaseMySql;
@ -172,12 +179,17 @@ bool Servatrice::initServer()
if (databaseType != DatabaseNone) {
settingsCache->beginGroup("database");
dbPrefix = settingsCache->value("prefix").toString();
servatriceDatabaseInterface->initDatabase("QMYSQL",
settingsCache->value("hostname").toString(),
settingsCache->value("database").toString(),
settingsCache->value("user").toString(),
settingsCache->value("password").toString());
bool dbOpened =
servatriceDatabaseInterface->initDatabase("QMYSQL",
settingsCache->value("hostname").toString(),
settingsCache->value("database").toString(),
settingsCache->value("user").toString(),
settingsCache->value("password").toString());
settingsCache->endGroup();
if (!dbOpened) {
qDebug() << "Failed to open database";
return false;
}
updateServerList();
@ -342,7 +354,7 @@ bool Servatrice::initServer()
if (gameServer->listen(QHostAddress::Any, gamePort))
qDebug() << "Server listening.";
else {
qDebug() << "gameServer->listen(): Error.";
qDebug() << "gameServer->listen(): Error:" << gameServer->errorString();
return false;
}
return true;

View file

@ -9,6 +9,7 @@
#include <QSqlError>
#include <QSqlQuery>
#include <QDateTime>
#include <QChar>
Servatrice_DatabaseInterface::Servatrice_DatabaseInterface(int _instanceId, Servatrice *_server)
: instanceId(_instanceId),
@ -34,7 +35,9 @@ void Servatrice_DatabaseInterface::initDatabase(const QSqlDatabase &_sqlDatabase
}
}
void Servatrice_DatabaseInterface::initDatabase(const QString &type, const QString &hostName, const QString &databaseName, const QString &userName, const QString &password)
bool Servatrice_DatabaseInterface::initDatabase(const QString &type, const QString &hostName,
const QString &databaseName, const QString &userName,
const QString &password)
{
sqlDatabase = QSqlDatabase::addDatabase(type, "main");
sqlDatabase.setHostName(hostName);
@ -42,7 +45,7 @@ void Servatrice_DatabaseInterface::initDatabase(const QString &type, const QStri
sqlDatabase.setUserName(userName);
sqlDatabase.setPassword(password);
openDatabase();
return openDatabase();
}
bool Servatrice_DatabaseInterface::openDatabase()
@ -102,11 +105,89 @@ bool Servatrice_DatabaseInterface::usernameIsValid(const QString &user)
return re.exactMatch(user);
}
// TODO move this to Server
bool Servatrice_DatabaseInterface::getRequireRegistration()
{
return settingsCache->value("authentication/regonly", 0).toBool();
}
bool Servatrice_DatabaseInterface::registerUser(const QString &userName, const QString &realName, ServerInfo_User_Gender const &gender, const QString &password, const QString &emailAddress, const QString &country, QString &token, bool active)
{
if (!checkSql())
return false;
QString passwordSha512 = PasswordHasher::computeHash(password, PasswordHasher::generateRandomSalt());
token = active ? QString() : PasswordHasher::generateActivationToken();
QSqlQuery *query = prepareQuery("insert into {prefix}_users "
"(name, realname, gender, password_sha512, email, country, registrationDate, active, token) "
"values "
"(:userName, :realName, :gender, :password_sha512, :email, :country, UTC_TIMESTAMP(), :active, :token)");
query->bindValue(":userName", userName);
query->bindValue(":realName", realName);
query->bindValue(":gender", getGenderChar(gender));
query->bindValue(":password_sha512", passwordSha512);
query->bindValue(":email", emailAddress);
query->bindValue(":country", country);
query->bindValue(":active", active ? 1 : 0);
query->bindValue(":token", token);
if (!execSqlQuery(query)) {
qDebug() << "Failed to insert user: " << query->lastError() << " sql: " << query->lastQuery();
return false;
}
return true;
}
bool Servatrice_DatabaseInterface::activateUser(const QString &userName, const QString &token)
{
if (!checkSql())
return false;
QSqlQuery *activateQuery = prepareQuery("select name from {prefix}_users where active=0 and name=:username and token=:token");
activateQuery->bindValue(":username", userName);
activateQuery->bindValue(":token", token);
if (!execSqlQuery(activateQuery)) {
qDebug() << "Account activation failed: SQL error." << activateQuery->lastError()<< " sql: " << activateQuery->lastQuery();
return false;
}
if (activateQuery->next()) {
const QString name = activateQuery->value(0).toString();
// redundant check
if(name == userName)
{
QSqlQuery *query = prepareQuery("update {prefix}_users set active=1 where name = :userName");
query->bindValue(":userName", userName);
if (!execSqlQuery(query)) {
qDebug() << "Failed to activate user: " << query->lastError() << " sql: " << query->lastQuery();
return false;
}
return true;
}
}
return false;
}
QChar Servatrice_DatabaseInterface::getGenderChar(ServerInfo_User_Gender const &gender)
{
switch (gender) {
case ServerInfo_User_Gender_GenderUnknown:
return QChar('u');
case ServerInfo_User_Gender_Male:
return QChar('m');
case ServerInfo_User_Gender_Female:
return QChar('f');
default:
return QChar('u');
}
}
AuthenticationResult Servatrice_DatabaseInterface::checkUserPassword(Server_ProtocolHandler *handler, const QString &user, const QString &password, QString &reasonStr, int &banSecondsLeft)
{
switch (server->getAuthenticationMethod()) {
@ -125,45 +206,10 @@ AuthenticationResult Servatrice_DatabaseInterface::checkUserPassword(Server_Prot
if (!usernameIsValid(user))
return UsernameInvalid;
QSqlQuery *ipBanQuery = prepareQuery("select time_to_sec(timediff(now(), date_add(b.time_from, interval b.minutes minute))), b.minutes <=> 0, b.visible_reason from {prefix}_bans b where b.time_from = (select max(c.time_from) from {prefix}_bans c where c.ip_address = :address) and b.ip_address = :address2");
ipBanQuery->bindValue(":address", static_cast<ServerSocketInterface *>(handler)->getPeerAddress().toString());
ipBanQuery->bindValue(":address2", static_cast<ServerSocketInterface *>(handler)->getPeerAddress().toString());
if (!execSqlQuery(ipBanQuery)) {
qDebug("Login denied: SQL error");
return NotLoggedIn;
}
if (checkUserIsBanned(handler->getAddress(), user, reasonStr, banSecondsLeft))
return UserIsBanned;
if (ipBanQuery->next()) {
const int secondsLeft = -ipBanQuery->value(0).toInt();
const bool permanentBan = ipBanQuery->value(1).toInt();
if ((secondsLeft > 0) || permanentBan) {
reasonStr = ipBanQuery->value(2).toString();
banSecondsLeft = permanentBan ? 0 : secondsLeft;
qDebug("Login denied: banned by address");
return UserIsBanned;
}
}
QSqlQuery *nameBanQuery = prepareQuery("select time_to_sec(timediff(now(), date_add(b.time_from, interval b.minutes minute))), b.minutes <=> 0, b.visible_reason from {prefix}_bans b where b.time_from = (select max(c.time_from) from {prefix}_bans c where c.user_name = :name2) and b.user_name = :name1");
nameBanQuery->bindValue(":name1", user);
nameBanQuery->bindValue(":name2", user);
if (!execSqlQuery(nameBanQuery)) {
qDebug("Login denied: SQL error");
return NotLoggedIn;
}
if (nameBanQuery->next()) {
const int secondsLeft = -nameBanQuery->value(0).toInt();
const bool permanentBan = nameBanQuery->value(1).toInt();
if ((secondsLeft > 0) || permanentBan) {
reasonStr = nameBanQuery->value(2).toString();
banSecondsLeft = permanentBan ? 0 : secondsLeft;
qDebug("Login denied: banned by name");
return UserIsBanned;
}
}
QSqlQuery *passwordQuery = prepareQuery("select password_sha512 from {prefix}_users where name = :name and active = 1");
QSqlQuery *passwordQuery = prepareQuery("select password_sha512, active from {prefix}_users where name = :name");
passwordQuery->bindValue(":name", user);
if (!execSqlQuery(passwordQuery)) {
qDebug("Login denied: SQL error");
@ -172,6 +218,11 @@ AuthenticationResult Servatrice_DatabaseInterface::checkUserPassword(Server_Prot
if (passwordQuery->next()) {
const QString correctPassword = passwordQuery->value(0).toString();
const bool userIsActive = passwordQuery->value(1).toBool();
if(!userIsActive) {
qDebug("Login denied: user not active");
return UserIsInactive;
}
if (correctPassword == PasswordHasher::computeHash(password, correctPassword.left(16))) {
qDebug("Login accepted: password right");
return PasswordRight;
@ -188,7 +239,80 @@ AuthenticationResult Servatrice_DatabaseInterface::checkUserPassword(Server_Prot
return UnknownUser;
}
bool Servatrice_DatabaseInterface::userExists(const QString &user)
bool Servatrice_DatabaseInterface::checkUserIsBanned(const QString &ipAddress, const QString &userName, QString &banReason, int &banSecondsRemaining)
{
if (server->getAuthenticationMethod() != Servatrice::AuthenticationSql)
return false;
if (!checkSql()) {
qDebug("Failed to check if user is banned. Database invalid.");
return false;
}
return
checkUserIsIpBanned(ipAddress, banReason, banSecondsRemaining)
|| checkUserIsNameBanned(userName, banReason, banSecondsRemaining);
}
bool Servatrice_DatabaseInterface::checkUserIsNameBanned(const QString &userName, QString &banReason, int &banSecondsRemaining)
{
QSqlQuery *nameBanQuery = prepareQuery("select time_to_sec(timediff(now(), date_add(b.time_from, interval b.minutes minute))), b.minutes <=> 0, b.visible_reason from {prefix}_bans b where b.time_from = (select max(c.time_from) from {prefix}_bans c where c.user_name = :name2) and b.user_name = :name1");
nameBanQuery->bindValue(":name1", userName);
nameBanQuery->bindValue(":name2", userName);
if (!execSqlQuery(nameBanQuery)) {
qDebug() << "Name ban check failed: SQL error" << nameBanQuery->lastError();
return false;
}
if (nameBanQuery->next()) {
const int secondsLeft = -nameBanQuery->value(0).toInt();
const bool permanentBan = nameBanQuery->value(1).toInt();
if ((secondsLeft > 0) || permanentBan) {
banReason = nameBanQuery->value(2).toString();
banSecondsRemaining = permanentBan ? 0 : secondsLeft;
qDebug() << "Username" << userName << "is banned by name";
return true;
}
}
return false;
}
bool Servatrice_DatabaseInterface::checkUserIsIpBanned(const QString &ipAddress, QString &banReason, int &banSecondsRemaining)
{
QSqlQuery *ipBanQuery = prepareQuery(
"select"
" time_to_sec(timediff(now(), date_add(b.time_from, interval b.minutes minute))),"
" b.minutes <=> 0,"
" b.visible_reason"
" from {prefix}_bans b"
" where"
" b.time_from = (select max(c.time_from)"
" from {prefix}_bans c"
" where c.ip_address = :address)"
" and b.ip_address = :address2");
ipBanQuery->bindValue(":address", ipAddress);
ipBanQuery->bindValue(":address2", ipAddress);
if (!execSqlQuery(ipBanQuery)) {
qDebug() << "IP ban check failed: SQL error." << ipBanQuery->lastError();
return false;
}
if (ipBanQuery->next()) {
const int secondsLeft = -ipBanQuery->value(0).toInt();
const bool permanentBan = ipBanQuery->value(1).toInt();
if ((secondsLeft > 0) || permanentBan) {
banReason = ipBanQuery->value(2).toString();
banSecondsRemaining = permanentBan ? 0 : secondsLeft;
qDebug() << "User is banned by address" << ipAddress;
return true;
}
}
return false;
}
bool Servatrice_DatabaseInterface::activeUserExists(const QString &user)
{
if (server->getAuthenticationMethod() == Servatrice::AuthenticationSql) {
checkSql();
@ -202,6 +326,20 @@ bool Servatrice_DatabaseInterface::userExists(const QString &user)
return false;
}
bool Servatrice_DatabaseInterface::userExists(const QString &user)
{
if (server->getAuthenticationMethod() == Servatrice::AuthenticationSql) {
checkSql();
QSqlQuery *query = prepareQuery("select 1 from {prefix}_users where name = :name");
query->bindValue(":name", user);
if (!execSqlQuery(query))
return false;
return query->next();
}
return false;
}
int Servatrice_DatabaseInterface::getUserIdInDB(const QString &name)
{
if (server->getAuthenticationMethod() == Servatrice::AuthenticationSql) {
@ -579,4 +717,4 @@ void Servatrice_DatabaseInterface::logMessage(const int senderId, const QString
query->bindValue(":target_id", (targetType == MessageTargetChat && targetId < 1) ? QVariant() : targetId);
query->bindValue(":target_name", targetName);
execSqlQuery(query);
}
}

View file

@ -4,6 +4,7 @@
#include <QObject>
#include <QSqlDatabase>
#include <QHash>
#include <QChar>
#include "server.h"
#include "server_database_interface.h"
@ -18,7 +19,11 @@ private:
QHash<QString, QSqlQuery *> preparedStatements;
Servatrice *server;
ServerInfo_User evalUserQueryResult(const QSqlQuery *query, bool complete, bool withId = false);
bool usernameIsValid(const QString &user);
/** Must be called after checkSql and server is known to be in auth mode. */
bool checkUserIsIpBanned(const QString &ipAddress, QString &banReason, int &banSecondsRemaining);
/** Must be called after checkSql and server is known to be in auth mode. */
bool checkUserIsNameBanned(QString const &userName, QString &banReason, int &banSecondsRemaining);
QChar getGenderChar(ServerInfo_User_Gender const &gender);
protected:
AuthenticationResult checkUserPassword(Server_ProtocolHandler *handler, const QString &user, const QString &password, QString &reasonStr, int &secondsLeft);
public slots:
@ -26,13 +31,15 @@ public slots:
public:
Servatrice_DatabaseInterface(int _instanceId, Servatrice *_server);
~Servatrice_DatabaseInterface();
void initDatabase(const QString &type, const QString &hostName, const QString &databaseName, const QString &userName, const QString &password);
bool initDatabase(const QString &type, const QString &hostName, const QString &databaseName,
const QString &userName, const QString &password);
bool openDatabase();
bool checkSql();
QSqlQuery * prepareQuery(const QString &queryText);
bool execSqlQuery(QSqlQuery *query);
const QSqlDatabase &getDatabase() { return sqlDatabase; }
bool activeUserExists(const QString &user);
bool userExists(const QString &user);
int getUserIdInDB(const QString &name);
QMap<QString, ServerInfo_User> getBuddyList(const QString &name);
@ -53,8 +60,12 @@ public:
void lockSessionTables();
void unlockSessionTables();
bool userSessionExists(const QString &userName);
bool usernameIsValid(const QString &user);
bool checkUserIsBanned(const QString &ipAddress, const QString &userName, QString &banReason, int &banSecondsRemaining);
bool getRequireRegistration();
bool registerUser(const QString &userName, const QString &realName, ServerInfo_User_Gender const &gender, const QString &password, const QString &emailAddress, const QString &country, QString &token, bool active = false);
bool activateUser(const QString &userName, const QString &token);
void logMessage(const int senderId, const QString &senderName, const QString &senderIp, const QString &logMessage, LogMessage_TargetType targetType, const int targetId, const QString &targetName);
};

View file

@ -51,12 +51,15 @@
#include "pb/response_deck_list.pb.h"
#include "pb/response_deck_download.pb.h"
#include "pb/response_deck_upload.pb.h"
#include "pb/response_register.pb.h"
#include "pb/response_replay_list.pb.h"
#include "pb/response_replay_download.pb.h"
#include "pb/serverinfo_replay.pb.h"
#include "pb/serverinfo_user.pb.h"
#include "pb/serverinfo_deckstorage.pb.h"
#include "smtp/SmtpMime"
#include "version_string.h"
#include <string>
#include <iostream>
@ -267,6 +270,8 @@ Response::ResponseCode ServerSocketInterface::processExtendedSessionCommand(int
case SessionCommand::REPLAY_DOWNLOAD: return cmdReplayDownload(cmd.GetExtension(Command_ReplayDownload::ext), rc);
case SessionCommand::REPLAY_MODIFY_MATCH: return cmdReplayModifyMatch(cmd.GetExtension(Command_ReplayModifyMatch::ext), rc);
case SessionCommand::REPLAY_DELETE_MATCH: return cmdReplayDeleteMatch(cmd.GetExtension(Command_ReplayDeleteMatch::ext), rc);
case SessionCommand::REGISTER: return cmdRegisterAccount(cmd.GetExtension(Command_Register::ext), rc); break;
case SessionCommand::ACTIVATE: return cmdActivateAccount(cmd.GetExtension(Command_Activate::ext), rc); break;
default: return Response::RespFunctionNotAllowed;
}
}
@ -764,6 +769,179 @@ Response::ResponseCode ServerSocketInterface::cmdBanFromServer(const Command_Ban
return Response::RespOk;
}
Response::ResponseCode ServerSocketInterface::cmdRegisterAccount(const Command_Register &cmd, ResponseContainer &rc)
{
QString userName = QString::fromStdString(cmd.user_name());
qDebug() << "Got register command: " << userName;
bool registrationEnabled = settingsCache->value("registration/enabled", false).toBool();
if (!registrationEnabled)
return Response::RespRegistrationDisabled;
QString emailAddress = QString::fromStdString(cmd.email());
bool requireEmailForRegistration = settingsCache->value("registration/requireemail", true).toBool();
if (requireEmailForRegistration)
{
QRegExp rx("\\b[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\\.[A-Za-z]{2,4}\\b");
if(emailAddress.isEmpty() || !rx.exactMatch(emailAddress))
return Response::RespEmailRequiredToRegister;
}
// TODO: Move this method outside of the db interface
if (!sqlInterface->usernameIsValid(userName))
return Response::RespUsernameInvalid;
if(sqlInterface->userExists(userName))
return Response::RespUserAlreadyExists;
QString banReason;
int banSecondsRemaining;
if (sqlInterface->checkUserIsBanned(this->getAddress(), userName, banReason, banSecondsRemaining))
{
Response_Register *re = new Response_Register;
re->set_denied_reason_str(banReason.toStdString());
if (banSecondsRemaining != 0)
re->set_denied_end_time(QDateTime::currentDateTime().addSecs(banSecondsRemaining).toTime_t());
rc.setResponseExtension(re);
return Response::RespUserIsBanned;
}
if (tooManyRegistrationAttempts(this->getAddress()))
return Response::RespTooManyRequests;
QString realName = QString::fromStdString(cmd.real_name());
ServerInfo_User_Gender gender = cmd.gender();
QString country = QString::fromStdString(cmd.country());
QString password = QString::fromStdString(cmd.password());
// TODO make this configurable?
if(password.length() < 6)
return Response::RespPasswordTooShort;
QString token;
bool regSucceeded = sqlInterface->registerUser(userName, realName, gender, password, emailAddress, country, token, !requireEmailForRegistration);
if(regSucceeded)
{
qDebug() << "Accepted register command for user: " << userName;
if(requireEmailForRegistration)
{
// TODO call a slot on another thread to send email
sendActivationTokenMail(userName, emailAddress, token);
return Response::RespRegistrationAcceptedNeedsActivation;
} else {
return Response::RespRegistrationAccepted;
}
} else {
return Response::RespRegistrationFailed;
}
}
bool ServerSocketInterface::sendActivationTokenMail(const QString &nickname, const QString &recipient, const QString &token)
{
QString tmp = settingsCache->value("smtp/connection", "tcp").toString();
SmtpClient::ConnectionType connection = SmtpClient::TcpConnection;
if(tmp == "ssl")
connection = SmtpClient::SslConnection;
else if(tmp == "tls")
connection = SmtpClient::TlsConnection;
tmp = settingsCache->value("smtp/auth", "plain").toString();
SmtpClient::AuthMethod auth = SmtpClient::AuthPlain;
if(tmp == "login")
auth = SmtpClient::AuthLogin;
QString host = settingsCache->value("smtp/host", "localhost").toString();
int port = settingsCache->value("smtp/port", 25).toInt();
QString username = settingsCache->value("smtp/username", "").toString();
QString password = settingsCache->value("smtp/password", "").toString();
QString email = settingsCache->value("smtp/email", "").toString();
QString name = settingsCache->value("smtp/name", "").toString();
QString subject = settingsCache->value("smtp/subject", "").toString();
QString body = settingsCache->value("smtp/body", "").toString();
if(email.isEmpty())
{
qDebug() << "[MAIL] Missing email field" << endl;
return false;
}
if(body.isEmpty())
{
qDebug() << "[MAIL] Missing body field" << endl;
return false;
}
if(recipient.isEmpty())
{
qDebug() << "[MAIL] Missing recipient field" << endl;
return false;
}
if(token.isEmpty())
{
qDebug() << "[MAIL] Missing token field" << endl;
return false;
}
SmtpClient smtp(host, port, connection);
smtp.setUser(username);
smtp.setPassword(password);
smtp.setAuthMethod(auth);
MimeMessage message;
EmailAddress sender(email, name);
message.setSender(&sender);
EmailAddress to(recipient, nickname);
message.addRecipient(&to);
message.setSubject(subject);
MimeText text;
text.setText(body.replace("%username", nickname).replace("%token", token));
message.addPart(&text);
// Now we can send the mail
if (!smtp.connectToHost()) {
qDebug() << "[MAIL] Failed to connect to host" << host << "on port" << port;
return false;
}
if (!smtp.login()) {
qDebug() << "[MAIL] Failed to login as " << username;
return false;
}
if (!smtp.sendMail(message)) {
qDebug() << "[MAIL] Failed to send mail to " << recipient;
return false;
}
smtp.quit();
qDebug() << "[MAIL] Sent activation email to " << recipient << " for nickname " << nickname;
return true;
}
bool ServerSocketInterface::tooManyRegistrationAttempts(const QString &ipAddress)
{
// TODO: implement
Q_UNUSED(ipAddress);
return false;
}
Response::ResponseCode ServerSocketInterface::cmdActivateAccount(const Command_Activate &cmd, ResponseContainer & /*rc*/)
{
QString userName = QString::fromStdString(cmd.user_name());
QString token = QString::fromStdString(cmd.token());
if(sqlInterface->activateUser(userName, token))
{
qDebug() << "Accepted activation for user" << QString::fromStdString(cmd.user_name());
return Response::RespActivationAccepted;
} else {
qDebug() << "Failed activation for user" << QString::fromStdString(cmd.user_name());
return Response::RespActivationFailed;
}
}
// ADMIN FUNCTIONS.
// Permission is checked by the calling function.

View file

@ -59,6 +59,7 @@ signals:
void outputQueueChanged();
protected:
void logDebugMessage(const QString &message);
bool tooManyRegistrationAttempts(const QString &ipAddress);
private:
QMutex outputQueueMutex;
Servatrice *servatrice;
@ -91,10 +92,14 @@ private:
Response::ResponseCode cmdBanFromServer(const Command_BanFromServer &cmd, ResponseContainer &rc);
Response::ResponseCode cmdShutdownServer(const Command_ShutdownServer &cmd, ResponseContainer &rc);
Response::ResponseCode cmdUpdateServerMessage(const Command_UpdateServerMessage &cmd, ResponseContainer &rc);
Response::ResponseCode cmdRegisterAccount(const Command_Register &cmd, ResponseContainer &rc);
Response::ResponseCode cmdActivateAccount(const Command_Activate &cmd, ResponseContainer & /* rc */);
Response::ResponseCode processExtendedSessionCommand(int cmdType, const SessionCommand &cmd, ResponseContainer &rc);
Response::ResponseCode processExtendedModeratorCommand(int cmdType, const ModeratorCommand &cmd, ResponseContainer &rc);
Response::ResponseCode processExtendedAdminCommand(int cmdType, const AdminCommand &cmd, ResponseContainer &rc);
bool sendActivationTokenMail(const QString &nickname, const QString &recipient, const QString &token);
public:
ServerSocketInterface(Servatrice *_server, Servatrice_DatabaseInterface *_databaseInterface, QObject *parent = 0);
~ServerSocketInterface();

View file

@ -0,0 +1,31 @@
/*
Copyright (c) 2011-2012 - Tőkés Attila
This file is part of SmtpClient for Qt.
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
See the LICENSE file for more details.
*/
#ifndef SMTPMIME_H
#define SMTPMIME_H
#include "smtpclient.h"
#include "mimepart.h"
#include "mimehtml.h"
#include "mimeattachment.h"
#include "mimemessage.h"
#include "mimetext.h"
#include "mimeinlinefile.h"
#include "mimefile.h"
#endif // SMTPMIME_H

View file

@ -0,0 +1,60 @@
/*
Copyright (c) 2011-2012 - Tőkés Attila
This file is part of SmtpClient for Qt.
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
See the LICENSE file for more details.
*/
#include "emailaddress.h"
/* [1] Constructors and Destructors */
EmailAddress::EmailAddress(const QString & address, const QString & name)
{
this->address = address;
this->name = name;
}
EmailAddress::~EmailAddress()
{
}
/* [1] --- */
/* [2] Getters and Setters */
void EmailAddress::setName(const QString & name)
{
this->name = name;
}
void EmailAddress::setAddress(const QString & address)
{
this->address = address;
}
const QString & EmailAddress::getName() const
{
return name;
}
const QString & EmailAddress::getAddress() const
{
return address;
}
/* [2] --- */

View file

@ -0,0 +1,61 @@
/*
Copyright (c) 2011-2012 - Tőkés Attila
This file is part of SmtpClient for Qt.
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
See the LICENSE file for more details.
*/
#ifndef EMAILADDRESS_H
#define EMAILADDRESS_H
#include <QObject>
#include "smtpexports.h"
class SMTP_EXPORT EmailAddress : public QObject
{
Q_OBJECT
public:
/* [1] Constructors and Destructors */
EmailAddress();
EmailAddress(const QString & address, const QString & name="");
~EmailAddress();
/* [1] --- */
/* [2] Getters and Setters */
void setName(const QString & name);
void setAddress(const QString & address);
const QString & getName() const;
const QString & getAddress() const;
/* [2] --- */
private:
/* [3] Private members */
QString name;
QString address;
/* [3] --- */
};
#endif // EMAILADDRESS_H

View file

@ -0,0 +1,50 @@
/*
Copyright (c) 2011-2012 - Tőkés Attila
This file is part of SmtpClient for Qt.
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
See the LICENSE file for more details.
*/
#include "mimeattachment.h"
#include <QFileInfo>
/* [1] Constructors and Destructors */
MimeAttachment::MimeAttachment(QFile *file)
: MimeFile(file)
{
}
MimeAttachment::MimeAttachment(const QByteArray& stream, const QString& fileName): MimeFile(stream, fileName)
{
}
MimeAttachment::~MimeAttachment()
{
}
/* [1] --- */
/* [2] Protected methods */
void MimeAttachment::prepare()
{
this->header += "Content-disposition: attachment\r\n";
/* !!! IMPORTANT !!! */
MimeFile::prepare();
}
/* [2] --- */

View file

@ -0,0 +1,51 @@
/*
Copyright (c) 2011-2012 - Tőkés Attila
This file is part of SmtpClient for Qt.
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
See the LICENSE file for more details.
*/
#ifndef MIMEATTACHMENT_H
#define MIMEATTACHMENT_H
#include <QFile>
#include "mimepart.h"
#include "mimefile.h"
#include "smtpexports.h"
class SMTP_EXPORT MimeAttachment : public MimeFile
{
Q_OBJECT
public:
/* [1] Constructors and Destructors */
MimeAttachment(QFile* file);
MimeAttachment(const QByteArray& stream, const QString& fileName);
~MimeAttachment();
/* [1] --- */
protected:
/* [2] Protected methods */
virtual void prepare();
/* [2] --- */
};
#endif // MIMEATTACHMENT_H

View file

@ -0,0 +1,66 @@
/*
Copyright (c) 2011-2012 - Tőkés Attila
This file is part of SmtpClient for Qt.
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
See the LICENSE file for more details.
*/
#include "mimecontentformatter.h"
MimeContentFormatter::MimeContentFormatter(int max_length) :
max_length(max_length)
{}
QString MimeContentFormatter::format(const QString &content, bool quotedPrintable) const {
QString out;
int chars = 0;
for (int i = 0; i < content.length() ; ++i) {
chars++;
if (!quotedPrintable) {
if (chars > max_length) {
out.append("\r\n");
chars = 1;
}
}
else {
if (content[i] == '\n') { // new line
out.append(content[i]);
chars = 0;
continue;
}
if ((chars > max_length - 1)
|| ((content[i] == '=') && (chars > max_length - 3) )) {
out.append('=');
out.append("\r\n");
chars = 1;
}
}
out.append(content[i]);
}
return out;
}
void MimeContentFormatter::setMaxLength(int l) {
max_length = l;
}
int MimeContentFormatter::getMaxLength() const {
return max_length;
}

View file

@ -0,0 +1,43 @@
/*
Copyright (c) 2011-2012 - Tőkés Attila
This file is part of SmtpClient for Qt.
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
See the LICENSE file for more details.
*/
#ifndef MIMECONTENTFORMATTER_H
#define MIMECONTENTFORMATTER_H
#include <QObject>
#include <QByteArray>
#include "smtpexports.h"
class SMTP_EXPORT MimeContentFormatter : public QObject
{
Q_OBJECT
public:
MimeContentFormatter (int max_length = 76);
void setMaxLength(int l);
int getMaxLength() const;
QString format(const QString &content, bool quotedPrintable = false) const;
protected:
int max_length;
};
#endif // MIMECONTENTFORMATTER_H

View file

@ -0,0 +1,70 @@
/*
Copyright (c) 2011-2012 - Tőkés Attila
This file is part of SmtpClient for Qt.
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
See the LICENSE file for more details.
*/
#include "mimefile.h"
#include <QFileInfo>
/* [1] Constructors and Destructors */
MimeFile::MimeFile(QFile *file)
{
this->file = file;
this->cType = "application/octet-stream";
this->cName = QFileInfo(*file).fileName();
this->cEncoding = Base64;
}
MimeFile::MimeFile(const QByteArray& stream, const QString& fileName)
{
this->cEncoding = Base64;
this->cType = "application/octet-stream";
this->file = 0;
this->cName = fileName;
this->content = stream;
}
MimeFile::~MimeFile()
{
if (file)
delete file;
}
/* [1] --- */
/* [2] Getters and setters */
/* [2] --- */
/* [3] Protected methods */
void MimeFile::prepare()
{
if (this->file)
{
file->open(QIODevice::ReadOnly);
this->content = file->readAll();
file->close();
}
/* !!! IMPORTANT !!!! */
MimePart::prepare();
}
/* [3] --- */

View file

@ -0,0 +1,62 @@
/*
Copyright (c) 2011-2012 - Tőkés Attila
This file is part of SmtpClient for Qt.
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
See the LICENSE file for more details.
*/
#ifndef MIMEFILE_H
#define MIMEFILE_H
#include "mimepart.h"
#include <QFile>
#include "smtpexports.h"
class SMTP_EXPORT MimeFile : public MimePart
{
Q_OBJECT
public:
/* [1] Constructors and Destructors */
MimeFile(const QByteArray& stream, const QString& fileName);
MimeFile(QFile *f);
~MimeFile();
/* [1] --- */
/* [2] Getters and Setters */
/* [2] --- */
protected:
/* [3] Protected members */
QFile* file;
/* [3] --- */
/* [4] Protected methods */
virtual void prepare();
/* [4] --- */
};
#endif // MIMEFILE_H

View file

@ -0,0 +1,57 @@
/*
Copyright (c) 2011-2012 - Tőkés Attila
This file is part of SmtpClient for Qt.
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
See the LICENSE file for more details.
*/
#include "mimehtml.h"
/* [1] Constructors and Destructors */
MimeHtml::MimeHtml(const QString &html) : MimeText(html)
{
this->cType = "text/html";
}
MimeHtml::~MimeHtml() {}
/* [1] --- */
/* [2] Getters and Setters */
void MimeHtml::setHtml(const QString & html)
{
this->text = html;
}
const QString & MimeHtml::getHtml() const
{
return text;
}
/* [2] --- */
/* [3] Protected methods */
void MimeHtml::prepare()
{
/* !!! IMPORTANT !!! */
MimeText::prepare();
}
/* [3] --- */

View file

@ -0,0 +1,61 @@
/*
Copyright (c) 2011-2012 - Tőkés Attila
This file is part of SmtpClient for Qt.
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
See the LICENSE file for more details.
*/
#ifndef MIMEHTML_H
#define MIMEHTML_H
#include "mimetext.h"
#include "smtpexports.h"
class SMTP_EXPORT MimeHtml : public MimeText
{
Q_OBJECT
public:
/* [1] Constructors and Destructors */
MimeHtml(const QString &html = "");
~MimeHtml();
/* [1] --- */
/* [2] Getters and Setters */
void setHtml(const QString & html);
const QString& getHtml() const;
/* [2] --- */
protected:
/* [3] Protected members */
/* [3] --- */
/* [4] Protected methods */
virtual void prepare();
/* [4] --- */
};
#endif // MIMEHTML_H

View file

@ -0,0 +1,52 @@
/*
Copyright (c) 2011-2012 - Tőkés Attila
This file is part of SmtpClient for Qt.
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
See the LICENSE file for more details.
*/
#include "mimeinlinefile.h"
/* [1] Constructors and Destructors */
MimeInlineFile::MimeInlineFile(QFile *f)
: MimeFile(f)
{
}
MimeInlineFile::~MimeInlineFile()
{}
/* [1] --- */
/* [2] Getters and Setters */
/* [2] --- */
/* [3] Protected methods */
void MimeInlineFile::prepare()
{
this->header += "Content-Disposition: inline\r\n";
/* !!! IMPORTANT !!! */
MimeFile::prepare();
}
/* [3] --- */

View file

@ -0,0 +1,56 @@
/*
Copyright (c) 2011-2012 - Tőkés Attila
This file is part of SmtpClient for Qt.
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
See the LICENSE file for more details.
*/
#ifndef MIMEINLINEFILE_H
#define MIMEINLINEFILE_H
#include "mimefile.h"
#include "smtpexports.h"
class SMTP_EXPORT MimeInlineFile : public MimeFile
{
public:
/* [1] Constructors and Destructors */
MimeInlineFile(QFile *f);
~MimeInlineFile();
/* [1] --- */
/* [2] Getters and Setters */
/* [2] --- */
protected:
/* [3] Protected members */
/* [3] --- */
/* [4] Protected methods */
virtual void prepare();
/* [4] --- */
};
#endif // MIMEINLINEFILE_H

View file

@ -0,0 +1,257 @@
/*
Copyright (c) 2011-2012 - Tőkés Attila
This file is part of SmtpClient for Qt.
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
See the LICENSE file for more details.
*/
#include "mimemessage.h"
#include <QDateTime>
#include "quotedprintable.h"
#include <typeinfo>
/* [1] Constructors and Destructors */
MimeMessage::MimeMessage(bool createAutoMimeContent) :
hEncoding(MimePart::_8Bit)
{
if (createAutoMimeContent)
this->content = new MimeMultiPart();
autoMimeContentCreated = createAutoMimeContent;
}
MimeMessage::~MimeMessage()
{
if (this->autoMimeContentCreated)
{
this->autoMimeContentCreated = false;
delete (this->content);
}
}
/* [1] --- */
/* [2] Getters and Setters */
MimePart& MimeMessage::getContent() {
return *content;
}
void MimeMessage::setContent(MimePart *content) {
if (this->autoMimeContentCreated)
{
this->autoMimeContentCreated = false;
delete (this->content);
}
this->content = content;
}
void MimeMessage::setSender(EmailAddress* e)
{
this->sender = e;
}
void MimeMessage::addRecipient(EmailAddress* rcpt, RecipientType type)
{
switch (type)
{
case To:
recipientsTo << rcpt;
break;
case Cc:
recipientsCc << rcpt;
break;
case Bcc:
recipientsBcc << rcpt;
break;
}
}
void MimeMessage::addTo(EmailAddress* rcpt) {
this->recipientsTo << rcpt;
}
void MimeMessage::addCc(EmailAddress* rcpt) {
this->recipientsCc << rcpt;
}
void MimeMessage::addBcc(EmailAddress* rcpt) {
this->recipientsBcc << rcpt;
}
void MimeMessage::setSubject(const QString & subject)
{
this->subject = subject;
}
void MimeMessage::addPart(MimePart *part)
{
if (typeid(*content) == typeid(MimeMultiPart)) {
((MimeMultiPart*) content)->addPart(part);
};
}
void MimeMessage::setHeaderEncoding(MimePart::Encoding hEnc)
{
this->hEncoding = hEnc;
}
const EmailAddress & MimeMessage::getSender() const
{
return *sender;
}
const QList<EmailAddress*> & MimeMessage::getRecipients(RecipientType type) const
{
switch (type)
{
default:
case To:
return recipientsTo;
case Cc:
return recipientsCc;
case Bcc:
return recipientsBcc;
}
}
const QString & MimeMessage::getSubject() const
{
return subject;
}
const QList<MimePart*> & MimeMessage::getParts() const
{
if (typeid(*content) == typeid(MimeMultiPart)) {
return ((MimeMultiPart*) content)->getParts();
}
else {
QList<MimePart*> *res = new QList<MimePart*>();
res->append(content);
return *res;
}
}
/* [2] --- */
/* [3] Public Methods */
QString MimeMessage::toString()
{
QString mime;
/* =========== MIME HEADER ============ */
/* ---------- Sender / From ----------- */
mime = "From:";
if (sender->getName() != "")
{
switch (hEncoding)
{
case MimePart::Base64:
mime += " =?utf-8?B?" + QByteArray().append(sender->getName()).toBase64() + "?=";
break;
case MimePart::QuotedPrintable:
mime += " =?utf-8?Q?" + QuotedPrintable::encode(QByteArray().append(sender->getName())).replace(' ', "_").replace(':',"=3A") + "?=";
break;
default:
mime += " " + sender->getName();
}
}
mime += " <" + sender->getAddress() + ">\r\n";
/* ---------------------------------- */
/* ------- Recipients / To ---------- */
mime += "To:";
QList<EmailAddress*>::iterator it; int i;
for (i = 0, it = recipientsTo.begin(); it != recipientsTo.end(); ++it, ++i)
{
if (i != 0) { mime += ","; }
if ((*it)->getName() != "")
{
switch (hEncoding)
{
case MimePart::Base64:
mime += " =?utf-8?B?" + QByteArray().append((*it)->getName()).toBase64() + "?=";
break;
case MimePart::QuotedPrintable:
mime += " =?utf-8?Q?" + QuotedPrintable::encode(QByteArray().append((*it)->getName())).replace(' ', "_").replace(':',"=3A") + "?=";
break;
default:
mime += " " + (*it)->getName();
}
}
mime += " <" + (*it)->getAddress() + ">";
}
mime += "\r\n";
/* ---------------------------------- */
/* ------- Recipients / Cc ---------- */
if (recipientsCc.size() != 0) {
mime += "Cc:";
}
for (i = 0, it = recipientsCc.begin(); it != recipientsCc.end(); ++it, ++i)
{
if (i != 0) { mime += ","; }
if ((*it)->getName() != "")
{
switch (hEncoding)
{
case MimePart::Base64:
mime += " =?utf-8?B?" + QByteArray().append((*it)->getName()).toBase64() + "?=";
break;
case MimePart::QuotedPrintable:
mime += " =?utf-8?Q?" + QuotedPrintable::encode(QByteArray().append((*it)->getName())).replace(' ', "_").replace(':',"=3A") + "?=";
break;
default:
mime += " " + (*it)->getName();
}
}
mime += " <" + (*it)->getAddress() + ">";
}
if (recipientsCc.size() != 0) {
mime += "\r\n";
}
/* ---------------------------------- */
/* ------------ Subject ------------- */
mime += "Subject: ";
switch (hEncoding)
{
case MimePart::Base64:
mime += "=?utf-8?B?" + QByteArray().append(subject).toBase64() + "?=";
break;
case MimePart::QuotedPrintable:
mime += "=?utf-8?Q?" + QuotedPrintable::encode(QByteArray().append(subject)).replace(' ', "_").replace(':',"=3A") + "?=";
break;
default:
mime += subject;
}
/* ---------------------------------- */
mime += "\r\n";
mime += "MIME-Version: 1.0\r\n";
mime += content->toString();
return mime;
}
/* [3] --- */

View file

@ -0,0 +1,92 @@
/*
Copyright (c) 2011-2012 - Tőkés Attila
This file is part of SmtpClient for Qt.
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
See the LICENSE file for more details.
*/
#ifndef MIMEMESSAGE_H
#define MIMEMESSAGE_H
#include "mimepart.h"
#include "mimemultipart.h"
#include "emailaddress.h"
#include <QList>
#include "smtpexports.h"
class SMTP_EXPORT MimeMessage : public QObject
{
public:
enum RecipientType {
To, // primary
Cc, // carbon copy
Bcc // blind carbon copy
};
/* [1] Constructors and Destructors */
MimeMessage(bool createAutoMimeConent = true);
~MimeMessage();
/* [1] --- */
/* [2] Getters and Setters */
void setSender(EmailAddress* e);
void addRecipient(EmailAddress* rcpt, RecipientType type = To);
void addTo(EmailAddress* rcpt);
void addCc(EmailAddress* rcpt);
void addBcc(EmailAddress* rcpt);
void setSubject(const QString & subject);
void addPart(MimePart* part);
void setHeaderEncoding(MimePart::Encoding);
const EmailAddress & getSender() const;
const QList<EmailAddress*> & getRecipients(RecipientType type = To) const;
const QString & getSubject() const;
const QList<MimePart*> & getParts() const;
MimePart& getContent();
void setContent(MimePart *content);
/* [2] --- */
/* [3] Public methods */
virtual QString toString();
/* [3] --- */
protected:
/* [4] Protected members */
EmailAddress* sender;
QList<EmailAddress*> recipientsTo, recipientsCc, recipientsBcc;
QString subject;
MimePart *content;
bool autoMimeContentCreated;
MimePart::Encoding hEncoding;
/* [4] --- */
};
#endif // MIMEMESSAGE_H

View file

@ -0,0 +1,78 @@
/*
Copyright (c) 2011-2012 - Tőkés Attila
This file is part of SmtpClient for Qt.
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
See the LICENSE file for more details.
*/
#include "mimemultipart.h"
#include <QTime>
#include <QCryptographicHash>
const QString MULTI_PART_NAMES[] = {
"multipart/mixed", // Mixed
"multipart/digest", // Digest
"multipart/alternative", // Alternative
"multipart/related", // Related
"multipart/report", // Report
"multipart/signed", // Signed
"multipart/encrypted" // Encrypted
};
MimeMultiPart::MimeMultiPart(MultiPartType type)
{
this->type = type;
this->cType = MULTI_PART_NAMES[this->type];
this->cEncoding = _8Bit;
QCryptographicHash md5(QCryptographicHash::Md5);
md5.addData(QByteArray().append(qrand()));
cBoundary = md5.result().toHex();
}
MimeMultiPart::~MimeMultiPart() {
}
void MimeMultiPart::addPart(MimePart *part) {
parts.append(part);
}
const QList<MimePart*> & MimeMultiPart::getParts() const {
return parts;
}
void MimeMultiPart::prepare() {
QList<MimePart*>::iterator it;
content = "";
for (it = parts.begin(); it != parts.end(); it++) {
content += "--" + cBoundary + "\r\n";
(*it)->prepare();
content += (*it)->toString();
};
content += "--" + cBoundary + "--\r\n";
MimePart::prepare();
}
void MimeMultiPart::setMimeType(const MultiPartType type) {
this->type = type;
this->cType = MULTI_PART_NAMES[type];
}
MimeMultiPart::MultiPartType MimeMultiPart::getMimeType() const {
return type;
}

View file

@ -0,0 +1,75 @@
/*
Copyright (c) 2011-2012 - Tőkés Attila
This file is part of SmtpClient for Qt.
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
See the LICENSE file for more details.
*/
#ifndef MIMEMULTIPART_H
#define MIMEMULTIPART_H
#include "mimepart.h"
#include "smtpexports.h"
class SMTP_EXPORT MimeMultiPart : public MimePart
{
Q_OBJECT
public:
/* [0] Enums */
enum MultiPartType {
Mixed = 0, // RFC 2046, section 5.1.3
Digest = 1, // RFC 2046, section 5.1.5
Alternative = 2, // RFC 2046, section 5.1.4
Related = 3, // RFC 2387
Report = 4, // RFC 6522
Signed = 5, // RFC 1847, section 2.1
Encrypted = 6 // RFC 1847, section 2.2
};
/* [0] --- */
/* [1] Constructors and Destructors */
MimeMultiPart(const MultiPartType type = Related);
~MimeMultiPart();
/* [1] --- */
/* [2] Getters and Setters */
void setMimeType(const MultiPartType type);
MultiPartType getMimeType() const;
const QList<MimePart*> & getParts() const;
/* [2] --- */
/* [3] Public methods */
void addPart(MimePart *part);
virtual void prepare();
/* [3] --- */
protected:
QList< MimePart* > parts;
MultiPartType type;
};
#endif // MIMEMULTIPART_H

View file

@ -0,0 +1,214 @@
/*
Copyright (c) 2011-2012 - Tőkés Attila
This file is part of SmtpClient for Qt.
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
See the LICENSE file for more details.
*/
#include "mimepart.h"
#include "quotedprintable.h"
/* [1] Constructors and Destructors */
MimePart::MimePart()
{
cEncoding = _7Bit;
prepared = false;
cBoundary = "";
}
MimePart::~MimePart()
{
return;
}
/* [1] --- */
/* [2] Getters and Setters */
void MimePart::setContent(const QByteArray & content)
{
this->content = content;
}
void MimePart::setHeader(const QString & header)
{
this->header = header;
}
void MimePart::addHeaderLine(const QString & line)
{
this->header += line + "\r\n";
}
const QString& MimePart::getHeader() const
{
return header;
}
const QByteArray& MimePart::getContent() const
{
return content;
}
void MimePart::setContentId(const QString & cId)
{
this->cId = cId;
}
const QString & MimePart::getContentId() const
{
return this->cId;
}
void MimePart::setContentName(const QString & cName)
{
this->cName = cName;
}
const QString & MimePart::getContentName() const
{
return this->cName;
}
void MimePart::setContentType(const QString & cType)
{
this->cType = cType;
}
const QString & MimePart::getContentType() const
{
return this->cType;
}
void MimePart::setCharset(const QString & charset)
{
this->cCharset = charset;
}
const QString & MimePart::getCharset() const
{
return this->cCharset;
}
void MimePart::setEncoding(Encoding enc)
{
this->cEncoding = enc;
}
MimePart::Encoding MimePart::getEncoding() const
{
return this->cEncoding;
}
MimeContentFormatter& MimePart::getContentFormatter()
{
return this->formatter;
}
/* [2] --- */
/* [3] Public methods */
QString MimePart::toString()
{
if (!prepared)
prepare();
return mimeString;
}
/* [3] --- */
/* [4] Protected methods */
void MimePart::prepare()
{
mimeString = QString();
/* === Header Prepare === */
/* Content-Type */
mimeString.append("Content-Type: ").append(cType);
if (cName != "")
mimeString.append("; name=\"").append(cName).append("\"");
if (cCharset != "")
mimeString.append("; charset=").append(cCharset);
if (cBoundary != "")
mimeString.append("; boundary=").append(cBoundary);
mimeString.append("\r\n");
/* ------------ */
/* Content-Transfer-Encoding */
mimeString.append("Content-Transfer-Encoding: ");
switch (cEncoding)
{
case _7Bit:
mimeString.append("7bit\r\n");
break;
case _8Bit:
mimeString.append("8bit\r\n");
break;
case Base64:
mimeString.append("base64\r\n");
break;
case QuotedPrintable:
mimeString.append("quoted-printable\r\n");
break;
}
/* ------------------------ */
/* Content-Id */
if (cId != NULL)
mimeString.append("Content-ID: <").append(cId).append(">\r\n");
/* ---------- */
/* Addition header lines */
mimeString.append(header).append("\r\n");
/* ------------------------- */
/* === End of Header Prepare === */
/* === Content === */
switch (cEncoding)
{
case _7Bit:
mimeString.append(QString(content).toLatin1());
break;
case _8Bit:
mimeString.append(content);
break;
case Base64:
mimeString.append(formatter.format(content.toBase64()));
break;
case QuotedPrintable:
mimeString.append(formatter.format(QuotedPrintable::encode(content), true));
break;
}
mimeString.append("\r\n");
/* === End of Content === */
prepared = true;
}
/* [4] --- */

View file

@ -0,0 +1,114 @@
/*
Copyright (c) 2011-2012 - Tőkés Attila
This file is part of SmtpClient for Qt.
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
See the LICENSE file for more details.
*/
#ifndef MIMEPART_H
#define MIMEPART_H
#include <QObject>
#include "mimecontentformatter.h"
#include "smtpexports.h"
class SMTP_EXPORT MimePart : public QObject
{
Q_OBJECT
public:
/* [0] Enumerations */
enum Encoding {
_7Bit,
_8Bit,
Base64,
QuotedPrintable
};
/* [0] --- */
/* [1] Constructors and Destructors */
MimePart();
~MimePart();
/* [1] --- */
/* [2] Getters and Setters */
const QString& getHeader() const;
const QByteArray& getContent() const;
void setContent(const QByteArray & content);
void setHeader(const QString & header);
void addHeaderLine(const QString & line);
void setContentId(const QString & cId);
const QString & getContentId() const;
void setContentName(const QString & cName);
const QString & getContentName() const;
void setContentType(const QString & cType);
const QString & getContentType() const;
void setCharset(const QString & charset);
const QString & getCharset() const;
void setEncoding(Encoding enc);
Encoding getEncoding() const;
MimeContentFormatter& getContentFormatter();
/* [2] --- */
/* [3] Public methods */
virtual QString toString();
virtual void prepare();
/* [3] --- */
protected:
/* [4] Protected members */
QString header;
QByteArray content;
QString cId;
QString cName;
QString cType;
QString cCharset;
QString cBoundary;
Encoding cEncoding;
QString mimeString;
bool prepared;
MimeContentFormatter formatter;
/* [4] --- */
};
#endif // MIMEPART_H

View file

@ -0,0 +1,62 @@
/*
Copyright (c) 2011-2012 - Tőkés Attila
This file is part of SmtpClient for Qt.
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
See the LICENSE file for more details.
*/
#include "mimetext.h"
/* [1] Constructors and Destructors */
MimeText::MimeText(const QString &txt)
{
this->text = txt;
this->cType = "text/plain";
this->cCharset = "utf-8";
this->cEncoding = _8Bit;
}
MimeText::~MimeText() { }
/* [1] --- */
/* [2] Getters and Setters */
void MimeText::setText(const QString & text)
{
this->text = text;
}
const QString & MimeText::getText() const
{
return text;
}
/* [2] --- */
/* [3] Protected Methods */
void MimeText::prepare()
{
this->content.clear();
this->content.append(text);
/* !!! IMPORTANT !!! */
MimePart::prepare();
}
/* [3] --- */

View file

@ -0,0 +1,62 @@
/*
Copyright (c) 2011-2012 - Tőkés Attila
This file is part of SmtpClient for Qt.
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
See the LICENSE file for more details.
*/
#ifndef MIMETEXT_H
#define MIMETEXT_H
#include "mimepart.h"
#include "smtpexports.h"
class SMTP_EXPORT MimeText : public MimePart
{
public:
/* [1] Constructors and Destructors */
MimeText(const QString &text = "");
~MimeText();
/* [1] --- */
/* [2] Getters and Setters*/
void setText(const QString & text);
const QString & getText() const;
/* [2] --- */
protected:
/* [3] Protected members */
QString text;
/* [3] --- */
/* [4] Protected methods */
void prepare();
/* [4] --- */
};
#endif // MIMETEXT_H

View file

@ -0,0 +1,69 @@
/*
Copyright (c) 2011-2012 - Tőkés Attila
This file is part of SmtpClient for Qt.
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
See the LICENSE file for more details.
*/
#include "quotedprintable.h"
QString QuotedPrintable::encode(const QByteArray &input)
{
QString output;
char byte;
const char hex[] = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F'};
for (int i = 0; i < input.length() ; ++i)
{
byte = input[i];
if ((byte == 0x20) || ((byte >= 33) && (byte <= 126) && (byte != 61)))
{
output.append(byte);
}
else
{
output.append('=');
output.append(hex[((byte >> 4) & 0x0F)]);
output.append(hex[(byte & 0x0F)]);
}
}
return output;
}
QByteArray QuotedPrintable::decode(const QString &input)
{
// 0 1 2 3 4 5 6 7 8 9 : ; < = > ? @ A B C D E F
const int hexVal[] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 0, 0, 0, 0, 0, 0, 10, 11, 12, 13, 14, 15};
QByteArray output;
for (int i = 0; i < input.length(); ++i)
{
if (input.at(i).toLatin1() == '=')
{
output.append((hexVal[input.at(i + 1).toLatin1() - '0'] << 4) + hexVal[input.at(i + 2).toLatin1() - '0']);
i += 2;
}
else
{
output.append(input.at(i).toLatin1());
}
}
return output;
}

View file

@ -0,0 +1,39 @@
/*
Copyright (c) 2011-2012 - Tőkés Attila
This file is part of SmtpClient for Qt.
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
See the LICENSE file for more details.
*/
#ifndef QUOTEDPRINTABLE_H
#define QUOTEDPRINTABLE_H
#include <QObject>
#include <QByteArray>
#include "smtpexports.h"
class SMTP_EXPORT QuotedPrintable : public QObject
{
Q_OBJECT
public:
static QString encode(const QByteArray &input);
static QByteArray decode(const QString &input);
private:
QuotedPrintable();
};
#endif // QUOTEDPRINTABLE_H

View file

@ -0,0 +1,482 @@
/*
Copyright (c) 2011-2012 - Tőkés Attila
This file is part of SmtpClient for Qt.
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
See the LICENSE file for more details.
*/
#include "smtpclient.h"
#include <QFileInfo>
#include <QByteArray>
/* [1] Constructors and destructors */
SmtpClient::SmtpClient(const QString & host, int port, ConnectionType connectionType) :
name("localhost"),
authMethod(AuthPlain),
connectionTimeout(5000),
responseTimeout(5000),
sendMessageTimeout(60000)
{
setConnectionType(connectionType);
this->host = host;
this->port = port;
connect(socket, SIGNAL(stateChanged(QAbstractSocket::SocketState)),
this, SLOT(socketStateChanged(QAbstractSocket::SocketState)));
connect(socket, SIGNAL(error(QAbstractSocket::SocketError)),
this, SLOT(socketError(QAbstractSocket::SocketError)));
connect(socket, SIGNAL(readyRead()),
this, SLOT(socketReadyRead()));
}
SmtpClient::~SmtpClient() {}
/* [1] --- */
/* [2] Getters and Setters */
void SmtpClient::setUser(const QString &user)
{
this->user = user;
}
void SmtpClient::setPassword(const QString &password)
{
this->password = password;
}
void SmtpClient::setAuthMethod(AuthMethod method)
{
this->authMethod = method;
}
void SmtpClient::setHost(const QString &host)
{
this->host = host;
}
void SmtpClient::setPort(int port)
{
this->port = port;
}
void SmtpClient::setConnectionType(ConnectionType ct)
{
this->connectionType = ct;
switch (connectionType)
{
case TcpConnection:
socket = new QTcpSocket(this);
break;
case SslConnection:
case TlsConnection:
socket = new QSslSocket(this);
}
}
const QString& SmtpClient::getHost() const
{
return this->host;
}
const QString& SmtpClient::getUser() const
{
return this->user;
}
const QString& SmtpClient::getPassword() const
{
return this->password;
}
SmtpClient::AuthMethod SmtpClient::getAuthMethod() const
{
return this->authMethod;
}
int SmtpClient::getPort() const
{
return this->port;
}
SmtpClient::ConnectionType SmtpClient::getConnectionType() const
{
return connectionType;
}
const QString& SmtpClient::getName() const
{
return this->name;
}
void SmtpClient::setName(const QString &name)
{
this->name = name;
}
const QString & SmtpClient::getResponseText() const
{
return responseText;
}
int SmtpClient::getResponseCode() const
{
return responseCode;
}
QTcpSocket* SmtpClient::getSocket() {
return socket;
}
int SmtpClient::getConnectionTimeout() const
{
return connectionTimeout;
}
void SmtpClient::setConnectionTimeout(int msec)
{
connectionTimeout = msec;
}
int SmtpClient::getResponseTimeout() const
{
return responseTimeout;
}
void SmtpClient::setResponseTimeout(int msec)
{
responseTimeout = msec;
}
int SmtpClient::getSendMessageTimeout() const
{
return sendMessageTimeout;
}
void SmtpClient::setSendMessageTimeout(int msec)
{
sendMessageTimeout = msec;
}
/* [2] --- */
/* [3] Public methods */
bool SmtpClient::connectToHost()
{
switch (connectionType)
{
case TlsConnection:
case TcpConnection:
socket->connectToHost(host, port);
break;
case SslConnection:
((QSslSocket*) socket)->connectToHostEncrypted(host, port);
break;
}
// Tries to connect to server
if (!socket->waitForConnected(connectionTimeout))
{
emit smtpError(ConnectionTimeoutError);
return false;
}
try
{
// Wait for the server's response
waitForResponse();
// If the response code is not 220 (Service ready)
// means that is something wrong with the server
if (responseCode != 220)
{
emit smtpError(ServerError);
return false;
}
// Send a EHLO/HELO message to the server
// The client's first command must be EHLO/HELO
sendMessage("EHLO " + name);
// Wait for the server's response
waitForResponse();
// The response code needs to be 250.
if (responseCode != 250) {
emit smtpError(ServerError);
return false;
}
if (connectionType == TlsConnection) {
// send a request to start TLS handshake
sendMessage("STARTTLS");
// Wait for the server's response
waitForResponse();
// The response code needs to be 220.
if (responseCode != 220) {
emit smtpError(ServerError);
return false;
};
((QSslSocket*) socket)->startClientEncryption();
if (!((QSslSocket*) socket)->waitForEncrypted(connectionTimeout)) {
qDebug() << ((QSslSocket*) socket)->errorString();
emit smtpError(ConnectionTimeoutError);
return false;
}
// Send ELHO one more time
sendMessage("EHLO " + name);
// Wait for the server's response
waitForResponse();
// The response code needs to be 250.
if (responseCode != 250) {
emit smtpError(ServerError);
return false;
}
}
}
catch (ResponseTimeoutException)
{
return false;
}
catch (SendMessageTimeoutException)
{
return false;
}
// If no errors occured the function returns true.
return true;
}
bool SmtpClient::login()
{
return login(user, password, authMethod);
}
bool SmtpClient::login(const QString &user, const QString &password, AuthMethod method)
{
try {
if (method == AuthPlain)
{
// Sending command: AUTH PLAIN base64('\0' + username + '\0' + password)
sendMessage("AUTH PLAIN " + QByteArray().append((char) 0).append(user).append((char) 0).append(password).toBase64());
// Wait for the server's response
waitForResponse();
// If the response is not 235 then the authentication was faild
if (responseCode != 235)
{
emit smtpError(AuthenticationFailedError);
return false;
}
}
else if (method == AuthLogin)
{
// Sending command: AUTH LOGIN
sendMessage("AUTH LOGIN");
// Wait for 334 response code
waitForResponse();
if (responseCode != 334) { emit smtpError(AuthenticationFailedError); return false; }
// Send the username in base64
sendMessage(QByteArray().append(user).toBase64());
// Wait for 334
waitForResponse();
if (responseCode != 334) { emit smtpError(AuthenticationFailedError); return false; }
// Send the password in base64
sendMessage(QByteArray().append(password).toBase64());
// Wait for the server's responce
waitForResponse();
// If the response is not 235 then the authentication was faild
if (responseCode != 235)
{
emit smtpError(AuthenticationFailedError);
return false;
}
}
}
catch (ResponseTimeoutException e)
{
// Responce Timeout exceeded
emit smtpError(AuthenticationFailedError);
return false;
}
catch (SendMessageTimeoutException)
{
// Send Timeout exceeded
emit smtpError(AuthenticationFailedError);
return false;
}
return true;
}
bool SmtpClient::sendMail(MimeMessage& email)
{
try
{
// Send the MAIL command with the sender
sendMessage("MAIL FROM: <" + email.getSender().getAddress() + ">");
waitForResponse();
if (responseCode != 250) return false;
// Send RCPT command for each recipient
QList<EmailAddress*>::const_iterator it, itEnd;
// To (primary recipients)
for (it = email.getRecipients().begin(), itEnd = email.getRecipients().end();
it != itEnd; ++it)
{
sendMessage("RCPT TO: <" + (*it)->getAddress() + ">");
waitForResponse();
if (responseCode != 250) return false;
}
// Cc (carbon copy)
for (it = email.getRecipients(MimeMessage::Cc).begin(), itEnd = email.getRecipients(MimeMessage::Cc).end();
it != itEnd; ++it)
{
sendMessage("RCPT TO: <" + (*it)->getAddress() + ">");
waitForResponse();
if (responseCode != 250) return false;
}
// Bcc (blind carbon copy)
for (it = email.getRecipients(MimeMessage::Bcc).begin(), itEnd = email.getRecipients(MimeMessage::Bcc).end();
it != itEnd; ++it)
{
sendMessage("RCPT TO: <" + (*it)->getAddress() + ">");
waitForResponse();
if (responseCode != 250) return false;
}
// Send DATA command
sendMessage("DATA");
waitForResponse();
if (responseCode != 354) return false;
sendMessage(email.toString());
// Send \r\n.\r\n to end the mail data
sendMessage(".");
waitForResponse();
if (responseCode != 250) return false;
}
catch (ResponseTimeoutException)
{
return false;
}
catch (SendMessageTimeoutException)
{
return false;
}
return true;
}
void SmtpClient::quit()
{
sendMessage("QUIT");
}
/* [3] --- */
/* [4] Protected methods */
void SmtpClient::waitForResponse() throw (ResponseTimeoutException)
{
do {
if (!socket->waitForReadyRead(responseTimeout))
{
emit smtpError(ResponseTimeoutError);
throw ResponseTimeoutException();
}
while (socket->canReadLine()) {
// Save the server's response
responseText = socket->readLine();
// Extract the respose code from the server's responce (first 3 digits)
responseCode = responseText.left(3).toInt();
if (responseCode / 100 == 4)
emit smtpError(ServerError);
if (responseCode / 100 == 5)
emit smtpError(ClientError);
if (responseText[3] == ' ') { return; }
}
} while (true);
}
void SmtpClient::sendMessage(const QString &text) throw (SendMessageTimeoutException)
{
socket->write(text.toUtf8() + "\r\n");
if (! socket->waitForBytesWritten(sendMessageTimeout))
{
emit smtpError(SendDataTimeoutError);
throw SendMessageTimeoutException();
}
}
/* [4] --- */
/* [5] Slots for the socket's signals */
void SmtpClient::socketStateChanged(QAbstractSocket::SocketState /* state */)
{
}
void SmtpClient::socketError(QAbstractSocket::SocketError /* socketError */)
{
}
void SmtpClient::socketReadyRead()
{
}
/* [5] --- */

View file

@ -0,0 +1,184 @@
/*
Copyright (c) 2011-2012 - Tőkés Attila
This file is part of SmtpClient for Qt.
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
See the LICENSE file for more details.
*/
#ifndef SMTPCLIENT_H
#define SMTPCLIENT_H
#include <QObject>
#include <QtNetwork/QSslSocket>
#include "mimemessage.h"
#include "smtpexports.h"
class SMTP_EXPORT SmtpClient : public QObject
{
Q_OBJECT
public:
/* [0] Enumerations */
enum AuthMethod
{
AuthPlain,
AuthLogin
};
enum SmtpError
{
ConnectionTimeoutError,
ResponseTimeoutError,
SendDataTimeoutError,
AuthenticationFailedError,
ServerError, // 4xx smtp error
ClientError // 5xx smtp error
};
enum ConnectionType
{
TcpConnection,
SslConnection,
TlsConnection // STARTTLS
};
/* [0] --- */
/* [1] Constructors and Destructors */
SmtpClient(const QString & host = "localhost", int port = 25, ConnectionType ct = TcpConnection);
~SmtpClient();
/* [1] --- */
/* [2] Getters and Setters */
const QString& getHost() const;
void setHost(const QString &host);
int getPort() const;
void setPort(int port);
const QString& getName() const;
void setName(const QString &name);
ConnectionType getConnectionType() const;
void setConnectionType(ConnectionType ct);
const QString & getUser() const;
void setUser(const QString &user);
const QString & getPassword() const;
void setPassword(const QString &password);
SmtpClient::AuthMethod getAuthMethod() const;
void setAuthMethod(AuthMethod method);
const QString & getResponseText() const;
int getResponseCode() const;
int getConnectionTimeout() const;
void setConnectionTimeout(int msec);
int getResponseTimeout() const;
void setResponseTimeout(int msec);
int getSendMessageTimeout() const;
void setSendMessageTimeout(int msec);
QTcpSocket* getSocket();
/* [2] --- */
/* [3] Public methods */
bool connectToHost();
bool login();
bool login(const QString &user, const QString &password, AuthMethod method = AuthLogin);
bool sendMail(MimeMessage& email);
void quit();
/* [3] --- */
protected:
/* [4] Protected members */
QTcpSocket *socket;
QString host;
int port;
ConnectionType connectionType;
QString name;
QString user;
QString password;
AuthMethod authMethod;
int connectionTimeout;
int responseTimeout;
int sendMessageTimeout;
QString responseText;
int responseCode;
class ResponseTimeoutException {};
class SendMessageTimeoutException {};
/* [4] --- */
/* [5] Protected methods */
void waitForResponse() throw (ResponseTimeoutException);
void sendMessage(const QString &text) throw (SendMessageTimeoutException);
/* [5] --- */
protected slots:
/* [6] Protected slots */
void socketStateChanged(QAbstractSocket::SocketState state);
void socketError(QAbstractSocket::SocketError error);
void socketReadyRead();
/* [6] --- */
signals:
/* [7] Signals */
void smtpError(SmtpClient::SmtpError e);
/* [7] --- */
};
#endif // SMTPCLIENT_H

View file

@ -0,0 +1,15 @@
#ifndef SMTPEXPORTS_H
#define SMTPEXPORTS_H
/*
#ifdef SMTP_BUILD
#define SMTP_EXPORT Q_DECL_EXPORT
#else
#define SMTP_EXPORT Q_DECL_IMPORT
#endif
*/
// Servatrice compiles this statically in, so there's no need to declare exports
#define SMTP_EXPORT
#endif // SMTPEXPORTS_H