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:
parent
0a809a7288
commit
d0ea9dfa0c
39 changed files with 1767 additions and 493 deletions
6
core/Core.cpp
Normal file
6
core/Core.cpp
Normal file
|
|
@ -0,0 +1,6 @@
|
||||||
|
#include "Core.h"
|
||||||
|
|
||||||
|
|
||||||
|
Core::Core()
|
||||||
|
{
|
||||||
|
}
|
||||||
12
core/Core.h
Normal file
12
core/Core.h
Normal file
|
|
@ -0,0 +1,12 @@
|
||||||
|
#ifndef CORE_H
|
||||||
|
#define CORE_H
|
||||||
|
|
||||||
|
|
||||||
|
class Core
|
||||||
|
{
|
||||||
|
|
||||||
|
public:
|
||||||
|
Core();
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // CORE_H
|
||||||
269
core/Expected.h
Normal file
269
core/Expected.h
Normal file
|
|
@ -0,0 +1,269 @@
|
||||||
|
#ifndef EXPECTED_H
|
||||||
|
#define EXPECTED_H
|
||||||
|
|
||||||
|
#include <exception>
|
||||||
|
|
||||||
|
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::swap;
|
||||||
|
// 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 {
|
||||||
|
// using std::swap;
|
||||||
|
// swap(m_error, 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& ) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
catch (...) {
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <class F>
|
||||||
|
static Expected fromCode(F fun)
|
||||||
|
{
|
||||||
|
try {
|
||||||
|
return Expected(fun());
|
||||||
|
}
|
||||||
|
catch (...) {
|
||||||
|
return fromException();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
template <>
|
||||||
|
class Expected<void> {
|
||||||
|
std::exception_ptr m_error;
|
||||||
|
bool m_valid;
|
||||||
|
|
||||||
|
public:
|
||||||
|
|
||||||
|
Expected<void>()
|
||||||
|
: m_valid(true)
|
||||||
|
{}
|
||||||
|
|
||||||
|
|
||||||
|
Expected(const Expected& rhs)
|
||||||
|
: m_valid(rhs.m_valid)
|
||||||
|
, m_error(rhs.m_error)
|
||||||
|
{}
|
||||||
|
|
||||||
|
Expected(Expected<void> &&rhs)
|
||||||
|
: m_valid(rhs.m_valid)
|
||||||
|
, m_error(std::move(rhs.m_error))
|
||||||
|
{}
|
||||||
|
|
||||||
|
~Expected()
|
||||||
|
{}
|
||||||
|
|
||||||
|
// void swap(Expected& rhs)
|
||||||
|
// {
|
||||||
|
// if (m_valid) {
|
||||||
|
// if (!rhs.m_valid) {
|
||||||
|
// m_error = rhs.m_error;
|
||||||
|
// std::swap(m_valid, rhs.m_valid);
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// else {
|
||||||
|
// if (rhs.m_valid) {
|
||||||
|
// rhs.swap(*this);
|
||||||
|
// }
|
||||||
|
// else {
|
||||||
|
// using std::swap;
|
||||||
|
// swap(m_error, rhs.m_error);
|
||||||
|
// std::swap(m_valid, rhs.m_valid);
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
|
||||||
|
template <class E>
|
||||||
|
static Expected<void> fromException(const E& exception)
|
||||||
|
{
|
||||||
|
if (typeid(exception) != typeid(E)) {
|
||||||
|
throw std::invalid_argument("slicing detected");
|
||||||
|
}
|
||||||
|
return fromException(std::make_exception_ptr(exception));
|
||||||
|
}
|
||||||
|
|
||||||
|
static Expected<void> fromException(std::exception_ptr p)
|
||||||
|
{
|
||||||
|
Expected<void> result;
|
||||||
|
result.m_valid = false;
|
||||||
|
result.m_error = std::exception_ptr(std::move(p));
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
static Expected<void> fromException()
|
||||||
|
{
|
||||||
|
return fromException(std::current_exception());
|
||||||
|
}
|
||||||
|
|
||||||
|
bool valid() const
|
||||||
|
{
|
||||||
|
return m_valid;
|
||||||
|
}
|
||||||
|
|
||||||
|
void get() const
|
||||||
|
{
|
||||||
|
if (!m_valid) {
|
||||||
|
std::rethrow_exception(m_error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
template <class E>
|
||||||
|
bool hasException() const
|
||||||
|
{
|
||||||
|
try {
|
||||||
|
if (!m_valid) {
|
||||||
|
std::rethrow_exception(m_error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (const E& ) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
catch (...) {
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <class F>
|
||||||
|
static Expected fromCode(F fun)
|
||||||
|
{
|
||||||
|
try {
|
||||||
|
fun();
|
||||||
|
return Expected<void>();
|
||||||
|
}
|
||||||
|
catch (...) {
|
||||||
|
return fromException();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
#endif // EXPECTED_H
|
||||||
244
core/PasswordManager.cpp
Normal file
244
core/PasswordManager.cpp
Normal file
|
|
@ -0,0 +1,244 @@
|
||||||
|
#include "PasswordManager.h"
|
||||||
|
|
||||||
|
#include <botan/filters.h>
|
||||||
|
#include <botan/pipe.h>
|
||||||
|
#include <botan/sha2_64.h>
|
||||||
|
#include <botan/hash.h>
|
||||||
|
#include <botan/hmac.h>
|
||||||
|
#include <botan/pbkdf2.h>
|
||||||
|
#include <botan/rng.h>
|
||||||
|
#include <botan/base64.h>
|
||||||
|
#include <botan/loadstor.h>
|
||||||
|
#include <botan/mem_ops.h>
|
||||||
|
|
||||||
|
#include <boost/assert.hpp>
|
||||||
|
|
||||||
|
#include "Core.h"
|
||||||
|
|
||||||
|
using namespace Botan;
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
|
||||||
|
/*
|
||||||
|
First 24 bits of SHA-256("Botan Cryptobox"), followed by 8 0 bits
|
||||||
|
for later use as flags, etc if needed
|
||||||
|
*/
|
||||||
|
const uint8_t c_PasswordVersionCode = 0x10;
|
||||||
|
|
||||||
|
const size_t c_VersionCodeLen = 1;
|
||||||
|
|
||||||
|
const size_t CIPHER_KEY_LEN = 32;
|
||||||
|
const size_t CIPHER_IV_LEN = 16;
|
||||||
|
const size_t MAC_KEY_LEN = 32;
|
||||||
|
const size_t MAC_OUTPUT_LEN = 20;
|
||||||
|
const size_t PBKDF_SALT_LEN = 10;
|
||||||
|
const size_t PBKDF_ITERATIONS = 8 * 1024;
|
||||||
|
|
||||||
|
const size_t PBKDF_OUTPUT_LEN = CIPHER_KEY_LEN + CIPHER_IV_LEN + MAC_KEY_LEN;
|
||||||
|
|
||||||
|
const char * const c_Cipher = "Serpent/CTR-BE";
|
||||||
|
|
||||||
|
const char * const c_IniGroupSecurity = "Security";
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
StrengthenedKey generateKey(const std::string &passphrase, const uint8_t *salt, int saltlength)
|
||||||
|
{
|
||||||
|
PKCS5_PBKDF2 pbkdf(new HMAC(new SHA_512));
|
||||||
|
OctetString master_key = pbkdf.derive_key(
|
||||||
|
PBKDF_OUTPUT_LEN,
|
||||||
|
passphrase,
|
||||||
|
salt, saltlength,
|
||||||
|
PBKDF_ITERATIONS);
|
||||||
|
|
||||||
|
const uint8_t* mk = master_key.begin();
|
||||||
|
|
||||||
|
return StrengthenedKey(
|
||||||
|
SymmetricKey(mk, CIPHER_KEY_LEN),
|
||||||
|
SymmetricKey(mk + CIPHER_KEY_LEN, MAC_KEY_LEN),
|
||||||
|
InitializationVector(mk + CIPHER_KEY_LEN + MAC_KEY_LEN, CIPHER_IV_LEN));
|
||||||
|
}
|
||||||
|
|
||||||
|
// secure_vector<uint8_t> pbkdf_salt(PBKDF_SALT_LEN);
|
||||||
|
// rng.randomize( pbkdf_salt.data(), pbkdf_salt.size());
|
||||||
|
// StrengthenedKey strengthened_key = generateKey(passphrase, pbkdf_salt.data(), pbkdf_salt.size());
|
||||||
|
|
||||||
|
|
||||||
|
std::string encrypt(const std::string &input,
|
||||||
|
const StrengthenedKey &strengthened_key)
|
||||||
|
{
|
||||||
|
|
||||||
|
Pipe pipe(get_cipher(c_Cipher, strengthened_key.cipher_key,
|
||||||
|
strengthened_key.iv, ENCRYPTION),
|
||||||
|
new Fork(
|
||||||
|
nullptr,
|
||||||
|
new MAC_Filter(new HMAC(new SHA_512),
|
||||||
|
strengthened_key.mac_key, MAC_OUTPUT_LEN)));
|
||||||
|
|
||||||
|
pipe.process_msg((const uint8_t*)input.data(), input.length());
|
||||||
|
|
||||||
|
/*
|
||||||
|
Output format is:
|
||||||
|
mac (20 bytes)
|
||||||
|
ciphertext
|
||||||
|
*/
|
||||||
|
const size_t ciphertext_len = pipe.remaining(0);
|
||||||
|
std::vector<uint8_t> out_buf(MAC_OUTPUT_LEN + ciphertext_len);
|
||||||
|
|
||||||
|
BOTAN_ASSERT_EQUAL(
|
||||||
|
pipe.read(&out_buf[0], MAC_OUTPUT_LEN, 1),
|
||||||
|
MAC_OUTPUT_LEN, "MAC output");
|
||||||
|
BOTAN_ASSERT_EQUAL(
|
||||||
|
pipe.read(&out_buf[MAC_OUTPUT_LEN], ciphertext_len, 0),
|
||||||
|
ciphertext_len, "Ciphertext size");
|
||||||
|
|
||||||
|
return base64_encode(out_buf.data(), out_buf.size());
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string decrypt(const std::string &input, const StrengthenedKey &strengthened_key)
|
||||||
|
{
|
||||||
|
secure_vector<uint8_t> ciphertext = base64_decode(input);
|
||||||
|
|
||||||
|
if(ciphertext.size() < (MAC_OUTPUT_LEN)) {
|
||||||
|
throw Decoding_Error("Invalid encrypted password input");
|
||||||
|
}
|
||||||
|
|
||||||
|
Pipe pipe(new Fork(
|
||||||
|
get_cipher(c_Cipher, strengthened_key.cipher_key, strengthened_key.iv, DECRYPTION),
|
||||||
|
new MAC_Filter(new HMAC(new SHA_512), strengthened_key.mac_key, MAC_OUTPUT_LEN)
|
||||||
|
));
|
||||||
|
|
||||||
|
const size_t ciphertext_offset = MAC_OUTPUT_LEN;
|
||||||
|
pipe.process_msg(&ciphertext[ciphertext_offset], ciphertext.size() - ciphertext_offset);
|
||||||
|
|
||||||
|
uint8_t computed_mac[MAC_OUTPUT_LEN];
|
||||||
|
BOTAN_ASSERT_EQUAL(MAC_OUTPUT_LEN, pipe.read(computed_mac, MAC_OUTPUT_LEN, 1), "MAC size");
|
||||||
|
|
||||||
|
if(!same_mem(computed_mac, &ciphertext[0], MAC_OUTPUT_LEN)) {
|
||||||
|
throw Decoding_Error("Encrypted password integrity failure");
|
||||||
|
}
|
||||||
|
|
||||||
|
return pipe.read_all_as_string(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
struct constants {
|
||||||
|
const int pbkdf_salt_len;
|
||||||
|
};
|
||||||
|
|
||||||
|
constants v1_consts = {
|
||||||
|
10
|
||||||
|
};
|
||||||
|
|
||||||
|
} // end of unnamed namespace
|
||||||
|
|
||||||
|
/*
|
||||||
|
* File layout:
|
||||||
|
*
|
||||||
|
* Header
|
||||||
|
* version
|
||||||
|
* key_salt
|
||||||
|
* hash_salt
|
||||||
|
* master_hash
|
||||||
|
*
|
||||||
|
*
|
||||||
|
* Passwords
|
||||||
|
* key = pw
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
PasswordManager::PasswordManager()
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
Expected<bool> PasswordManager::unlock(const std::string &master_password)
|
||||||
|
{
|
||||||
|
try {
|
||||||
|
bool result = false;
|
||||||
|
if (m_masterHash.length() == 0 && master_password.empty()) {
|
||||||
|
result = true;
|
||||||
|
} else {
|
||||||
|
StrengthenedKey key = generateKey(master_password, m_keySalt.begin(), m_keySalt.length());
|
||||||
|
OctetString hash = hashStrengthenedKey(key, m_hashSalt);
|
||||||
|
|
||||||
|
BOOST_ASSERT_MSG(hash.length() == m_masterHash.length(), "Both hashes should have the same length! Versioning error?");
|
||||||
|
|
||||||
|
if (same_mem(m_masterHash.begin(), hash.begin(), hash.length())) {
|
||||||
|
result = true;
|
||||||
|
m_masterKey = key;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
} catch (...) {
|
||||||
|
return Expected<bool>::fromException();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Expected<bool> PasswordManager::changeMasterPassword(const std::string &old_master_password,
|
||||||
|
const std::string &new_master_password)
|
||||||
|
{
|
||||||
|
try {
|
||||||
|
bool result = false;
|
||||||
|
if (m_masterHash.length() == 0 && old_master_password.empty()) {
|
||||||
|
// Nothing set yet so we initialize for first use
|
||||||
|
m_keySalt = OctetString(m_rng, v1_consts.pbkdf_salt_len);
|
||||||
|
m_masterKey = generateKey(new_master_password, m_keySalt.begin(), m_keySalt.length());
|
||||||
|
|
||||||
|
m_hashSalt = OctetString(m_rng, v1_consts.pbkdf_salt_len);
|
||||||
|
m_masterHash = hashStrengthenedKey(m_masterKey, m_hashSalt);
|
||||||
|
result = true;
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
} catch (...) {
|
||||||
|
return Expected<bool>::fromException();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void PasswordManager::lock()
|
||||||
|
{
|
||||||
|
m_masterKey = StrengthenedKey();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool PasswordManager::locked() const
|
||||||
|
{
|
||||||
|
return m_masterKey.cipher_key.size() == 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
Expected<void> PasswordManager::savePassword(const std::string &key, const std::string &password)
|
||||||
|
{
|
||||||
|
if (locked()) {
|
||||||
|
return Expected<void>::fromException(std::logic_error("Need to unlock the password manager first"));
|
||||||
|
}
|
||||||
|
std::string epw = encrypt(password, m_masterKey);
|
||||||
|
m_store.emplace(key, epw);
|
||||||
|
|
||||||
|
return Expected<void>();
|
||||||
|
}
|
||||||
|
|
||||||
|
Expected<bool> PasswordManager::getPassword(const std::string &key, std::string &out)
|
||||||
|
{
|
||||||
|
if (locked()) {
|
||||||
|
return Expected<bool>::fromException(std::logic_error("Need to unlock the password manager first"));
|
||||||
|
}
|
||||||
|
auto fi = m_store.find(key);
|
||||||
|
|
||||||
|
bool result = false;
|
||||||
|
if (fi != m_store.end()) {
|
||||||
|
out = decrypt(fi->second, m_masterKey);
|
||||||
|
result = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
Botan::OctetString PasswordManager::hashStrengthenedKey(const StrengthenedKey &key, const OctetString &salt)
|
||||||
|
{
|
||||||
|
std::unique_ptr<Botan::HashFunction> hash3(Botan::HashFunction::create("SHA-3"));
|
||||||
|
BOOST_ASSERT_MSG(hash3 != nullptr, "SHA-3 algorithm not available");
|
||||||
|
hash3->update(salt.begin(), salt.length());
|
||||||
|
hash3->update(key.cipher_key.begin(), key.cipher_key.length());
|
||||||
|
hash3->update(key.mac_key.begin(), key.mac_key.length());
|
||||||
|
hash3->update(key.iv.begin(), key.iv.length());
|
||||||
|
return hash3->final();
|
||||||
|
}
|
||||||
66
core/PasswordManager.h
Normal file
66
core/PasswordManager.h
Normal file
|
|
@ -0,0 +1,66 @@
|
||||||
|
#ifndef PASSWORDMANAGER_H
|
||||||
|
#define PASSWORDMANAGER_H
|
||||||
|
|
||||||
|
#include "Expected.h"
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
#include <botan/botan.h>
|
||||||
|
#include <botan/symkey.h>
|
||||||
|
|
||||||
|
#include <map>
|
||||||
|
|
||||||
|
struct StrengthenedKey {
|
||||||
|
Botan::SymmetricKey cipher_key;
|
||||||
|
Botan::SymmetricKey mac_key;
|
||||||
|
Botan::InitializationVector iv;
|
||||||
|
|
||||||
|
StrengthenedKey() {}
|
||||||
|
StrengthenedKey(const Botan::SymmetricKey &ck, const Botan::SymmetricKey &mk,
|
||||||
|
const Botan::InitializationVector &i)
|
||||||
|
: cipher_key(ck)
|
||||||
|
, mac_key(mk)
|
||||||
|
, iv(i)
|
||||||
|
{}
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
class PasswordManager {
|
||||||
|
public:
|
||||||
|
|
||||||
|
// static PasswordManager create(const std::string &file_name);
|
||||||
|
|
||||||
|
PasswordManager();
|
||||||
|
/** Unlocks the passwords of the connections.
|
||||||
|
*
|
||||||
|
* \return Normally it return a bool specifying if the password was accepted.
|
||||||
|
* on rare occasions it could return an error.
|
||||||
|
*/
|
||||||
|
Expected<bool> unlock(const std::string &master_password);
|
||||||
|
|
||||||
|
Expected<bool> changeMasterPassword(const std::string &master_password,
|
||||||
|
const std::string &new_master_password);
|
||||||
|
|
||||||
|
/** Forget master password
|
||||||
|
*/
|
||||||
|
void lock();
|
||||||
|
bool locked() const;
|
||||||
|
|
||||||
|
Expected<void> savePassword(const std::string &key, const std::string &password);
|
||||||
|
Expected<bool> getPassword(const std::string &key, std::string &out);
|
||||||
|
|
||||||
|
private:
|
||||||
|
Botan::AutoSeeded_RNG m_rng;
|
||||||
|
Botan::OctetString m_keySalt; // salt for generating crypto key
|
||||||
|
StrengthenedKey m_masterKey; // crypto key
|
||||||
|
Botan::OctetString m_hashSalt; // salt of the hash of the passphrase
|
||||||
|
Botan::OctetString m_masterHash; // hash for checking the passphrase
|
||||||
|
|
||||||
|
using t_KeyPasswords = std::map<std::string, std::string>;
|
||||||
|
|
||||||
|
t_KeyPasswords m_store;
|
||||||
|
|
||||||
|
static Botan::OctetString hashStrengthenedKey(const StrengthenedKey &key, const Botan::OctetString &salt);
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
#endif // PASSWORDMANAGER_H
|
||||||
162
core/SqlLexer.cpp
Normal file
162
core/SqlLexer.cpp
Normal file
|
|
@ -0,0 +1,162 @@
|
||||||
|
#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
|
||||||
|
while (true) {
|
||||||
|
startpos = m_pos;
|
||||||
|
QChar c = nextChar();
|
||||||
|
// if (LexerState::Null == m_state) {
|
||||||
|
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 == '/' && peekChar() == '*') {
|
||||||
|
// nextChar();
|
||||||
|
// m_state = LexerState::InBlockComment;
|
||||||
|
// }
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
// }
|
||||||
|
// else if (LexerState::InBlockComment == m_state) {
|
||||||
|
// if (c == QChar::Null) {
|
||||||
|
// // eof current buffer, we need to return state so
|
||||||
|
// if (m_pos == startpos) {
|
||||||
|
// break;
|
||||||
|
// }
|
||||||
|
// else {
|
||||||
|
// length = m_pos - startpos;
|
||||||
|
// tokentype = BasicTokenType::OpenBlockComment;
|
||||||
|
// return true;
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// else if (c == '*') {
|
||||||
|
// nextChar();
|
||||||
|
// if (peekChar() == '/') {
|
||||||
|
// nextChar();
|
||||||
|
// length = m_pos - startpos;
|
||||||
|
// tokentype = BasicTokenType::BlockComment;
|
||||||
|
// m_state = LexerState::Null;
|
||||||
|
// return true;
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
@ -8,6 +8,8 @@ enum class BasicTokenType {
|
||||||
End, // End of input
|
End, // End of input
|
||||||
Symbol, // can be many things, keyword, object name, operator, ..
|
Symbol, // can be many things, keyword, object name, operator, ..
|
||||||
Comment,
|
Comment,
|
||||||
|
BlockComment,
|
||||||
|
OpenBlockComment, // Busy with a block comment end not detected before end of current input
|
||||||
QuotedString,
|
QuotedString,
|
||||||
DollarQuotedString,
|
DollarQuotedString,
|
||||||
QuotedIdentifier
|
QuotedIdentifier
|
||||||
|
|
@ -15,7 +17,8 @@ enum class BasicTokenType {
|
||||||
|
|
||||||
enum class LexerState {
|
enum class LexerState {
|
||||||
Null,
|
Null,
|
||||||
InDollarQuotedString
|
InDollarQuotedString,
|
||||||
|
InBlockComment
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -34,11 +37,13 @@ public:
|
||||||
*/
|
*/
|
||||||
bool nextBasicToken(int &startpos, int &length, BasicTokenType &tokentype, QString &out);
|
bool nextBasicToken(int &startpos, int &length, BasicTokenType &tokentype, QString &out);
|
||||||
|
|
||||||
|
LexerState currentState() const { return m_state; }
|
||||||
private:
|
private:
|
||||||
QString m_block;
|
QString m_block;
|
||||||
int m_pos = 0;
|
int m_pos = 0;
|
||||||
LexerState m_state;
|
LexerState m_state;
|
||||||
|
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // SQLLEXER_H
|
#endif // SQLLEXER_H
|
||||||
43
core/core.pro
Normal file
43
core/core.pro
Normal file
|
|
@ -0,0 +1,43 @@
|
||||||
|
#-------------------------------------------------
|
||||||
|
#
|
||||||
|
# Project created by QtCreator 2017-02-26T10:51:14
|
||||||
|
#
|
||||||
|
#-------------------------------------------------
|
||||||
|
|
||||||
|
QT -= gui
|
||||||
|
|
||||||
|
TARGET = core
|
||||||
|
TEMPLATE = lib
|
||||||
|
CONFIG += staticlib c++11
|
||||||
|
|
||||||
|
INCLUDEPATH += C:\prog\include C:\VSproj\boost_1_63_0
|
||||||
|
DEFINES += WIN32_LEAN_AND_MEAN NOMINMAX
|
||||||
|
LIBS += /LIBPATH:C:\VSproj\boost_1_63_0\stage\lib /LIBPATH:c:\prog\lib\ libpq.lib fmt.lib User32.lib ws2_32.lib
|
||||||
|
|
||||||
|
|
||||||
|
# The following define makes your compiler emit warnings if you use
|
||||||
|
# any feature of Qt which as been marked as deprecated (the exact warnings
|
||||||
|
# depend on your compiler). Please consult the documentation of the
|
||||||
|
# deprecated API in order to know how to port your code away from it.
|
||||||
|
DEFINES += QT_DEPRECATED_WARNINGS
|
||||||
|
|
||||||
|
# You can also make your code fail to compile if you use deprecated APIs.
|
||||||
|
# In order to do so, uncomment the following line.
|
||||||
|
# You can also select to disable deprecated APIs only up to a certain version of Qt.
|
||||||
|
#DEFINES += QT_DISABLE_DEPRECATED_BEFORE=0x060000 # disables all the APIs deprecated before Qt 6.0.0
|
||||||
|
|
||||||
|
SOURCES += Core.cpp \
|
||||||
|
my_boost_assert_handler.cpp \
|
||||||
|
SqlLexer.cpp \
|
||||||
|
PasswordManager.cpp \
|
||||||
|
CsvWriter.cpp
|
||||||
|
|
||||||
|
HEADERS += Core.h \
|
||||||
|
PasswordManager.h \
|
||||||
|
SqlLexer.h \
|
||||||
|
ScopeGuard.h \
|
||||||
|
CsvWriter.h
|
||||||
|
unix {
|
||||||
|
target.path = /usr/lib
|
||||||
|
INSTALLS += target
|
||||||
|
}
|
||||||
20
core/my_boost_assert_handler.cpp
Normal file
20
core/my_boost_assert_handler.cpp
Normal file
|
|
@ -0,0 +1,20 @@
|
||||||
|
#include <sstream>
|
||||||
|
#include <stdexcept>
|
||||||
|
|
||||||
|
namespace boost
|
||||||
|
{
|
||||||
|
void assertion_failed(char const * expr, char const * function, char const * file, long line)
|
||||||
|
{
|
||||||
|
std::ostringstream out;
|
||||||
|
out << "Assertion failure int " << function << " " << file << ":" << line;
|
||||||
|
throw std::runtime_error(out.str());
|
||||||
|
}
|
||||||
|
|
||||||
|
void assertion_failed_msg(char const * expr, char const * msg, char const * function, char const * file, long line)
|
||||||
|
{
|
||||||
|
std::ostringstream out;
|
||||||
|
out << "Assertion failure int " << function << " " << file << ":" << line << "\n"<< msg;
|
||||||
|
throw std::runtime_error(out.str());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
@ -1,6 +1,9 @@
|
||||||
TEMPLATE = subdirs
|
TEMPLATE = subdirs
|
||||||
|
|
||||||
SUBDIRS += src
|
DEFINES += BOOST_ENABLE_ASSERT_HANDLER
|
||||||
|
|
||||||
|
SUBDIRS += src \
|
||||||
|
core
|
||||||
|
|
||||||
CONFIG(debug, debug|release) {
|
CONFIG(debug, debug|release) {
|
||||||
SUBDIRS += tests
|
SUBDIRS += tests
|
||||||
|
|
|
||||||
|
|
@ -57,7 +57,10 @@ ConnectionConfig::ConnectionConfig()
|
||||||
|
|
||||||
void ConnectionConfig::setName(std::string desc)
|
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
|
const std::string& ConnectionConfig::name() const
|
||||||
|
|
@ -67,7 +70,10 @@ const std::string& ConnectionConfig::name() const
|
||||||
|
|
||||||
void ConnectionConfig::setHost(std::string host)
|
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
|
const std::string& ConnectionConfig::host() const
|
||||||
|
|
@ -77,7 +83,10 @@ const std::string& ConnectionConfig::host() const
|
||||||
|
|
||||||
void ConnectionConfig::setHostAddr(std::string v)
|
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
|
const std::string& ConnectionConfig::hostAddr() const
|
||||||
|
|
@ -87,7 +96,11 @@ const std::string& ConnectionConfig::hostAddr() const
|
||||||
|
|
||||||
void ConnectionConfig::setPort(unsigned short port)
|
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
|
unsigned short ConnectionConfig::port() const
|
||||||
|
|
@ -97,7 +110,11 @@ unsigned short ConnectionConfig::port() const
|
||||||
|
|
||||||
void ConnectionConfig::setUser(std::string v)
|
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
|
const std::string& ConnectionConfig::user() const
|
||||||
|
|
@ -107,7 +124,10 @@ const std::string& ConnectionConfig::user() const
|
||||||
|
|
||||||
void ConnectionConfig::setPassword(std::string v)
|
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
|
const std::string& ConnectionConfig::password() const
|
||||||
|
|
@ -117,7 +137,10 @@ const std::string& ConnectionConfig::password() const
|
||||||
|
|
||||||
void ConnectionConfig::setDbname(std::string v)
|
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
|
const std::string& ConnectionConfig::dbname() const
|
||||||
|
|
@ -127,7 +150,11 @@ const std::string& ConnectionConfig::dbname() const
|
||||||
|
|
||||||
void ConnectionConfig::setSslMode(SslMode m)
|
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
|
SslMode ConnectionConfig::sslMode() const
|
||||||
|
|
@ -137,7 +164,10 @@ SslMode ConnectionConfig::sslMode() const
|
||||||
|
|
||||||
void ConnectionConfig::setSslCert(std::string v)
|
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
|
const std::string& ConnectionConfig::sslCert() const
|
||||||
|
|
@ -147,7 +177,10 @@ const std::string& ConnectionConfig::sslCert() const
|
||||||
|
|
||||||
void ConnectionConfig::setSslKey(std::string v)
|
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
|
const std::string& ConnectionConfig::sslKey() const
|
||||||
|
|
@ -157,7 +190,10 @@ const std::string& ConnectionConfig::sslKey() const
|
||||||
|
|
||||||
void ConnectionConfig::setSslRootCert(std::string v)
|
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
|
const std::string& ConnectionConfig::sslRootCert() const
|
||||||
|
|
@ -167,7 +203,10 @@ const std::string& ConnectionConfig::sslRootCert() const
|
||||||
|
|
||||||
void ConnectionConfig::setSslCrl(std::string v)
|
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
|
const std::string& ConnectionConfig::sslCrl() const
|
||||||
|
|
@ -210,3 +249,13 @@ bool ConnectionConfig::isSameDatabase(const ConnectionConfig &rhs) const
|
||||||
&& m_password == rhs.m_password
|
&& m_password == rhs.m_password
|
||||||
&& m_dbname == rhs.m_dbname;
|
&& m_dbname == rhs.m_dbname;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool ConnectionConfig::dirty() const
|
||||||
|
{
|
||||||
|
return m_dirty;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ConnectionConfig::clean()
|
||||||
|
{
|
||||||
|
m_dirty = false;
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -13,6 +13,12 @@ enum class SslMode {
|
||||||
verify_full=5
|
verify_full=5
|
||||||
};
|
};
|
||||||
|
|
||||||
|
enum class PasswordMode {
|
||||||
|
Unsave,
|
||||||
|
Encrypted,
|
||||||
|
DontSave
|
||||||
|
};
|
||||||
|
|
||||||
class ConnectionConfig {
|
class ConnectionConfig {
|
||||||
public:
|
public:
|
||||||
ConnectionConfig();
|
ConnectionConfig();
|
||||||
|
|
@ -57,6 +63,9 @@ public:
|
||||||
const char * const * getValues() const;
|
const char * const * getValues() const;
|
||||||
|
|
||||||
bool isSameDatabase(const ConnectionConfig &rhs) const;
|
bool isSameDatabase(const ConnectionConfig &rhs) const;
|
||||||
|
|
||||||
|
bool dirty() const;
|
||||||
|
void clean();
|
||||||
private:
|
private:
|
||||||
std::string m_name;
|
std::string m_name;
|
||||||
std::string m_host;
|
std::string m_host;
|
||||||
|
|
@ -75,6 +84,8 @@ private:
|
||||||
|
|
||||||
std::string m_applicationName;
|
std::string m_applicationName;
|
||||||
|
|
||||||
|
bool m_dirty = false;
|
||||||
|
|
||||||
static std::vector<const char*> s_keywords;
|
static std::vector<const char*> s_keywords;
|
||||||
mutable std::vector<const char*> m_values;
|
mutable std::vector<const char*> m_values;
|
||||||
};
|
};
|
||||||
|
|
|
||||||
148
src/ConnectionList.cpp
Normal file
148
src/ConnectionList.cpp
Normal 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
54
src/ConnectionList.h
Normal 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
|
||||||
|
|
@ -1,74 +1,27 @@
|
||||||
#include "connectionlistmodel.h"
|
#include "ConnectionListModel.h"
|
||||||
#include <QDir>
|
#include "ConnectionList.h"
|
||||||
#include <QStandardPaths>
|
|
||||||
#include <QSettings>
|
|
||||||
#include "scopeguard.h"
|
#include "scopeguard.h"
|
||||||
|
#include "util.h"
|
||||||
|
|
||||||
inline QString stdStrToQ(const std::string &s)
|
#include <botan/cryptobox.h>
|
||||||
{
|
|
||||||
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());
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/** Saves a connection configuration.
|
ConnectionListModel::ConnectionListModel(ConnectionList *conns, QObject *parent)
|
||||||
|
|
||||||
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)
|
|
||||||
: QAbstractListModel(parent)
|
: QAbstractListModel(parent)
|
||||||
|
, m_connections(conns)
|
||||||
{
|
{
|
||||||
load();
|
}
|
||||||
|
|
||||||
|
ConnectionListModel::~ConnectionListModel()
|
||||||
|
{
|
||||||
|
delete m_connections;
|
||||||
}
|
}
|
||||||
|
|
||||||
int ConnectionListModel::rowCount(const QModelIndex &parent) const
|
int ConnectionListModel::rowCount(const QModelIndex &parent) const
|
||||||
{
|
{
|
||||||
int result = 0;
|
int result = 0;
|
||||||
if (parent == QModelIndex()) {
|
if (parent == QModelIndex()) {
|
||||||
result = m_connections.size();
|
result = m_connections->size();
|
||||||
}
|
}
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
@ -84,7 +37,7 @@ QVariant ConnectionListModel::data(const QModelIndex &index, int role) const
|
||||||
if (role == Qt::DisplayRole || role == Qt::EditRole) {
|
if (role == Qt::DisplayRole || role == Qt::EditRole) {
|
||||||
int row = index.row();
|
int row = index.row();
|
||||||
int col = index.column();
|
int col = index.column();
|
||||||
const ConnectionConfig& cfg = m_connections.at(row).m_config;
|
const ConnectionConfig& cfg = m_connections->getConfigByIdx(row);
|
||||||
switch (col) {
|
switch (col) {
|
||||||
case 0:
|
case 0:
|
||||||
result = makeLongDescription(cfg);
|
result = makeLongDescription(cfg);
|
||||||
|
|
@ -119,9 +72,10 @@ bool ConnectionListModel::setData(const QModelIndex &index, const QVariant &valu
|
||||||
if (role == Qt::EditRole) {
|
if (role == Qt::EditRole) {
|
||||||
int row = index.row();
|
int row = index.row();
|
||||||
int col = index.column();
|
int col = index.column();
|
||||||
auto& elem = m_connections.at(row);
|
// auto& elem = m_connections.at(row);
|
||||||
elem.m_dirty = true;
|
// elem.m_dirty = true;
|
||||||
ConnectionConfig& cfg = elem.m_config;
|
// ConnectionConfig& cfg = elem.m_config;
|
||||||
|
ConnectionConfig& cfg = m_connections->getConfigByIdx(row);
|
||||||
if (col > 0) {
|
if (col > 0) {
|
||||||
result = true;
|
result = true;
|
||||||
}
|
}
|
||||||
|
|
@ -158,7 +112,7 @@ Qt::ItemFlags ConnectionListModel::flags(const QModelIndex &index) const
|
||||||
{
|
{
|
||||||
Qt::ItemFlags result;
|
Qt::ItemFlags result;
|
||||||
int row = index.row();
|
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;
|
result = Qt::ItemIsSelectable | Qt::ItemIsEditable | Qt::ItemIsEnabled;
|
||||||
}
|
}
|
||||||
return result;
|
return result;
|
||||||
|
|
@ -180,17 +134,17 @@ QString ConnectionListModel::makeLongDescription(const ConnectionConfig &cfg)
|
||||||
return stdStrToQ(result);
|
return stdStrToQ(result);
|
||||||
}
|
}
|
||||||
|
|
||||||
void ConnectionListModel::add(const ConnectionConfig &cfg)
|
void ConnectionListModel::newItem()
|
||||||
{
|
{
|
||||||
m_connections.emplace_back(QUuid::createUuid(), cfg);
|
int i = m_connections->createNew();
|
||||||
auto idx = createIndex(m_connections.size()-1, 0);
|
auto idx = createIndex(i, 0);
|
||||||
emit dataChanged(idx, idx);
|
emit dataChanged(idx, idx);
|
||||||
}
|
}
|
||||||
|
|
||||||
Expected<ConnectionConfig> ConnectionListModel::get(int row)
|
Expected<ConnectionConfig> ConnectionListModel::get(int row)
|
||||||
{
|
{
|
||||||
if (row >= 0 && row < (int)m_connections.size()) {
|
if (row >= 0 && row < (int)m_connections->size()) {
|
||||||
return m_connections.at(row).m_config;
|
return m_connections->getConfigByIdx(row);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
return Expected<ConnectionConfig>::fromException(std::out_of_range("Invalid row"));
|
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 ConnectionListModel::removeRows(int row, int count, const QModelIndex &parent)
|
||||||
{
|
{
|
||||||
bool result = false;
|
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);
|
beginRemoveRows(parent, row, row + count -1);
|
||||||
SCOPE_EXIT { endRemoveRows(); };
|
SCOPE_EXIT { endRemoveRows(); };
|
||||||
|
|
||||||
auto f = m_connections.begin() + row;
|
m_connections->remove(row, count);
|
||||||
auto l = f + count;
|
|
||||||
deleteFromIni(f, l);
|
|
||||||
m_connections.erase(f, l);
|
|
||||||
result = true;
|
result = true;
|
||||||
|
|
||||||
}
|
}
|
||||||
return result;
|
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()
|
||||||
void ConnectionListModel::load()
|
//{
|
||||||
{
|
// m_connections->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::save()
|
void ConnectionListModel::save()
|
||||||
{
|
{
|
||||||
QString file_name = iniFileName();
|
m_connections->save();
|
||||||
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);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void ConnectionListModel::save(int index)
|
void ConnectionListModel::save(int index)
|
||||||
{
|
{
|
||||||
if (index >= 0 && index < (int)m_connections.size()) {
|
m_connections->save(index);
|
||||||
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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
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());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -5,11 +5,11 @@
|
||||||
#include <memory>
|
#include <memory>
|
||||||
|
|
||||||
#include <QAbstractListModel>
|
#include <QAbstractListModel>
|
||||||
#include <QUuid>
|
|
||||||
|
|
||||||
#include "connectionconfig.h"
|
#include "connectionconfig.h"
|
||||||
#include "expected.h"
|
#include "expected.h"
|
||||||
|
|
||||||
|
class ConnectionList;
|
||||||
|
|
||||||
/** \brief Model class for the list of connections.
|
/** \brief Model class for the list of connections.
|
||||||
*
|
*
|
||||||
|
|
@ -18,7 +18,9 @@
|
||||||
class ConnectionListModel : public QAbstractListModel {
|
class ConnectionListModel : public QAbstractListModel {
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
public:
|
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 rowCount(const QModelIndex &parent = QModelIndex()) const override;
|
||||||
virtual int columnCount(const QModelIndex &/*parent*/) 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 bool setData(const QModelIndex &index, const QVariant &value, int role = Qt::EditRole) override;
|
||||||
virtual Qt::ItemFlags flags(const QModelIndex &index) const override;
|
virtual Qt::ItemFlags flags(const QModelIndex &index) const override;
|
||||||
|
|
||||||
void add(const ConnectionConfig &cfg);
|
void newItem();
|
||||||
Expected<ConnectionConfig> get(int row);
|
Expected<ConnectionConfig> get(int row);
|
||||||
virtual bool removeRows(int row, int count, const QModelIndex &parent = QModelIndex()) override;
|
virtual bool removeRows(int row, int count, const QModelIndex &parent = QModelIndex()) override;
|
||||||
|
|
||||||
void load();
|
|
||||||
void save();
|
void save();
|
||||||
void save(int index);
|
void save(int index);
|
||||||
private:
|
private:
|
||||||
class LijstElem {
|
|
||||||
public:
|
|
||||||
QUuid m_uuid;
|
|
||||||
bool m_dirty = false;
|
|
||||||
ConnectionConfig m_config;
|
|
||||||
|
|
||||||
LijstElem(const QUuid id, const ConnectionConfig &cfg)
|
ConnectionList *m_connections;
|
||||||
: 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);
|
|
||||||
static QString iniFileName();
|
|
||||||
|
|
||||||
static QString makeLongDescription(const ConnectionConfig &cfg);
|
static QString makeLongDescription(const ConnectionConfig &cfg);
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -37,12 +37,12 @@ ConnectionManagerWindow::~ConnectionManagerWindow()
|
||||||
|
|
||||||
void ConnectionManagerWindow::on_actionAdd_Connection_triggered()
|
void ConnectionManagerWindow::on_actionAdd_Connection_triggered()
|
||||||
{
|
{
|
||||||
ConnectionConfig c;
|
// ConnectionConfig c;
|
||||||
c.setName("new");
|
// c.setName("new");
|
||||||
//m_listModel->add(c);
|
//m_listModel->add(c);
|
||||||
|
|
||||||
auto clm = m_masterController->getConnectionListModel();
|
auto clm = m_masterController->getConnectionListModel();
|
||||||
clm->add(c);
|
clm->newItem();
|
||||||
|
|
||||||
// Select the new row
|
// Select the new row
|
||||||
auto idx = clm->index(clm->rowCount() - 1, 0);
|
auto idx = clm->index(clm->rowCount() - 1, 0);
|
||||||
|
|
@ -110,3 +110,67 @@ void ConnectionManagerWindow::on_actionManage_server_triggered()
|
||||||
auto ci = ui->listView->selectionModel()->currentIndex();
|
auto ci = ui->listView->selectionModel()->currentIndex();
|
||||||
m_masterController->openServerWindowForConnection(ci.row());
|
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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -15,10 +15,8 @@ class QStandardItemModel;
|
||||||
/** \brief Class that holds glue code for the ConnectionManager UI.
|
/** \brief Class that holds glue code for the ConnectionManager UI.
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
class ConnectionManagerWindow : public QMainWindow
|
class ConnectionManagerWindow : public QMainWindow {
|
||||||
{
|
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
|
|
||||||
public:
|
public:
|
||||||
explicit ConnectionManagerWindow(MasterController *master, QWidget *parent = 0);
|
explicit ConnectionManagerWindow(MasterController *master, QWidget *parent = 0);
|
||||||
~ConnectionManagerWindow();
|
~ConnectionManagerWindow();
|
||||||
|
|
@ -27,15 +25,13 @@ private slots:
|
||||||
void on_actionAdd_Connection_triggered();
|
void on_actionAdd_Connection_triggered();
|
||||||
void on_currentChanged(const QModelIndex ¤t, const QModelIndex &previous);
|
void on_currentChanged(const QModelIndex ¤t, const QModelIndex &previous);
|
||||||
void on_actionDelete_connection_triggered();
|
void on_actionDelete_connection_triggered();
|
||||||
|
|
||||||
void on_actionConnect_triggered();
|
void on_actionConnect_triggered();
|
||||||
|
|
||||||
void on_actionQuit_application_triggered();
|
void on_actionQuit_application_triggered();
|
||||||
|
|
||||||
void on_actionBackup_database_triggered();
|
void on_actionBackup_database_triggered();
|
||||||
|
|
||||||
void on_actionManage_server_triggered();
|
void on_actionManage_server_triggered();
|
||||||
|
|
||||||
|
void on_testButton_clicked();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
Ui::ConnectionManagerWindow *ui;
|
Ui::ConnectionManagerWindow *ui;
|
||||||
QDataWidgetMapper *m_mapper = nullptr;
|
QDataWidgetMapper *m_mapper = nullptr;
|
||||||
|
|
|
||||||
|
|
@ -182,6 +182,13 @@
|
||||||
<item row="10" column="1">
|
<item row="10" column="1">
|
||||||
<widget class="QLineEdit" name="edtCrl"/>
|
<widget class="QLineEdit" name="edtCrl"/>
|
||||||
</item>
|
</item>
|
||||||
|
<item row="11" column="0">
|
||||||
|
<widget class="QPushButton" name="testButton">
|
||||||
|
<property name="text">
|
||||||
|
<string>PushButton</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
</layout>
|
</layout>
|
||||||
</widget>
|
</widget>
|
||||||
</widget>
|
</widget>
|
||||||
|
|
|
||||||
156
src/Expected.h
156
src/Expected.h
|
|
@ -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
|
|
||||||
|
|
@ -1,6 +1,7 @@
|
||||||
#include "MasterController.h"
|
#include "MasterController.h"
|
||||||
#include "connectionmanagerwindow.h"
|
#include "ConnectionManagerWindow.h"
|
||||||
#include "connectionlistmodel.h"
|
#include "ConnectionList.h"
|
||||||
|
#include "ConnectionListModel.h"
|
||||||
#include "MainWindow.h"
|
#include "MainWindow.h"
|
||||||
#include "ServerWindow.h"
|
#include "ServerWindow.h"
|
||||||
|
|
||||||
|
|
@ -12,11 +13,14 @@ MasterController::~MasterController()
|
||||||
{
|
{
|
||||||
delete m_connectionManagerWindow;
|
delete m_connectionManagerWindow;
|
||||||
delete m_connectionListModel;
|
delete m_connectionListModel;
|
||||||
|
delete m_connectionList;
|
||||||
}
|
}
|
||||||
|
|
||||||
void MasterController::init()
|
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 = new ConnectionManagerWindow(this, nullptr);
|
||||||
m_connectionManagerWindow->show();
|
m_connectionManagerWindow->show();
|
||||||
|
|
|
||||||
|
|
@ -5,16 +5,18 @@
|
||||||
#include <map>
|
#include <map>
|
||||||
|
|
||||||
class ConnectionConfig;
|
class ConnectionConfig;
|
||||||
|
class ConnectionList;
|
||||||
class ConnectionListModel;
|
class ConnectionListModel;
|
||||||
class ConnectionManagerWindow;
|
class ConnectionManagerWindow;
|
||||||
|
|
||||||
/** \brief Controller class responsible for all things global.
|
/** \brief Controller class responsible for all things global.
|
||||||
*/
|
*/
|
||||||
class MasterController : public QObject
|
class MasterController : public QObject {
|
||||||
{
|
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
public:
|
public:
|
||||||
explicit MasterController(QObject *parent = 0);
|
explicit MasterController(QObject *parent = 0);
|
||||||
|
MasterController(const MasterController&) = delete;
|
||||||
|
MasterController &operator=(const MasterController&) = delete;
|
||||||
~MasterController();
|
~MasterController();
|
||||||
|
|
||||||
void init();
|
void init();
|
||||||
|
|
@ -33,7 +35,7 @@ signals:
|
||||||
public slots:
|
public slots:
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
ConnectionList *m_connectionList = nullptr;
|
||||||
ConnectionListModel *m_connectionListModel = nullptr;
|
ConnectionListModel *m_connectionListModel = nullptr;
|
||||||
ConnectionManagerWindow *m_connectionManagerWindow = nullptr;
|
ConnectionManagerWindow *m_connectionManagerWindow = nullptr;
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -22,6 +22,7 @@ QWidget *ParamTypeDelegate::createEditor(QWidget *parent,
|
||||||
QWidget *w = nullptr;
|
QWidget *w = nullptr;
|
||||||
|
|
||||||
QComboBox *cmbbx = new QComboBox(parent);
|
QComboBox *cmbbx = new QComboBox(parent);
|
||||||
|
cmbbx->setMaxVisibleItems(32);
|
||||||
cmbbx->setModel(m_typeSelectionModel);
|
cmbbx->setModel(m_typeSelectionModel);
|
||||||
w = cmbbx;
|
w = cmbbx;
|
||||||
|
|
||||||
|
|
|
||||||
134
src/SqlLexer.cpp
134
src/SqlLexer.cpp
|
|
@ -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;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
@ -133,7 +133,10 @@ void SqlSyntaxHighlighter::setTypes(const PgTypeContainer *types)
|
||||||
|
|
||||||
void SqlSyntaxHighlighter::highlightBlock(const QString &text)
|
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;
|
int startpos, length;
|
||||||
BasicTokenType tokentype;
|
BasicTokenType tokentype;
|
||||||
QString s;
|
QString s;
|
||||||
|
|
@ -151,6 +154,9 @@ void SqlSyntaxHighlighter::highlightBlock(const QString &text)
|
||||||
setFormat(startpos, length, m_typeFormat);
|
setFormat(startpos, length, m_typeFormat);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
case BasicTokenType::OpenBlockComment:
|
||||||
|
setCurrentBlockState((int)lexer.currentState());
|
||||||
|
case BasicTokenType::BlockComment:
|
||||||
case BasicTokenType::Comment:
|
case BasicTokenType::Comment:
|
||||||
setFormat(startpos, length, m_commentFormat);
|
setFormat(startpos, length, m_commentFormat);
|
||||||
break;
|
break;
|
||||||
|
|
|
||||||
35
src/src.pro
35
src/src.pro
|
|
@ -4,6 +4,7 @@
|
||||||
#
|
#
|
||||||
#-------------------------------------------------
|
#-------------------------------------------------
|
||||||
|
|
||||||
|
CONFIG += c++11
|
||||||
QT += core gui
|
QT += core gui
|
||||||
|
|
||||||
greaterThan(QT_MAJOR_VERSION, 4): QT += widgets sql
|
greaterThan(QT_MAJOR_VERSION, 4): QT += widgets sql
|
||||||
|
|
@ -11,9 +12,17 @@ greaterThan(QT_MAJOR_VERSION, 4): QT += widgets sql
|
||||||
TARGET = pglab
|
TARGET = pglab
|
||||||
TEMPLATE = app
|
TEMPLATE = app
|
||||||
|
|
||||||
INCLUDEPATH += C:\prog\include
|
INCLUDEPATH += C:\prog\include C:\VSproj\boost_1_63_0
|
||||||
DEFINES += WIN32_LEAN_AND_MEAN NOMINMAX
|
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
|
win32:RC_ICONS += pglab.ico
|
||||||
|
|
||||||
|
|
@ -29,7 +38,6 @@ SOURCES += main.cpp\
|
||||||
tsqueue.cpp \
|
tsqueue.cpp \
|
||||||
win32event.cpp \
|
win32event.cpp \
|
||||||
waithandlelist.cpp \
|
waithandlelist.cpp \
|
||||||
CsvWriter.cpp \
|
|
||||||
DatabaseWindow.cpp \
|
DatabaseWindow.cpp \
|
||||||
ConnectionManagerWindow.cpp \
|
ConnectionManagerWindow.cpp \
|
||||||
ConnectionListModel.cpp \
|
ConnectionListModel.cpp \
|
||||||
|
|
@ -53,7 +61,6 @@ SOURCES += main.cpp\
|
||||||
ParamListModel.cpp \
|
ParamListModel.cpp \
|
||||||
MainWindow.cpp \
|
MainWindow.cpp \
|
||||||
SqlSyntaxHighlighter.cpp \
|
SqlSyntaxHighlighter.cpp \
|
||||||
SqlLexer.cpp \
|
|
||||||
ServerWindow.cpp \
|
ServerWindow.cpp \
|
||||||
ASyncWindow.cpp \
|
ASyncWindow.cpp \
|
||||||
DatabasesTableModel.cpp \
|
DatabasesTableModel.cpp \
|
||||||
|
|
@ -65,7 +72,8 @@ SOURCES += main.cpp\
|
||||||
PgAuthIdContainer.cpp \
|
PgAuthIdContainer.cpp \
|
||||||
Pgsql_Result.cpp \
|
Pgsql_Result.cpp \
|
||||||
Pgsql_Row.cpp \
|
Pgsql_Row.cpp \
|
||||||
Pgsql_Value.cpp
|
Pgsql_Value.cpp \
|
||||||
|
ConnectionList.cpp
|
||||||
|
|
||||||
HEADERS += \
|
HEADERS += \
|
||||||
sqlparser.h \
|
sqlparser.h \
|
||||||
|
|
@ -78,13 +86,10 @@ HEADERS += \
|
||||||
tsqueue.h \
|
tsqueue.h \
|
||||||
win32event.h \
|
win32event.h \
|
||||||
waithandlelist.h \
|
waithandlelist.h \
|
||||||
CsvWriter.h \
|
|
||||||
DatabaseWindow.h \
|
DatabaseWindow.h \
|
||||||
ConnectionManagerWindow.h \
|
ConnectionManagerWindow.h \
|
||||||
ConnectionListModel.h \
|
ConnectionListModel.h \
|
||||||
ConnectionConfig.h \
|
ConnectionConfig.h \
|
||||||
ScopeGuard.h \
|
|
||||||
Expected.h \
|
|
||||||
QueryTab.h \
|
QueryTab.h \
|
||||||
stopwatch.h \
|
stopwatch.h \
|
||||||
util.h \
|
util.h \
|
||||||
|
|
@ -117,7 +122,8 @@ HEADERS += \
|
||||||
PgAuthIdContainer.h \
|
PgAuthIdContainer.h \
|
||||||
Pgsql_Result.h \
|
Pgsql_Result.h \
|
||||||
Pgsql_Row.h \
|
Pgsql_Row.h \
|
||||||
Pgsql_Value.h
|
Pgsql_Value.h \
|
||||||
|
ConnectionList.h
|
||||||
|
|
||||||
FORMS += mainwindow.ui \
|
FORMS += mainwindow.ui \
|
||||||
DatabaseWindow.ui \
|
DatabaseWindow.ui \
|
||||||
|
|
@ -132,3 +138,14 @@ RESOURCES += \
|
||||||
resources.qrc
|
resources.qrc
|
||||||
|
|
||||||
QMAKE_LFLAGS_WINDOWS = /SUBSYSTEM:WINDOWS,5.01
|
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
|
||||||
|
|
|
||||||
|
|
@ -20,7 +20,6 @@ public:
|
||||||
QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override;
|
QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
// const PgTypeContainer* m_types;
|
|
||||||
std::vector<QString> m_types;
|
std::vector<QString> m_types;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -4,6 +4,7 @@
|
||||||
#include <QTextStream>
|
#include <QTextStream>
|
||||||
#include <QClipboard>
|
#include <QClipboard>
|
||||||
#include <sstream>
|
#include <sstream>
|
||||||
|
#include <stdexcept>
|
||||||
|
|
||||||
// Supported range from microseconds to seconds
|
// Supported range from microseconds to seconds
|
||||||
// min:sec to hours::min::sec
|
// min:sec to hours::min::sec
|
||||||
|
|
@ -134,3 +135,5 @@ QString ConvertToMultiLineCString(const QString &in)
|
||||||
out.append('"');
|
out.append('"');
|
||||||
return out;
|
return out;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
18
src/util.h
18
src/util.h
|
|
@ -1,6 +1,7 @@
|
||||||
#ifndef UTIL_H
|
#ifndef UTIL_H
|
||||||
#define UTIL_H
|
#define UTIL_H
|
||||||
|
|
||||||
|
#include <string>
|
||||||
#include <QString>
|
#include <QString>
|
||||||
#include <QTableView>
|
#include <QTableView>
|
||||||
|
|
||||||
|
|
@ -9,6 +10,23 @@ void copySelectionToClipboard(const QTableView *view);
|
||||||
QString ConvertToMultiLineCString(const QString &in);
|
QString ConvertToMultiLineCString(const QString &in);
|
||||||
void exportTable(const QTableView *view, QTextStream &out);
|
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 {
|
namespace std {
|
||||||
|
|
||||||
template <>
|
template <>
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,8 @@
|
||||||
#include "tst_mycase.h"
|
#include "tst_CsvWriter.h"
|
||||||
|
#include "tst_expected.h"
|
||||||
|
#include "tst_PasswordManager.h"
|
||||||
|
#include "tst_scopeguard.h"
|
||||||
|
#include "tst_SqlLexer.h"
|
||||||
|
|
||||||
#include <gtest/gtest.h>
|
#include <gtest/gtest.h>
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -8,6 +8,24 @@ CONFIG += qt
|
||||||
|
|
||||||
QT += core
|
QT += core
|
||||||
|
|
||||||
HEADERS += tst_mycase.h
|
HEADERS += \
|
||||||
|
tst_expected.h \
|
||||||
|
tst_SqlLexer.h \
|
||||||
|
tst_scopeguard.h \
|
||||||
|
tst_CsvWriter.h \
|
||||||
|
tst_PasswordManager.h
|
||||||
|
|
||||||
SOURCES += main.cpp ../../../src/SqlLexer.cpp
|
SOURCES += main.cpp
|
||||||
|
|
||||||
|
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 += C:\prog\include C:\VSproj\boost_1_63_0
|
||||||
|
INCLUDEPATH += $$PWD/../../../core
|
||||||
|
DEPENDPATH += $$PWD/../../../core
|
||||||
|
LIBS += c:\prog\lib\botand_imp.lib
|
||||||
|
|
||||||
|
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
|
||||||
|
|
|
||||||
113
tests/auto/mycase/tst_CsvWriter.h
Normal file
113
tests/auto/mycase/tst_CsvWriter.h
Normal file
|
|
@ -0,0 +1,113 @@
|
||||||
|
#include <gtest/gtest.h>
|
||||||
|
#include <gmock/gmock-matchers.h>
|
||||||
|
#include "CsvWriter.h"
|
||||||
|
#include <QTextStream>
|
||||||
|
#include <QByteArray>
|
||||||
|
|
||||||
|
using namespace testing;
|
||||||
|
|
||||||
|
|
||||||
|
TEST(CsvWriter, one_row_two_numbers)
|
||||||
|
{
|
||||||
|
QString result;
|
||||||
|
QTextStream stream(&result);
|
||||||
|
CsvWriter writer(&stream);
|
||||||
|
writer.setQuote('"');
|
||||||
|
writer.setSeperator(',');
|
||||||
|
writer.writeField("1");
|
||||||
|
writer.writeField("2");
|
||||||
|
writer.nextRow();
|
||||||
|
|
||||||
|
QString expected = QString::fromUtf8("1,2\n");
|
||||||
|
ASSERT_THAT(result, Eq(expected));
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(CsvWriter, one_row_one_number_one_unquoted_string)
|
||||||
|
{
|
||||||
|
QString result;
|
||||||
|
QTextStream stream(&result);
|
||||||
|
CsvWriter writer(&stream);
|
||||||
|
writer.setQuote('"');
|
||||||
|
writer.setSeperator(',');
|
||||||
|
writer.writeField("1");
|
||||||
|
writer.writeField("hello");
|
||||||
|
writer.nextRow();
|
||||||
|
|
||||||
|
QString expected = QString::fromUtf8("1,hello\n");
|
||||||
|
ASSERT_THAT(result, Eq(expected));
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(CsvWriter, one_row_one_number_one_quoted_string)
|
||||||
|
{
|
||||||
|
QString result;
|
||||||
|
QTextStream stream(&result);
|
||||||
|
CsvWriter writer(&stream);
|
||||||
|
writer.setQuote('"');
|
||||||
|
writer.setSeperator(',');
|
||||||
|
writer.writeField("1");
|
||||||
|
writer.writeField("hel,lo");
|
||||||
|
writer.nextRow();
|
||||||
|
|
||||||
|
QString expected = QString::fromUtf8("1,\"hel,lo\"\n");
|
||||||
|
ASSERT_THAT(result, Eq(expected));
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(CsvWriter, newline_in_field)
|
||||||
|
{
|
||||||
|
QString result;
|
||||||
|
QTextStream stream(&result);
|
||||||
|
CsvWriter writer(&stream);
|
||||||
|
writer.setQuote('"');
|
||||||
|
writer.setSeperator(',');
|
||||||
|
writer.writeField("1");
|
||||||
|
writer.writeField("hel\nlo");
|
||||||
|
writer.nextRow();
|
||||||
|
|
||||||
|
QString expected = QString::fromUtf8("1,\"hel\nlo\"\n");
|
||||||
|
ASSERT_THAT(result, Eq(expected));
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(CsvWriter, escape_quote)
|
||||||
|
{
|
||||||
|
QString result;
|
||||||
|
QTextStream stream(&result);
|
||||||
|
CsvWriter writer(&stream);
|
||||||
|
writer.setQuote('"');
|
||||||
|
writer.setSeperator(',');
|
||||||
|
writer.writeField("1");
|
||||||
|
writer.writeField("hel\"lo");
|
||||||
|
writer.nextRow();
|
||||||
|
|
||||||
|
QString expected = QString::fromUtf8("1,\"hel\"\"lo\"\n");
|
||||||
|
ASSERT_THAT(result, Eq(expected));
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(CsvWriter, non_default_seperator)
|
||||||
|
{
|
||||||
|
QString result;
|
||||||
|
QTextStream stream(&result);
|
||||||
|
CsvWriter writer(&stream);
|
||||||
|
writer.setQuote('"');
|
||||||
|
writer.setSeperator('\t');
|
||||||
|
writer.writeField("1");
|
||||||
|
writer.writeField("hel,lo");
|
||||||
|
writer.nextRow();
|
||||||
|
|
||||||
|
QString expected = QString::fromUtf8("1\thel,lo\n");
|
||||||
|
ASSERT_THAT(result, Eq(expected));
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(CsvWriter, non_default_quote)
|
||||||
|
{
|
||||||
|
QString result;
|
||||||
|
QTextStream stream(&result);
|
||||||
|
CsvWriter writer(&stream);
|
||||||
|
writer.setQuote('*');
|
||||||
|
writer.setSeperator('\t');
|
||||||
|
writer.writeField("1");
|
||||||
|
writer.writeField("hel\tlo");
|
||||||
|
writer.nextRow();
|
||||||
|
|
||||||
|
QString expected = QString::fromUtf8("1\t*hel\tlo*\n");
|
||||||
|
ASSERT_THAT(result, Eq(expected));
|
||||||
|
}
|
||||||
73
tests/auto/mycase/tst_PasswordManager.h
Normal file
73
tests/auto/mycase/tst_PasswordManager.h
Normal file
|
|
@ -0,0 +1,73 @@
|
||||||
|
#include <gtest/gtest.h>
|
||||||
|
#include <gmock/gmock-matchers.h>
|
||||||
|
#include "PasswordManager.h"
|
||||||
|
|
||||||
|
using namespace testing;
|
||||||
|
|
||||||
|
|
||||||
|
TEST(PasswordManager, initial_changeMasterPassword_returns_true)
|
||||||
|
{
|
||||||
|
PasswordManager pwm;
|
||||||
|
|
||||||
|
auto res = pwm.changeMasterPassword("", "my test passphrase");
|
||||||
|
ASSERT_NO_THROW( res.get() );
|
||||||
|
ASSERT_THAT( res.get(), Eq(true) );
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(PasswordManager, unlock_succeeds)
|
||||||
|
{
|
||||||
|
PasswordManager pwm;
|
||||||
|
|
||||||
|
std::string passphrase = "my test passphrase";
|
||||||
|
|
||||||
|
auto res = pwm.changeMasterPassword("", passphrase);
|
||||||
|
ASSERT_NO_THROW( res.get() );
|
||||||
|
ASSERT_THAT( res.get(), Eq(true) );
|
||||||
|
|
||||||
|
auto res2 = pwm.unlock(passphrase);
|
||||||
|
ASSERT_NO_THROW( res2.get() );
|
||||||
|
ASSERT_THAT( res2.get(), Eq(true) );
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(PasswordManager, unlock_fails)
|
||||||
|
{
|
||||||
|
PasswordManager pwm;
|
||||||
|
|
||||||
|
std::string passphrase = "my test passphrase";
|
||||||
|
|
||||||
|
auto res = pwm.changeMasterPassword("", passphrase);
|
||||||
|
ASSERT_NO_THROW( res.get() );
|
||||||
|
ASSERT_THAT( res.get(), Eq(true) );
|
||||||
|
|
||||||
|
auto res2 = pwm.unlock(passphrase + "2");
|
||||||
|
ASSERT_NO_THROW( res2.get() );
|
||||||
|
ASSERT_THAT( res2.get(), Eq(false) );
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(PasswordManager, test_save_get)
|
||||||
|
{
|
||||||
|
PasswordManager pwm;
|
||||||
|
|
||||||
|
std::string passphrase = "my test passphrase";
|
||||||
|
|
||||||
|
auto res = pwm.changeMasterPassword("", passphrase);
|
||||||
|
ASSERT_NO_THROW( res.get() );
|
||||||
|
ASSERT_THAT( res.get(), Eq(true) );
|
||||||
|
|
||||||
|
// auto res2 = pwm.unlock(passphrase + "2");
|
||||||
|
// ASSERT_NO_THROW( res2.get() );
|
||||||
|
// ASSERT_THAT( res2.get(), Eq(false) );
|
||||||
|
|
||||||
|
const std::string password = "password123";
|
||||||
|
const std::string key = "abc";
|
||||||
|
|
||||||
|
auto res2 = pwm.savePassword(key, password);
|
||||||
|
ASSERT_THAT( res2.valid(), Eq(true) );
|
||||||
|
|
||||||
|
std::string result;
|
||||||
|
auto res3 = pwm.getPassword(key, result);
|
||||||
|
ASSERT_THAT( res3.valid(), Eq(true) );
|
||||||
|
ASSERT_THAT( res3.get(), Eq(true) );
|
||||||
|
ASSERT_THAT( result, Eq(password) );
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -1,16 +1,11 @@
|
||||||
#include <gtest/gtest.h>
|
#include <gtest/gtest.h>
|
||||||
#include <gmock/gmock-matchers.h>
|
#include <gmock/gmock-matchers.h>
|
||||||
#include "../../../src/SqlLexer.h"
|
#include "SqlLexer.h"
|
||||||
|
|
||||||
using namespace testing;
|
using namespace testing;
|
||||||
|
|
||||||
TEST(mycase, myset)
|
|
||||||
{
|
|
||||||
EXPECT_EQ(1, 1);
|
|
||||||
ASSERT_THAT(0, Eq(0));
|
|
||||||
}
|
|
||||||
|
|
||||||
TEST(mycase, lexer)
|
TEST(SqlLexer, lexer)
|
||||||
{
|
{
|
||||||
QString input = " SELECT ";
|
QString input = " SELECT ";
|
||||||
SqlLexer lexer(input, LexerState::Null);
|
SqlLexer lexer(input, LexerState::Null);
|
||||||
|
|
@ -26,7 +21,7 @@ TEST(mycase, lexer)
|
||||||
ASSERT_THAT( out, Eq(QString("SELECT")) );
|
ASSERT_THAT( out, Eq(QString("SELECT")) );
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(mycase, lexer_quote_in_string)
|
TEST(SqlLexer, lexer_quote_in_string)
|
||||||
{
|
{
|
||||||
QString input = " 'abc''def' ";
|
QString input = " 'abc''def' ";
|
||||||
SqlLexer lexer(input, LexerState::Null);
|
SqlLexer lexer(input, LexerState::Null);
|
||||||
203
tests/auto/mycase/tst_expected.h
Normal file
203
tests/auto/mycase/tst_expected.h
Normal file
|
|
@ -0,0 +1,203 @@
|
||||||
|
#include <gtest/gtest.h>
|
||||||
|
#include <gmock/gmock-matchers.h>
|
||||||
|
#include "Expected.h"
|
||||||
|
|
||||||
|
using namespace testing;
|
||||||
|
|
||||||
|
Expected<int> getAnswerToEverything() { return 42; }
|
||||||
|
|
||||||
|
TEST(expected, valid_when_valid_returns_true)
|
||||||
|
{
|
||||||
|
Expected<int> v = getAnswerToEverything();
|
||||||
|
ASSERT_THAT(v.valid(), Eq(true));
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(expected, get_when_valid_returns_value)
|
||||||
|
{
|
||||||
|
Expected<int> v = getAnswerToEverything();
|
||||||
|
ASSERT_THAT(v.get(), Eq(42));
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(expected, hasException_when_valid_returns_false)
|
||||||
|
{
|
||||||
|
Expected<int> v = getAnswerToEverything();
|
||||||
|
ASSERT_THAT(v.hasException<std::exception>(), Eq(false));
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(expected, T_fromException_is_not_valid)
|
||||||
|
{
|
||||||
|
auto e = Expected<int>::fromException(std::runtime_error("hello"));
|
||||||
|
ASSERT_THAT(e.valid(), Eq(false));
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(expected, T_fromException_get_thows)
|
||||||
|
{
|
||||||
|
auto e = Expected<int>::fromException(std::runtime_error("hello"));
|
||||||
|
ASSERT_THROW (e.get(), std::runtime_error);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(expected, T_fromException_has_exception_true)
|
||||||
|
{
|
||||||
|
auto e = Expected<int>::fromException(std::runtime_error("hello"));
|
||||||
|
ASSERT_THAT(e.hasException<std::runtime_error>(), Eq(true));
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(expected, T_fromException_has_exception_false)
|
||||||
|
{
|
||||||
|
auto e = Expected<int>::fromException(std::runtime_error("hello"));
|
||||||
|
ASSERT_THAT(e.hasException<std::logic_error>(), Eq(false));
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(expected, T_fromException_has_derived_exception)
|
||||||
|
{
|
||||||
|
auto e = Expected<int>::fromException(std::runtime_error("hello"));
|
||||||
|
ASSERT_THAT(e.hasException<std::exception>(), Eq(true));
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(expected, T_fromCode_is_valid)
|
||||||
|
{
|
||||||
|
auto e = Expected<int>::fromCode([]() -> int { return 42; });
|
||||||
|
ASSERT_THAT(e.valid(), Eq(true));
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(expected, T_fromCode_get)
|
||||||
|
{
|
||||||
|
auto e = Expected<int>::fromCode([]() -> int { return 42; });
|
||||||
|
ASSERT_THAT(e.get(), Eq(42));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
TEST(expected, T_fromCode_E_is_not_valid)
|
||||||
|
{
|
||||||
|
auto e = Expected<int>::fromCode([]() -> int { throw std::runtime_error("hello"); });
|
||||||
|
ASSERT_THAT(e.valid(), Eq(false));
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(expected, T_fromCode_E_get_thows)
|
||||||
|
{
|
||||||
|
auto e = Expected<int>::fromCode([]() -> int { throw std::runtime_error("hello"); });
|
||||||
|
ASSERT_THROW (e.get(), std::runtime_error);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(expected, T_fromCode_E_has_exception_true)
|
||||||
|
{
|
||||||
|
auto e = Expected<int>::fromCode([]() -> int { throw std::runtime_error("hello"); });
|
||||||
|
ASSERT_THAT(e.hasException<std::runtime_error>(), Eq(true));
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(expected, T_fromCode_E_has_exception_false)
|
||||||
|
{
|
||||||
|
auto e = Expected<int>::fromCode([]() -> int { throw std::runtime_error("hello"); });
|
||||||
|
ASSERT_THAT(e.hasException<std::logic_error>(), Eq(false));
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(expected, T_fromCode_E_has_derived_exception)
|
||||||
|
{
|
||||||
|
auto e = Expected<int>::fromCode([]() -> int { throw std::runtime_error("hello"); });
|
||||||
|
ASSERT_THAT(e.hasException<std::exception>(), Eq(true));
|
||||||
|
}
|
||||||
|
|
||||||
|
//Expected<int> getIntWithStdRuntimeError() { return Expected<void>(); }
|
||||||
|
|
||||||
|
Expected<void> getNothing() { return Expected<void>(); }
|
||||||
|
|
||||||
|
|
||||||
|
TEST(expected_void, valid_when_valid_returns_true)
|
||||||
|
{
|
||||||
|
Expected<void> v = getNothing();
|
||||||
|
ASSERT_THAT(v.valid(), Eq(true));
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(expected_void, get_when_valid_returns_value)
|
||||||
|
{
|
||||||
|
Expected<void> v = getNothing();
|
||||||
|
ASSERT_NO_THROW(v.get());
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(expected_void, hasException_when_valid_returns_false)
|
||||||
|
{
|
||||||
|
Expected<void> v = getNothing();
|
||||||
|
ASSERT_THAT(v.hasException<std::exception>(), Eq(false));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
TEST(expected_void, void_fromException_is_not_valid)
|
||||||
|
{
|
||||||
|
auto e = Expected<void>::fromException(std::runtime_error("hello"));
|
||||||
|
ASSERT_THAT(e.valid(), Eq(false));
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(expected_void, void_fromException_get_thows)
|
||||||
|
{
|
||||||
|
auto e = Expected<void>::fromException(std::runtime_error("hello"));
|
||||||
|
ASSERT_THROW (e.get(), std::runtime_error);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(expected_void, void_fromException_has_exception_true)
|
||||||
|
{
|
||||||
|
auto e = Expected<void>::fromException(std::runtime_error("hello"));
|
||||||
|
ASSERT_THAT(e.hasException<std::runtime_error>(), Eq(true));
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(expected_void, void_fromException_has_exception_false)
|
||||||
|
{
|
||||||
|
auto e = Expected<void>::fromException(std::runtime_error("hello"));
|
||||||
|
ASSERT_THAT(e.hasException<std::logic_error>(), Eq(false));
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(expected_void, void_fromException_has_derived_exception)
|
||||||
|
{
|
||||||
|
auto e = Expected<void>::fromException(std::runtime_error("hello"));
|
||||||
|
ASSERT_THAT(e.hasException<std::exception>(), Eq(true));
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(expected_void, void_fromCode_is_valid)
|
||||||
|
{
|
||||||
|
auto e = Expected<void>::fromCode([]() -> void { });
|
||||||
|
ASSERT_THAT(e.valid(), Eq(true));
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(expected_void, void_fromCode_get)
|
||||||
|
{
|
||||||
|
auto e = Expected<void>::fromCode([]() -> void { });
|
||||||
|
ASSERT_NO_THROW(e.get());
|
||||||
|
}
|
||||||
|
|
||||||
|
void expected_void_throws_func()
|
||||||
|
{
|
||||||
|
throw std::runtime_error("hello");
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(expected_void, void_fromCode_E_is_not_valid)
|
||||||
|
{
|
||||||
|
auto e = Expected<void>::fromCode(expected_void_throws_func);
|
||||||
|
ASSERT_THAT(e.valid(), Eq(false));
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(expected_void, void_fromCode_E_get_thows)
|
||||||
|
{
|
||||||
|
auto e = Expected<void>::fromCode(expected_void_throws_func);
|
||||||
|
ASSERT_THROW (e.get(), std::runtime_error);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(expected_void, void_fromCode_E_has_exception_true)
|
||||||
|
{
|
||||||
|
auto e = Expected<void>::fromCode(expected_void_throws_func);
|
||||||
|
ASSERT_THAT(e.hasException<std::runtime_error>(), Eq(true));
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(expected_void, void_fromCode_E_has_exception_false)
|
||||||
|
{
|
||||||
|
auto e = Expected<void>::fromCode(expected_void_throws_func);
|
||||||
|
ASSERT_THAT(e.hasException<std::logic_error>(), Eq(false));
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(expected_void, void_fromCode_E_has_derived_exception)
|
||||||
|
{
|
||||||
|
auto e = Expected<void>::fromCode(expected_void_throws_func);
|
||||||
|
ASSERT_THAT(e.hasException<std::exception>(), Eq(true));
|
||||||
|
}
|
||||||
63
tests/auto/mycase/tst_scopeguard.h
Normal file
63
tests/auto/mycase/tst_scopeguard.h
Normal file
|
|
@ -0,0 +1,63 @@
|
||||||
|
#include <gtest/gtest.h>
|
||||||
|
#include <gmock/gmock-matchers.h>
|
||||||
|
#include "ScopeGuard.h"
|
||||||
|
|
||||||
|
using namespace testing;
|
||||||
|
|
||||||
|
|
||||||
|
TEST(ScopeGuard, normal_run_fun_on_destruction_1)
|
||||||
|
{
|
||||||
|
bool result = false;
|
||||||
|
auto sg = scopeGuard([&result]() { result = true; });
|
||||||
|
ASSERT_THAT(result, Eq(false));
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(ScopeGuard, normal_run_fun_on_destruction_2)
|
||||||
|
{
|
||||||
|
bool result = false;
|
||||||
|
{
|
||||||
|
auto sg = scopeGuard([&result]() { result = true; });
|
||||||
|
}
|
||||||
|
|
||||||
|
ASSERT_THAT(result, Eq(true));
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(ScopeGuard, dismiss)
|
||||||
|
{
|
||||||
|
bool result = false;
|
||||||
|
{
|
||||||
|
auto sg = scopeGuard([&result]() { result = true; });
|
||||||
|
sg.dismiss();
|
||||||
|
}
|
||||||
|
|
||||||
|
ASSERT_THAT(result, Eq(false));
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(ScopeGuard, SCOPE_EXIT_macro_1)
|
||||||
|
{
|
||||||
|
bool result = false;
|
||||||
|
{
|
||||||
|
SCOPE_EXIT { result = true; };
|
||||||
|
ASSERT_THAT(result, Eq(false)); // prove previous statement hasn't run yet
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(ScopeGuard, SCOPE_EXIT_macro_2)
|
||||||
|
{
|
||||||
|
bool result = false;
|
||||||
|
{
|
||||||
|
SCOPE_EXIT { result = true; };
|
||||||
|
}
|
||||||
|
|
||||||
|
ASSERT_THAT(result, Eq(true));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
//TEST(expected, get_when_valid_returns_value)
|
||||||
|
//{
|
||||||
|
// Expected<int> v = getAnswerToEverything();
|
||||||
|
// ASSERT_THAT(v.get(), Eq(42));
|
||||||
|
//}
|
||||||
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue