Moved some parts to a static lib so both the executable and the tests can link to it.

Written additional tests.
This commit is contained in:
eelke 2017-02-26 19:29:50 +01:00
parent 0a809a7288
commit d0ea9dfa0c
39 changed files with 1767 additions and 493 deletions

View file

@ -57,7 +57,10 @@ ConnectionConfig::ConnectionConfig()
void ConnectionConfig::setName(std::string desc)
{
m_name = std::move(desc);
if (m_name != desc) {
m_dirty = true;
m_name = std::move(desc);
}
}
const std::string& ConnectionConfig::name() const
@ -67,7 +70,10 @@ const std::string& ConnectionConfig::name() const
void ConnectionConfig::setHost(std::string host)
{
m_host = std::move(host);
if (m_host != host) {
m_dirty = true;
m_host = std::move(host);
}
}
const std::string& ConnectionConfig::host() const
@ -77,7 +83,10 @@ const std::string& ConnectionConfig::host() const
void ConnectionConfig::setHostAddr(std::string v)
{
m_hostaddr = std::move(v);
if (m_hostaddr != v) {
m_dirty = true;
m_hostaddr = std::move(v);
}
}
const std::string& ConnectionConfig::hostAddr() const
@ -87,7 +96,11 @@ const std::string& ConnectionConfig::hostAddr() const
void ConnectionConfig::setPort(unsigned short port)
{
m_port = std::to_string(port);
auto p = std::to_string(port);
if (m_port != p) {
m_dirty = true;
m_port = p;
}
}
unsigned short ConnectionConfig::port() const
@ -97,7 +110,11 @@ unsigned short ConnectionConfig::port() const
void ConnectionConfig::setUser(std::string v)
{
m_user = std::move(v);
if (m_user != v) {
m_dirty = true;
m_user = std::move(v);
}
}
const std::string& ConnectionConfig::user() const
@ -107,7 +124,10 @@ const std::string& ConnectionConfig::user() const
void ConnectionConfig::setPassword(std::string v)
{
m_password = std::move(v);
if (m_password != v) {
m_dirty = true;
m_password = std::move(v);
}
}
const std::string& ConnectionConfig::password() const
@ -117,7 +137,10 @@ const std::string& ConnectionConfig::password() const
void ConnectionConfig::setDbname(std::string v)
{
m_dbname = std::move(v);
if (m_dbname != v) {
m_dirty = true;
m_dbname = std::move(v);
}
}
const std::string& ConnectionConfig::dbname() const
@ -127,7 +150,11 @@ const std::string& ConnectionConfig::dbname() const
void ConnectionConfig::setSslMode(SslMode m)
{
m_sslMode = SslModeToString(m);
auto v = SslModeToString(m);
if (m_sslMode != v) {
m_dirty = true;
m_sslMode = v;
}
}
SslMode ConnectionConfig::sslMode() const
@ -137,7 +164,10 @@ SslMode ConnectionConfig::sslMode() const
void ConnectionConfig::setSslCert(std::string v)
{
m_sslCert = std::move(v);
if (m_sslCert != v) {
m_dirty = true;
m_sslCert = std::move(v);
}
}
const std::string& ConnectionConfig::sslCert() const
@ -147,7 +177,10 @@ const std::string& ConnectionConfig::sslCert() const
void ConnectionConfig::setSslKey(std::string v)
{
m_sslKey = std::move(v);
if (m_sslKey != v) {
m_dirty = true;
m_sslKey = std::move(v);
}
}
const std::string& ConnectionConfig::sslKey() const
@ -157,7 +190,10 @@ const std::string& ConnectionConfig::sslKey() const
void ConnectionConfig::setSslRootCert(std::string v)
{
m_sslRootCert = std::move(v);
if (m_sslRootCert != v) {
m_dirty = true;
m_sslRootCert = std::move(v);
}
}
const std::string& ConnectionConfig::sslRootCert() const
@ -167,7 +203,10 @@ const std::string& ConnectionConfig::sslRootCert() const
void ConnectionConfig::setSslCrl(std::string v)
{
m_sslCrl = std::move(v);
if (m_sslCrl != v) {
m_dirty = true;
m_sslCrl = std::move(v);
}
}
const std::string& ConnectionConfig::sslCrl() const
@ -210,3 +249,13 @@ bool ConnectionConfig::isSameDatabase(const ConnectionConfig &rhs) const
&& m_password == rhs.m_password
&& m_dbname == rhs.m_dbname;
}
bool ConnectionConfig::dirty() const
{
return m_dirty;
}
void ConnectionConfig::clean()
{
m_dirty = false;
}

View file

@ -13,6 +13,12 @@ enum class SslMode {
verify_full=5
};
enum class PasswordMode {
Unsave,
Encrypted,
DontSave
};
class ConnectionConfig {
public:
ConnectionConfig();
@ -57,6 +63,9 @@ public:
const char * const * getValues() const;
bool isSameDatabase(const ConnectionConfig &rhs) const;
bool dirty() const;
void clean();
private:
std::string m_name;
std::string m_host;
@ -75,6 +84,8 @@ private:
std::string m_applicationName;
bool m_dirty = false;
static std::vector<const char*> s_keywords;
mutable std::vector<const char*> m_values;
};

148
src/ConnectionList.cpp Normal file
View file

@ -0,0 +1,148 @@
#include "ConnectionList.h"
#include "scopeguard.h"
#include "util.h"
#include <QDir>
#include <QStandardPaths>
#include <QSettings>
namespace {
/** Saves a connection configuration.
Before calling this you may want to call beginGroup.
*/
void SaveConnectionConfig(QSettings &settings, const ConnectionConfig &cc)
{
settings.setValue("name", stdStrToQ(cc.name()));
settings.setValue("host", stdStrToQ(cc.host()));
settings.setValue("hostaddr", stdStrToQ(cc.hostAddr()));
settings.setValue("port", cc.port());
settings.setValue("user", stdStrToQ(cc.user()));
settings.setValue("password", stdStrToQ(cc.password()));
settings.setValue("dbname", stdStrToQ(cc.dbname()));
settings.setValue("sslmode", (int)cc.sslMode());
settings.setValue("sslcert", stdStrToQ(cc.sslCert()));
settings.setValue("sslkey", stdStrToQ(cc.sslKey()));
settings.setValue("sslrootcert", stdStrToQ(cc.sslRootCert()));
settings.setValue("sslcrl", stdStrToQ(cc.sslCrl()));
}
void LoadConnectionConfig(QSettings &settings, ConnectionConfig &cc)
{
cc.setName(qvarToStdStr(settings.value("name")));
cc.setHost(qvarToStdStr(settings.value("host")));
cc.setHostAddr(qvarToStdStr(settings.value("hostaddr")));
cc.setPort(settings.value("port", 5432).toInt());
cc.setUser(qvarToStdStr(settings.value("user")));
// std::string encpw = qvarToStdStr(settings.value("encryptedpw"));
// if (encpw.empty()) {
cc.setPassword(qvarToStdStr(settings.value("password")));
// }
// else {
// cc.setEncryptedPassword(encpw);
// }
cc.setDbname(qvarToStdStr(settings.value("dbname")));
cc.setSslMode((SslMode)settings.value("sslmode").toInt());
cc.setSslCert(qvarToStdStr(settings.value("sslcert")));
cc.setSslKey(qvarToStdStr(settings.value("sslkey")));
cc.setSslRootCert(qvarToStdStr(settings.value("sslrootcert")));
cc.setSslCrl(qvarToStdStr(settings.value("sslcrl")));
}
} // end of unnamed namespace
/// \todo should return an expected as creation of the folder can fail
QString ConnectionList::iniFileName()
{
QString path = QStandardPaths::writableLocation(QStandardPaths::AppDataLocation);
QDir dir(path);
if (!dir.exists()) {
dir.mkpath(".");
}
path += "/connections.ini";
return path;
}
ConnectionList::ConnectionList()
{
}
int ConnectionList::createNew()
{
m_connections.emplace_back(QUuid::createUuid(), ConnectionConfig());
return m_connections.size()-1;
}
void ConnectionList::remove(int idx, int count)
{
auto f = m_connections.begin() + idx;
auto l = f + count;
deleteFromIni(f, l);
m_connections.erase(f, l);
}
void ConnectionList::deleteFromIni(t_Connections::iterator begin, t_Connections::iterator end)
{
QString file_name = iniFileName();
QSettings settings(file_name, QSettings::IniFormat);
for (auto i = begin; i != end; ++i) {
settings.remove(i->m_uuid.toString());
}
}
void ConnectionList::load()
{
QString file_name = iniFileName();
QSettings settings(file_name, QSettings::IniFormat);
auto groups = settings.childGroups();
for (auto grp : groups) {
if (grp == "c_IniGroupSecurity") {
// Read security settings
} else {
QUuid uuid(grp);
if ( ! uuid.isNull() ) {
settings.beginGroup(grp);
SCOPE_EXIT { settings.endGroup(); };
ConnectionConfig cc;
LoadConnectionConfig(settings, cc);
m_connections.emplace_back(uuid, cc);
}
}
}
}
void ConnectionList::save()
{
QString file_name = iniFileName();
QSettings settings(file_name, QSettings::IniFormat);
for (auto& e : m_connections) {
settings.beginGroup(e.m_uuid.toString());
SCOPE_EXIT { settings.endGroup(); };
SaveConnectionConfig(settings, e.m_config);
e.m_config.clean();
}
settings.sync();
}
void ConnectionList::save(int index)
{
if (index >= 0 && index < (int)m_connections.size()) {
auto& e = m_connections[index];
if (e.m_config.dirty()) {
QString file_name = iniFileName();
QSettings settings(file_name, QSettings::IniFormat);
settings.beginGroup(e.m_uuid.toString());
SaveConnectionConfig(settings, e.m_config);
e.m_config.clean();
settings.sync();
}
}
}

54
src/ConnectionList.h Normal file
View file

@ -0,0 +1,54 @@
#ifndef CONNECTIONLIST_H
#define CONNECTIONLIST_H
#include "ConnectionConfig.h"
#include <QString>
#include <QUuid>
#include <vector>
#include "Expected.h"
class ConnectionList {
private:
static QString iniFileName();
public:
ConnectionList();
int size() const { return m_connections.size(); }
ConnectionConfig& getConfigByIdx(int idx)
{
return m_connections.at(idx).m_config;
}
int createNew();
void remove(int idx, int count);
void load();
void save();
void save(int index);
private:
class LijstElem {
public:
QUuid m_uuid;
ConnectionConfig m_config;
LijstElem(const QUuid id, const ConnectionConfig &cfg)
: m_uuid(id), m_config(cfg)
{}
};
using t_Connections = std::vector<LijstElem>;
t_Connections m_connections;
void deleteFromIni(t_Connections::iterator begin, t_Connections::iterator end);
};
#endif // CONNECTIONLIST_H

View file

@ -1,74 +1,27 @@
#include "connectionlistmodel.h"
#include <QDir>
#include <QStandardPaths>
#include <QSettings>
#include "ConnectionListModel.h"
#include "ConnectionList.h"
#include "scopeguard.h"
#include "util.h"
inline QString stdStrToQ(const std::string &s)
{
return QString::fromUtf8(s.c_str());
}
inline std::string qStrToStd(const QString &s)
{
return std::string(s.toUtf8().data());
}
inline std::string qvarToStdStr(const QVariant &c)
{
return qStrToStd(c.toString());
}
#include <botan/cryptobox.h>
/** Saves a connection configuration.
Before calling this you may want to call beginGroup.
*/
void SaveConnectionConfig(QSettings &settings, const ConnectionConfig &cc)
{
settings.setValue("name", stdStrToQ(cc.name()));
settings.setValue("host", stdStrToQ(cc.host()));
settings.setValue("hostaddr", stdStrToQ(cc.hostAddr()));
settings.setValue("port", cc.port());
settings.setValue("user", stdStrToQ(cc.user()));
settings.setValue("password", stdStrToQ(cc.password()));
settings.setValue("dbname", stdStrToQ(cc.dbname()));
settings.setValue("sslmode", (int)cc.sslMode());
settings.setValue("sslcert", stdStrToQ(cc.sslCert()));
settings.setValue("sslkey", stdStrToQ(cc.sslKey()));
settings.setValue("sslrootcert", stdStrToQ(cc.sslRootCert()));
settings.setValue("sslcrl", stdStrToQ(cc.sslCrl()));
}
void LoadConnectionConfig(QSettings &settings, ConnectionConfig &cc)
{
cc.setName(qvarToStdStr(settings.value("name")));
cc.setHost(qvarToStdStr(settings.value("host")));
cc.setHostAddr(qvarToStdStr(settings.value("hostaddr")));
cc.setPort(settings.value("port", 5432).toInt());
cc.setUser(qvarToStdStr(settings.value("user")));
cc.setPassword(qvarToStdStr(settings.value("password")));
cc.setDbname(qvarToStdStr(settings.value("dbname")));
cc.setSslMode((SslMode)settings.value("sslmode").toInt());
cc.setSslCert(qvarToStdStr(settings.value("sslcert")));
cc.setSslKey(qvarToStdStr(settings.value("sslkey")));
cc.setSslRootCert(qvarToStdStr(settings.value("sslrootcert")));
cc.setSslCrl(qvarToStdStr(settings.value("sslcrl")));
}
ConnectionListModel::ConnectionListModel(QObject *parent)
ConnectionListModel::ConnectionListModel(ConnectionList *conns, QObject *parent)
: QAbstractListModel(parent)
, m_connections(conns)
{
load();
}
ConnectionListModel::~ConnectionListModel()
{
delete m_connections;
}
int ConnectionListModel::rowCount(const QModelIndex &parent) const
{
int result = 0;
if (parent == QModelIndex()) {
result = m_connections.size();
result = m_connections->size();
}
return result;
}
@ -84,7 +37,7 @@ QVariant ConnectionListModel::data(const QModelIndex &index, int role) const
if (role == Qt::DisplayRole || role == Qt::EditRole) {
int row = index.row();
int col = index.column();
const ConnectionConfig& cfg = m_connections.at(row).m_config;
const ConnectionConfig& cfg = m_connections->getConfigByIdx(row);
switch (col) {
case 0:
result = makeLongDescription(cfg);
@ -119,9 +72,10 @@ bool ConnectionListModel::setData(const QModelIndex &index, const QVariant &valu
if (role == Qt::EditRole) {
int row = index.row();
int col = index.column();
auto& elem = m_connections.at(row);
elem.m_dirty = true;
ConnectionConfig& cfg = elem.m_config;
// auto& elem = m_connections.at(row);
// elem.m_dirty = true;
// ConnectionConfig& cfg = elem.m_config;
ConnectionConfig& cfg = m_connections->getConfigByIdx(row);
if (col > 0) {
result = true;
}
@ -158,7 +112,7 @@ Qt::ItemFlags ConnectionListModel::flags(const QModelIndex &index) const
{
Qt::ItemFlags result;
int row = index.row();
if (row >= 0 && row < (int)m_connections.size()) {
if (row >= 0 && row < (int)m_connections->size()) {
result = Qt::ItemIsSelectable | Qt::ItemIsEditable | Qt::ItemIsEnabled;
}
return result;
@ -180,17 +134,17 @@ QString ConnectionListModel::makeLongDescription(const ConnectionConfig &cfg)
return stdStrToQ(result);
}
void ConnectionListModel::add(const ConnectionConfig &cfg)
void ConnectionListModel::newItem()
{
m_connections.emplace_back(QUuid::createUuid(), cfg);
auto idx = createIndex(m_connections.size()-1, 0);
int i = m_connections->createNew();
auto idx = createIndex(i, 0);
emit dataChanged(idx, idx);
}
Expected<ConnectionConfig> ConnectionListModel::get(int row)
{
if (row >= 0 && row < (int)m_connections.size()) {
return m_connections.at(row).m_config;
if (row >= 0 && row < (int)m_connections->size()) {
return m_connections->getConfigByIdx(row);
}
else {
return Expected<ConnectionConfig>::fromException(std::out_of_range("Invalid row"));
@ -201,85 +155,29 @@ Expected<ConnectionConfig> ConnectionListModel::get(int row)
bool ConnectionListModel::removeRows(int row, int count, const QModelIndex &parent)
{
bool result = false;
if (row >= 0 && row < (int)m_connections.size()) {
if (row >= 0 && row < (int)m_connections->size()) {
beginRemoveRows(parent, row, row + count -1);
SCOPE_EXIT { endRemoveRows(); };
auto f = m_connections.begin() + row;
auto l = f + count;
deleteFromIni(f, l);
m_connections.erase(f, l);
m_connections->remove(row, count);
result = true;
}
return result;
}
/// \todo should return an expected as creation of the folder can fail
QString ConnectionListModel::iniFileName()
{
QString path = QStandardPaths::writableLocation(QStandardPaths::AppDataLocation);
QDir dir(path);
if (!dir.exists()) {
dir.mkpath(".");
}
path += "/connections.ini";
return path;
}
void ConnectionListModel::load()
{
QString file_name = iniFileName();
QSettings settings(file_name, QSettings::IniFormat);
auto groups = settings.childGroups();
for (auto grp : groups) {
QUuid uuid(grp);
if ( ! uuid.isNull() ) {
settings.beginGroup(grp);
SCOPE_EXIT { settings.endGroup(); };
ConnectionConfig cc;
LoadConnectionConfig(settings, cc);
m_connections.emplace_back(uuid, cc);
}
}
}
//void ConnectionListModel::load()
//{
// m_connections->load();
//}
void ConnectionListModel::save()
{
QString file_name = iniFileName();
QSettings settings(file_name, QSettings::IniFormat);
for (auto& e : m_connections) {
settings.beginGroup(e.m_uuid.toString());
SCOPE_EXIT { settings.endGroup(); };
SaveConnectionConfig(settings, e.m_config);
}
m_connections->save();
}
void ConnectionListModel::save(int index)
{
if (index >= 0 && index < (int)m_connections.size()) {
auto& e = m_connections[index];
if (e.m_dirty) {
QString file_name = iniFileName();
QSettings settings(file_name, QSettings::IniFormat);
settings.beginGroup(e.m_uuid.toString());
SaveConnectionConfig(settings, e.m_config);
settings.sync();
e.m_dirty = false;
}
}
m_connections->save(index);
}
void ConnectionListModel::deleteFromIni(t_Connections::iterator begin, t_Connections::iterator end)
{
QString file_name = iniFileName();
QSettings settings(file_name, QSettings::IniFormat);
for (auto i = begin; i != end; ++i) {
settings.remove(i->m_uuid.toString());
}
}

View file

@ -5,11 +5,11 @@
#include <memory>
#include <QAbstractListModel>
#include <QUuid>
#include "connectionconfig.h"
#include "expected.h"
class ConnectionList;
/** \brief Model class for the list of connections.
*
@ -18,7 +18,9 @@
class ConnectionListModel : public QAbstractListModel {
Q_OBJECT
public:
ConnectionListModel(QObject *parent);
ConnectionListModel(ConnectionList *conns, QObject *parent);
ConnectionListModel(const ConnectionListModel&) = delete;
~ConnectionListModel();
virtual int rowCount(const QModelIndex &parent = QModelIndex()) const override;
virtual int columnCount(const QModelIndex &/*parent*/) const override;
@ -27,30 +29,16 @@ public:
virtual bool setData(const QModelIndex &index, const QVariant &value, int role = Qt::EditRole) override;
virtual Qt::ItemFlags flags(const QModelIndex &index) const override;
void add(const ConnectionConfig &cfg);
void newItem();
Expected<ConnectionConfig> get(int row);
virtual bool removeRows(int row, int count, const QModelIndex &parent = QModelIndex()) override;
void load();
void save();
void save(int index);
private:
class LijstElem {
public:
QUuid m_uuid;
bool m_dirty = false;
ConnectionConfig m_config;
LijstElem(const QUuid id, const ConnectionConfig &cfg)
: m_uuid(id), m_config(cfg)
{}
};
ConnectionList *m_connections;
using t_Connections = std::vector<LijstElem>;
t_Connections m_connections;
void deleteFromIni(t_Connections::iterator begin, t_Connections::iterator end);
static QString iniFileName();
static QString makeLongDescription(const ConnectionConfig &cfg);
};

View file

@ -37,12 +37,12 @@ ConnectionManagerWindow::~ConnectionManagerWindow()
void ConnectionManagerWindow::on_actionAdd_Connection_triggered()
{
ConnectionConfig c;
c.setName("new");
// ConnectionConfig c;
// c.setName("new");
//m_listModel->add(c);
auto clm = m_masterController->getConnectionListModel();
clm->add(c);
clm->newItem();
// Select the new row
auto idx = clm->index(clm->rowCount() - 1, 0);
@ -110,3 +110,67 @@ void ConnectionManagerWindow::on_actionManage_server_triggered()
auto ci = ui->listView->selectionModel()->currentIndex();
m_masterController->openServerWindowForConnection(ci.row());
}
#include <botan/botan.h>
//#include <botan/base64.h>
//#include <botan/pbkdf.h>
//#include <botan/block_cipher.h>
//#include <botan/hex.h>
#include <botan/cryptobox.h>
void ConnectionManagerWindow::on_testButton_clicked()
{
std::string error = Botan::runtime_version_check(BOTAN_VERSION_MAJOR,
BOTAN_VERSION_MINOR,
BOTAN_VERSION_PATCH);
if (error.empty()) {
// Botan::AutoSeeded_RNG rng;
// Botan::secure_vector<Botan::byte> salt =
// //{ 0x3f, 0x0a, 0xb0, 0x11, 0x44, 0xfe, 0x9d, 0xf7, 0x85, 0xd3, 0x11, 0x38, 0xe2, 0xdf, 0x31, 0x42 };
// rng.random_vec(16);
// // salt should be random and saved with encrypted data so it can be used when we decrypt
// std::string password = "Hello kitty";
// std::unique_ptr<Botan::PBKDF> pbkdf(Botan::get_pbkdf("PBKDF2(SHA-256)"));
// Botan::OctetString aes256_key = pbkdf->derive_key(32, password, salt.data(), salt.size(), 10000);
// std::string plaintext("Your great-grandfather gave this watch to your granddad for good luck. Unfortunately, Dane's luck wasn't as good as his old man's.");
// Botan::secure_vector<uint8_t> pt(plaintext.data(),plaintext.data()+plaintext.length());
// std::unique_ptr<Botan::Cipher_Mode> enc(Botan::get_cipher_mode("AES-256/CBC/PKCS7", Botan::ENCRYPTION));
// enc->set_key(aes256_key);
// //generate fresh nonce (IV)
// //std::unique_ptr<Botan::RandomNumberGenerator> rng(new Botan::AutoSeeded_RNG);
// std::vector<uint8_t> iv(enc->default_nonce_length());
// rng.randomize(iv.data(), iv.size());
// enc->start(iv);
// enc->finish(pt);
// //std::cout << std::endl << enc->name() << " with iv " << Botan::hex_encode(iv) << std::endl << Botan::hex_encode(pt);
//std::string s = aes256_key.as_string();// + "\n" + t.format_string();
std::string passphrase = "my passphrase";
std::string plaintext("password1234");
try {
Botan::AutoSeeded_RNG rng;
std::string encrypted = Botan::CryptoBox::encrypt((const uint8_t*)plaintext.data(), plaintext.length(), passphrase, rng);
std::string decrypted = Botan::CryptoBox::decrypt(encrypted, passphrase);
std::string s = encrypted + "\n" + decrypted;
QMessageBox::information(this, "pglab",
QString::fromUtf8(s.c_str()), QMessageBox::Yes);
}
catch (Botan::Decoding_Error &e) {
QMessageBox::information(this, "pglab",
tr("Failure to decrypt"), QMessageBox::Yes);
}
}
else {
QMessageBox ::information(this, "pglab",
QString::fromUtf8(error.c_str()), QMessageBox::Yes);
}
}

View file

@ -15,10 +15,8 @@ class QStandardItemModel;
/** \brief Class that holds glue code for the ConnectionManager UI.
*
*/
class ConnectionManagerWindow : public QMainWindow
{
class ConnectionManagerWindow : public QMainWindow {
Q_OBJECT
public:
explicit ConnectionManagerWindow(MasterController *master, QWidget *parent = 0);
~ConnectionManagerWindow();
@ -27,15 +25,13 @@ private slots:
void on_actionAdd_Connection_triggered();
void on_currentChanged(const QModelIndex &current, const QModelIndex &previous);
void on_actionDelete_connection_triggered();
void on_actionConnect_triggered();
void on_actionQuit_application_triggered();
void on_actionBackup_database_triggered();
void on_actionManage_server_triggered();
void on_testButton_clicked();
private:
Ui::ConnectionManagerWindow *ui;
QDataWidgetMapper *m_mapper = nullptr;

View file

@ -182,6 +182,13 @@
<item row="10" column="1">
<widget class="QLineEdit" name="edtCrl"/>
</item>
<item row="11" column="0">
<widget class="QPushButton" name="testButton">
<property name="text">
<string>PushButton</string>
</property>
</widget>
</item>
</layout>
</widget>
</widget>

View file

@ -1,64 +0,0 @@
#include "csvwriter.h"
CsvWriter::CsvWriter()
{}
CsvWriter::CsvWriter(QTextStream *output)
: m_output(output)
{}
void CsvWriter::setDestination(QTextStream *output)
{
m_output = output;
m_column = 0;
}
void CsvWriter::setSeperator(QChar ch)
{
m_seperator = ch;
}
void CsvWriter::setQuote(QChar ch)
{
m_quote = ch;
}
void CsvWriter::writeField(QString field)
{
QTextStream &out = *m_output;
if (m_column > 0) {
out << m_seperator;
}
// if field contains any of seperator, quote or newline then it needs to be quoted
// when quoted quotes need to be doubled to escape them
bool needs_quotes = false;
for (auto ch : field) {
if (ch == '\n' || ch == m_seperator || ch == m_quote) {
needs_quotes = true;
break;
}
}
if (needs_quotes) {
out << m_quote;
for (auto ch : field) {
if (ch == m_quote)
out << m_quote;
out << ch;
}
out << m_quote;
}
else {
out << field;
}
++m_column;
}
void CsvWriter::nextRow()
{
QTextStream &out = *m_output;
out << '\n';
m_column = 0;
}

View file

@ -1,23 +0,0 @@
#ifndef CSVWRITER_H
#define CSVWRITER_H
#include <ostream>
#include <QTextStream>
class CsvWriter {
public:
CsvWriter();
explicit CsvWriter(QTextStream *output);
void setDestination(QTextStream *output);
void setSeperator(QChar ch);
void setQuote(QChar ch);
void writeField(QString field);
void nextRow();
private:
QChar m_seperator = ',';
QChar m_quote = '"';
QTextStream *m_output = nullptr;
int m_column = 0;
};
#endif // CSVWRITER_H

View file

@ -1,156 +0,0 @@
#ifndef EXPECTED_H
#define EXPECTED_H
template <typename T>
class Expected {
union {
T m_value;
std::exception_ptr m_error;
};
bool m_valid;
Expected() {} // internal use
public:
Expected(const T& rhs)
: m_value(rhs), m_valid(true)
{}
Expected(T&& rhs)
: m_value(std::move(rhs))
, m_valid(true)
{}
Expected(const Expected& rhs)
: m_valid(rhs.valid)
{
if (m_valid) {
new (&m_value) T(rhs.m_value);
}
else {
new (&m_error) std::exception_ptr(rhs.m_error);
}
}
Expected(Expected &&rhs)
: m_valid(rhs.m_valid)
{
if (m_valid) {
new (&m_value) T(std::move(rhs.m_value));
}
else {
new (&m_error) std::exception_ptr(std::move(rhs.m_error));
}
}
~Expected()
{
if (m_valid) {
m_value.~T();
}
else {
using std::exception_ptr;
m_error.~exception_ptr();
}
}
void swap(Expected& rhs)
{
if (m_valid) {
if (rhs.m_valid) {
using std::swamp;
swap(m_value, rhs.m_value);
}
else {
auto t = std::move(rhs.m_error);
new(&rhs.m_value) T(std::move(m_value));
new(&m_error) std::exception_ptr(t);
std::swap(m_valid, rhs.m_valid);
}
}
else {
if (rhs.m_valid) {
rhs.swap(*this);
}
else {
m_error.swap(rhs.m_error);
std::swap(m_valid, rhs.m_valid);
}
}
}
template <class E>
static Expected<T> fromException(const E& exception)
{
if (typeid(exception) != typeid(E)) {
throw std::invalid_argument("slicing detected");
}
return fromException(std::make_exception_ptr(exception));
}
static Expected<T> fromException(std::exception_ptr p)
{
Expected<T> result;
result.m_valid = false;
new (&result.m_error) std::exception_ptr(std::move(p));
return result;
}
static Expected<T> fromException()
{
return fromException(std::current_exception());
}
bool valid() const
{
return m_valid;
}
T& get()
{
if (!m_valid) {
std::rethrow_exception(m_error);
}
return m_value;
}
const T& get() const
{
if (!m_valid) {
std::rethrow_exception(m_error);
}
return m_value;
}
template <class E>
bool hasException() const
{
try {
if (!m_valid) {
std::rethrow_exception(m_error);
}
}
catch (const E& object) {
return true;
}
catch (...) {
}
return false;
}
template <class F>
static Expected fromCode(F fun)
{
try {
return Expected(fun());
}
catch (...) {
return fromException();
}
}
};
#endif // EXPECTED_H

View file

@ -1,6 +1,7 @@
#include "MasterController.h"
#include "connectionmanagerwindow.h"
#include "connectionlistmodel.h"
#include "ConnectionManagerWindow.h"
#include "ConnectionList.h"
#include "ConnectionListModel.h"
#include "MainWindow.h"
#include "ServerWindow.h"
@ -12,11 +13,14 @@ MasterController::~MasterController()
{
delete m_connectionManagerWindow;
delete m_connectionListModel;
delete m_connectionList;
}
void MasterController::init()
{
m_connectionListModel = new ConnectionListModel(this);
m_connectionList = new ConnectionList;
m_connectionList->load();
m_connectionListModel = new ConnectionListModel(m_connectionList, this);
m_connectionManagerWindow = new ConnectionManagerWindow(this, nullptr);
m_connectionManagerWindow->show();

View file

@ -5,16 +5,18 @@
#include <map>
class ConnectionConfig;
class ConnectionList;
class ConnectionListModel;
class ConnectionManagerWindow;
/** \brief Controller class responsible for all things global.
*/
class MasterController : public QObject
{
class MasterController : public QObject {
Q_OBJECT
public:
explicit MasterController(QObject *parent = 0);
MasterController(const MasterController&) = delete;
MasterController &operator=(const MasterController&) = delete;
~MasterController();
void init();
@ -33,7 +35,7 @@ signals:
public slots:
private:
ConnectionList *m_connectionList = nullptr;
ConnectionListModel *m_connectionListModel = nullptr;
ConnectionManagerWindow *m_connectionManagerWindow = nullptr;
};

View file

@ -22,6 +22,7 @@ QWidget *ParamTypeDelegate::createEditor(QWidget *parent,
QWidget *w = nullptr;
QComboBox *cmbbx = new QComboBox(parent);
cmbbx->setMaxVisibleItems(32);
cmbbx->setModel(m_typeSelectionModel);
w = cmbbx;

View file

@ -1,69 +0,0 @@
#ifndef SCOPEGUARD_H
#define SCOPEGUARD_H
/** \brief Template class for executing code at scope exit.
*
* By default the object will be an active mode and execute the function
* passed to the constructor when the object is destructed. You can however
* cancel this action by calling dismiss().
*
* There is a clever macro that allows you to write something like
* SCOPE_EXIT { foo(); };
*/
template<class Fun>
class ScopeGuard {
Fun f_;
bool active_;
public:
ScopeGuard(Fun f)
: f_(std::move(f))
, active_(true) {
}
~ScopeGuard() { if(active_) f_(); }
void dismiss() { active_=false; }
ScopeGuard() = delete;
ScopeGuard(const ScopeGuard&) = delete;
ScopeGuard& operator=(const ScopeGuard&) = delete;
ScopeGuard(ScopeGuard&& rhs)
: f_(std::move(rhs.f_))
, active_(rhs.active_)
{
rhs.dismiss();
}
};
template<class Fun>
ScopeGuard<Fun> scopeGuard(Fun f)
{
return ScopeGuard<Fun>(std::move(f));
}
namespace ScopeGuard_detail {
enum class ScopeGuardOnExit {};
template<typename Fun>
ScopeGuard<Fun> operator+(ScopeGuardOnExit, Fun&& fn) {
return ScopeGuard<Fun>(std::forward<Fun>(fn));
}
}
#define CONCATENATE_IMPL(s1, s2) s1##s2
#define CONCATENATE(s1, s2) CONCATENATE_IMPL(s1, s2)
#ifdef __COUNTER__
#define ANONYMOUS_VARIABLE(str) \
CONCATENATE(str,__COUNTER__)
#else
#define ANONYMOUS_VARIABLE(str) \
CONCATENATE(str,__LINE__)
#endif
#define SCOPE_EXIT \
auto ANONYMOUS_VARIABLE(SCOPE_EXIT_STATE) \
= ::ScopeGuard_detail::ScopeGuardOnExit() + [&]()
#endif // SCOPEGUARD_H

View file

@ -1,134 +0,0 @@
#include "SqlLexer.h"
SqlLexer::SqlLexer(const QString &block, LexerState currentstate)
: m_block(block)
, m_state(currentstate)
{}
QChar SqlLexer::nextChar()
{
QChar result = QChar::Null;
if (m_pos < m_block.size()) {
result = m_block.at(m_pos++);
}
return result;
}
QChar SqlLexer::peekChar()
{
QChar result = QChar::Null;
if (m_pos < m_block.size()) {
result = m_block.at(m_pos);
}
return result;
}
/**
* @brief NextBasicToken
* @param in
* @param ofs
* @param start
* @param length
* @return false when input seems invalid, it will return what it did recognize but something wasn't right, parser should try to recover
*/
bool SqlLexer::nextBasicToken(int &startpos, int &length, BasicTokenType &tokentype, QString &out)
{
// Basically chops based on white space
// it does also recognize comments and quoted strings/identifiers
bool result = false;
while (true) {
startpos = m_pos;
QChar c = nextChar();
if (c.isSpace()) {
// Just skip whitespace
}
else if (c == '-' && peekChar() == '-') { // two dashes, start of comment
// Loop till end of line or end of block
c = nextChar();
for (;;) {
c = peekChar();
if (c != QChar::Null && c != '\n')
nextChar();
else
break;
}
length = m_pos - startpos;
tokentype = BasicTokenType::Comment;
return true;
}
else if (c == '\'') {
// Single quoted string so it's an SQL text literal
while (true) {
c = peekChar();
if (c == QChar::Null || c == '\n') {
// unexpected end, pretend nothings wrong
length = m_pos - startpos;
tokentype = BasicTokenType::QuotedString;
return true;
}
else {
nextChar();
if (c == '\'') {
// maybe end of string literal
if (peekChar() == '\'') {
// Nope, just double quote to escape quote
nextChar(); // eat it
}
else {
length = m_pos - startpos;
tokentype = BasicTokenType::QuotedString;
return true;
}
}
}
}
}
else if (c == '"') {
// Double quoted identifier
while (true) {
c = peekChar();
if (c == QChar::Null || c == '\n') {
// unexpected end, pretend nothings wrong
length = m_pos - startpos;
tokentype = BasicTokenType::QuotedIdentifier;
return true;
}
else {
nextChar();
if (c == '"') {
// maybe end of string literal
if (peekChar() == '"') {
// Nope, just double quote to escape quote
nextChar(); // eat it
}
else {
length = m_pos - startpos;
tokentype = BasicTokenType::QuotedIdentifier;
return true;
}
}
}
}
}
else if (c == QChar::Null) {
break;
}
else {
// Undetermined symbol
for (;;) {
c = peekChar();
if (c.isLetterOrNumber() || c == '_')
nextChar();
else
break;
}
length = m_pos - startpos;
tokentype = BasicTokenType::Symbol;
QStringRef sr(&m_block, startpos, length);
out = sr.toString();
return true;
}
}
return false;
}

View file

@ -1,44 +0,0 @@
#ifndef SQLLEXER_H
#define SQLLEXER_H
#include <QString>
enum class BasicTokenType {
None,
End, // End of input
Symbol, // can be many things, keyword, object name, operator, ..
Comment,
QuotedString,
DollarQuotedString,
QuotedIdentifier
};
enum class LexerState {
Null,
InDollarQuotedString
};
class SqlLexer {
public:
SqlLexer(const QString &block, LexerState currentstate);
QChar nextChar();
QChar peekChar();
/**
* @brief NextBasicToken
* @param in
* @param ofs
* @param start
* @param length
* @return false when input seems invalid, it will return what it did recognize but something wasn't right, parser should try to recover
*/
bool nextBasicToken(int &startpos, int &length, BasicTokenType &tokentype, QString &out);
private:
QString m_block;
int m_pos = 0;
LexerState m_state;
};
#endif // SQLLEXER_H

View file

@ -133,7 +133,10 @@ void SqlSyntaxHighlighter::setTypes(const PgTypeContainer *types)
void SqlSyntaxHighlighter::highlightBlock(const QString &text)
{
SqlLexer lexer(text, LexerState::Null);
int previous_state = previousBlockState();
if (previous_state <= 0)
previous_state = 0;
SqlLexer lexer(text, (LexerState)previous_state);
int startpos, length;
BasicTokenType tokentype;
QString s;
@ -151,6 +154,9 @@ void SqlSyntaxHighlighter::highlightBlock(const QString &text)
setFormat(startpos, length, m_typeFormat);
}
break;
case BasicTokenType::OpenBlockComment:
setCurrentBlockState((int)lexer.currentState());
case BasicTokenType::BlockComment:
case BasicTokenType::Comment:
setFormat(startpos, length, m_commentFormat);
break;

View file

@ -4,6 +4,7 @@
#
#-------------------------------------------------
CONFIG += c++11
QT += core gui
greaterThan(QT_MAJOR_VERSION, 4): QT += widgets sql
@ -11,9 +12,17 @@ greaterThan(QT_MAJOR_VERSION, 4): QT += widgets sql
TARGET = pglab
TEMPLATE = app
INCLUDEPATH += C:\prog\include
INCLUDEPATH += C:\prog\include C:\VSproj\boost_1_63_0
DEFINES += WIN32_LEAN_AND_MEAN NOMINMAX
LIBS += c:\prog\lib\libpq.lib c:\prog\lib\fmt.lib User32.lib ws2_32.lib
LIBS += /LIBPATH:C:\VSproj\boost_1_63_0\stage\lib /LIBPATH:c:\prog\lib\ libpq.lib fmt.lib User32.lib ws2_32.lib
debug {
LIBS += c:\prog\lib\botand_imp.lib
}
release {
# LIBS += c:\prog\lib\botan.lib
}
win32:RC_ICONS += pglab.ico
@ -29,7 +38,6 @@ SOURCES += main.cpp\
tsqueue.cpp \
win32event.cpp \
waithandlelist.cpp \
CsvWriter.cpp \
DatabaseWindow.cpp \
ConnectionManagerWindow.cpp \
ConnectionListModel.cpp \
@ -53,7 +61,6 @@ SOURCES += main.cpp\
ParamListModel.cpp \
MainWindow.cpp \
SqlSyntaxHighlighter.cpp \
SqlLexer.cpp \
ServerWindow.cpp \
ASyncWindow.cpp \
DatabasesTableModel.cpp \
@ -65,7 +72,8 @@ SOURCES += main.cpp\
PgAuthIdContainer.cpp \
Pgsql_Result.cpp \
Pgsql_Row.cpp \
Pgsql_Value.cpp
Pgsql_Value.cpp \
ConnectionList.cpp
HEADERS += \
sqlparser.h \
@ -78,13 +86,10 @@ HEADERS += \
tsqueue.h \
win32event.h \
waithandlelist.h \
CsvWriter.h \
DatabaseWindow.h \
ConnectionManagerWindow.h \
ConnectionListModel.h \
ConnectionConfig.h \
ScopeGuard.h \
Expected.h \
QueryTab.h \
stopwatch.h \
util.h \
@ -117,7 +122,8 @@ HEADERS += \
PgAuthIdContainer.h \
Pgsql_Result.h \
Pgsql_Row.h \
Pgsql_Value.h
Pgsql_Value.h \
ConnectionList.h
FORMS += mainwindow.ui \
DatabaseWindow.ui \
@ -132,3 +138,14 @@ RESOURCES += \
resources.qrc
QMAKE_LFLAGS_WINDOWS = /SUBSYSTEM:WINDOWS,5.01
win32:CONFIG(release, debug|release): LIBS += -L$$OUT_PWD/../core/release/ -lcore
else:win32:CONFIG(debug, debug|release): LIBS += -L$$OUT_PWD/../core/debug/ -lcore
INCLUDEPATH += $$PWD/../core
DEPENDPATH += $$PWD/../core
win32-g++:CONFIG(release, debug|release): PRE_TARGETDEPS += $$OUT_PWD/../core/release/libcore.a
else:win32-g++:CONFIG(debug, debug|release): PRE_TARGETDEPS += $$OUT_PWD/../core/debug/libcore.a
else:win32:!win32-g++:CONFIG(release, debug|release): PRE_TARGETDEPS += $$OUT_PWD/../core/release/core.lib
else:win32:!win32-g++:CONFIG(debug, debug|release): PRE_TARGETDEPS += $$OUT_PWD/../core/debug/core.lib

View file

@ -20,7 +20,6 @@ public:
QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override;
private:
// const PgTypeContainer* m_types;
std::vector<QString> m_types;
};

View file

@ -4,6 +4,7 @@
#include <QTextStream>
#include <QClipboard>
#include <sstream>
#include <stdexcept>
// Supported range from microseconds to seconds
// min:sec to hours::min::sec
@ -134,3 +135,5 @@ QString ConvertToMultiLineCString(const QString &in)
out.append('"');
return out;
}

View file

@ -1,6 +1,7 @@
#ifndef UTIL_H
#define UTIL_H
#include <string>
#include <QString>
#include <QTableView>
@ -9,6 +10,23 @@ void copySelectionToClipboard(const QTableView *view);
QString ConvertToMultiLineCString(const QString &in);
void exportTable(const QTableView *view, QTextStream &out);
inline QString stdStrToQ(const std::string &s)
{
return QString::fromUtf8(s.c_str());
}
inline std::string qStrToStd(const QString &s)
{
return std::string(s.toUtf8().data());
}
inline std::string qvarToStdStr(const QVariant &c)
{
return qStrToStd(c.toString());
}
namespace std {
template <>