|
|
@ -4,64 +4,96 @@ |
|
|
|
#include <cstdio> |
|
|
|
#include <exception> |
|
|
|
#include <stdexcept> |
|
|
|
#include <vector> |
|
|
|
#include <map> |
|
|
|
|
|
|
|
//TODO: add const
|
|
|
|
|
|
|
|
using namespace std; |
|
|
|
using namespace pEp; |
|
|
|
|
|
|
|
sqlite3 *db; |
|
|
|
string db_path; |
|
|
|
using ResultSet = vector<map<string, string>>; |
|
|
|
using RSRecord = map<string, string>; |
|
|
|
|
|
|
|
void print_exception(const exception &e, int level = 0) |
|
|
|
{ |
|
|
|
cerr << string(level, ' ') << "exception: " << e.what() << endl; |
|
|
|
try { |
|
|
|
rethrow_if_nested(e); |
|
|
|
} catch (const exception &e) { |
|
|
|
print_exception(e, level + 1); |
|
|
|
} catch (...) { |
|
|
|
} |
|
|
|
} |
|
|
|
namespace pEp { |
|
|
|
namespace SQLite3 { |
|
|
|
::sqlite3 *db; |
|
|
|
string db_path; |
|
|
|
|
|
|
|
static int callback(void *NotUsed, int argc, char **argv, char **azColName) |
|
|
|
{ |
|
|
|
int i; |
|
|
|
for (i = 0; i < argc; i++) { |
|
|
|
printf("%s = %s\n", azColName[i], argv[i] ? argv[i] : "NULL"); |
|
|
|
} |
|
|
|
printf("\n"); |
|
|
|
return 0; |
|
|
|
} |
|
|
|
ResultSet resultset; |
|
|
|
|
|
|
|
void execute_sql(const string &stmt) |
|
|
|
{ |
|
|
|
// pEpLog("execute_sql(\"" + stmt + "\")");
|
|
|
|
char *zErrMsg = nullptr; |
|
|
|
int rc = sqlite3_exec(db, stmt.c_str(), callback, 0, &zErrMsg); |
|
|
|
if (rc != SQLITE_OK) { |
|
|
|
runtime_error e{ string("execute_sql: " + string(sqlite3_errmsg(db)) + ":" + string(zErrMsg)) }; |
|
|
|
sqlite3_free(zErrMsg); |
|
|
|
throw(e); |
|
|
|
} |
|
|
|
} |
|
|
|
void create_or_open_db() |
|
|
|
{ |
|
|
|
pEpLog("called"); |
|
|
|
int rc{::sqlite3_open(db_path.c_str(), &db)}; |
|
|
|
|
|
|
|
void create_or_open_db() |
|
|
|
{ |
|
|
|
pEpLog("called"); |
|
|
|
int rc = sqlite3_open(db_path.c_str(), &db); |
|
|
|
if (rc) { |
|
|
|
runtime_error e{string("Can't open database (" + db_path + "):" + ::sqlite3_errmsg(db))}; |
|
|
|
throw (e); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
void close_db() |
|
|
|
{ |
|
|
|
::sqlite3_close(db); |
|
|
|
} |
|
|
|
|
|
|
|
void delete_db() |
|
|
|
{ |
|
|
|
pEpLog("called"); |
|
|
|
remove(db_path.c_str()); |
|
|
|
if (errno) { |
|
|
|
cerr << "could not delete db (" + db_path + "): " << strerror(errno) << endl; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
ResultSet execute(const string& stmt) |
|
|
|
{ |
|
|
|
// pEpLog("execute(\"" + stmt + "\")");
|
|
|
|
resultset.clear(); |
|
|
|
char *zErrMsg = nullptr; |
|
|
|
int rc = ::sqlite3_exec(db, stmt.c_str(), [](void *NotUsed, int argc, char **argv, char **azColName) -> int { |
|
|
|
RSRecord record; |
|
|
|
for (int col = 0; col < argc; col++) { |
|
|
|
const string key = string{azColName[col]}; |
|
|
|
// TODO: NULL is not correct, could be a valid value
|
|
|
|
const string val = string{argv[col] ? argv[col] : "NULL"}; |
|
|
|
record.insert({key, val}); |
|
|
|
} |
|
|
|
resultset.push_back(record); |
|
|
|
return 0; |
|
|
|
}, 0, &zErrMsg); |
|
|
|
if (rc != SQLITE_OK) { |
|
|
|
runtime_error e{string("execute: " + string(::sqlite3_errmsg(db)) + ":" + string(zErrMsg))}; |
|
|
|
::sqlite3_free(zErrMsg); |
|
|
|
throw (e); |
|
|
|
} |
|
|
|
return resultset; |
|
|
|
} |
|
|
|
|
|
|
|
if (rc) { |
|
|
|
runtime_error e{ string("Can't open database (" + db_path + "):" + sqlite3_errmsg(db)) }; |
|
|
|
throw(e); |
|
|
|
string resultset_to_string(ResultSet rs) |
|
|
|
{ |
|
|
|
stringstream ss; |
|
|
|
int i = 0; |
|
|
|
for (const RSRecord& rec : rs) { |
|
|
|
for (const auto& item : rec) { |
|
|
|
ss << "RESULTSET[" << i << "][" << item.first << "] = " << item.second << "\"" << endl; |
|
|
|
} |
|
|
|
i++; |
|
|
|
} |
|
|
|
return ss.str(); |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
void delete_db() |
|
|
|
|
|
|
|
void print_exception(const exception& e, int level = 0) |
|
|
|
{ |
|
|
|
pEpLog("called"); |
|
|
|
remove(db_path.c_str()); |
|
|
|
if (errno) { |
|
|
|
cerr << "could not delete db (" + db_path + "): " << strerror(errno) << endl; |
|
|
|
cerr << string(level, ' ') << "exception: " << e.what() << endl; |
|
|
|
try { |
|
|
|
rethrow_if_nested(e); |
|
|
|
} catch (const exception& e) { |
|
|
|
print_exception(e, level + 1); |
|
|
|
} catch (...) { |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
@ -71,7 +103,7 @@ void db_config() |
|
|
|
try { |
|
|
|
string sql; |
|
|
|
sql = "PRAGMA foreign_keys=ON"; |
|
|
|
execute_sql(sql); |
|
|
|
SQLite3::execute(sql); |
|
|
|
} catch (...) { |
|
|
|
runtime_error e("db_config() - failed with exception"); |
|
|
|
throw_with_nested(e); |
|
|
@ -85,123 +117,207 @@ void create_tables() |
|
|
|
string sql; |
|
|
|
|
|
|
|
sql = "CREATE TABLE IF NOT EXISTS lists(" |
|
|
|
"address TEXT NOT NULL," |
|
|
|
"manager_id TEXT NOT NULL," |
|
|
|
"address TEXT NOT NULL," |
|
|
|
"moderator_address TEXT NOT NULL," |
|
|
|
"PRIMARY KEY(address));"; |
|
|
|
execute_sql(sql); |
|
|
|
SQLite3::execute(sql); |
|
|
|
|
|
|
|
sql = "CREATE TABLE IF NOT EXISTS member_of(" |
|
|
|
"address TEXT NOT NULL," |
|
|
|
"list_address TEXT NOT NULL," |
|
|
|
"PRIMARY KEY (address, list_address)," |
|
|
|
"FOREIGN KEY(list_address) REFERENCES lists(address) ON DELETE CASCADE);"; |
|
|
|
execute_sql(sql); |
|
|
|
SQLite3::execute(sql); |
|
|
|
} catch (...) { |
|
|
|
runtime_error e("create_tables() - failed with exception"); |
|
|
|
throw_with_nested(e); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
void list_add(const string &addr_list, const string &addr_mgr) |
|
|
|
void list_add(const string& addr_list, const string& addr_mgr) |
|
|
|
{ |
|
|
|
pEpLog("list_add(addr_list: \"" + addr_list + "\"\taddr_mgr: \"" + addr_mgr + "\")"); |
|
|
|
pEpLog("list_add(addr_list: \"" + addr_list + "\", addr_mgr: \"" + addr_mgr + "\")"); |
|
|
|
try { |
|
|
|
string sql = "INSERT INTO lists(address, manager_id)" |
|
|
|
"VALUES ('" + |
|
|
|
addr_list + "','" + addr_mgr + "');"; |
|
|
|
execute_sql(sql); |
|
|
|
string sql = "INSERT INTO lists(address, moderator_address) VALUES ('" + addr_list + "','" + |
|
|
|
addr_mgr + "');"; |
|
|
|
SQLite3::execute(sql); |
|
|
|
} catch (...) { |
|
|
|
runtime_error e( |
|
|
|
"list_add(addr_list: \"" + addr_list + "\"\taddr_mgr: \"" + addr_mgr + |
|
|
|
"\") - failed with exception"); |
|
|
|
"list_add(addr_list: \"" + addr_list + "\"\taddr_mgr: \"" + addr_mgr + |
|
|
|
"\") - failed with exception"); |
|
|
|
throw_with_nested(e); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
void list_delete(const string &addr_list) |
|
|
|
void list_delete(const string& addr_list) |
|
|
|
{ |
|
|
|
pEpLog("list_delete(addr_list: \"" + addr_list + "\")"); |
|
|
|
try { |
|
|
|
string sql; |
|
|
|
sql = "DELETE FROM lists WHERE lists.address = '" + addr_list + "'"; |
|
|
|
execute_sql(sql); |
|
|
|
sql = "DELETE FROM lists WHERE lists.address = '" + addr_list + "';"; |
|
|
|
SQLite3::execute(sql); |
|
|
|
} catch (...) { |
|
|
|
runtime_error e("list_delete(addr_list: \"" + addr_list + "\") - failed with exception"); |
|
|
|
throw_with_nested(e); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
void member_add(const string &addr_list, const string &addr_member) |
|
|
|
void member_add(const string& addr_list, const string& addr_member) |
|
|
|
{ |
|
|
|
pEpLog("member_add(addr_list: \"" + addr_list + "\", addr_member: \"" + addr_member + "\")"); |
|
|
|
try { |
|
|
|
string sql = "INSERT INTO member_of(address, list_address)" |
|
|
|
"VALUES ('" + |
|
|
|
addr_member + |
|
|
|
"'," |
|
|
|
"'" + |
|
|
|
addr_list + "');"; |
|
|
|
execute_sql(sql); |
|
|
|
string sql = "INSERT INTO member_of(address, list_address) VALUES ('" + addr_member + |
|
|
|
"', '" + addr_list + "');"; |
|
|
|
SQLite3::execute(sql); |
|
|
|
} catch (...) { |
|
|
|
runtime_error e( |
|
|
|
"member_add(addr_list: \"" + addr_list + "\", addr_member: \"" + addr_member + |
|
|
|
"\") - failed with exception"); |
|
|
|
"member_add(addr_list: \"" + addr_list + "\", addr_member: \"" + addr_member + |
|
|
|
"\") - failed with exception"); |
|
|
|
throw_with_nested(e); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
void member_remove(const string &addr_list, const string &addr_member) |
|
|
|
void member_remove(const string& addr_list, const string& addr_member) |
|
|
|
{ |
|
|
|
pEpLog("member_remove(addr_list: \"" + addr_list + "\", addr_member: '\"" + addr_member + "\")"); |
|
|
|
try { |
|
|
|
string sql; |
|
|
|
sql = "DELETE FROM member_of WHERE" |
|
|
|
"(member_of.address = '" + |
|
|
|
addr_member + "') AND (member_of.list_address = '" + addr_list + "');"; |
|
|
|
execute_sql(sql); |
|
|
|
sql = "DELETE FROM member_of WHERE (member_of.address = '" + addr_member + |
|
|
|
"') AND (member_of.list_address = '" + addr_list + "');"; |
|
|
|
SQLite3::execute(sql); |
|
|
|
} catch (...) { |
|
|
|
runtime_error e( |
|
|
|
"member_remove(" + addr_list + ", " + addr_member + ") - failed with exception"); |
|
|
|
"member_remove(" + addr_list + ", " + addr_member + ") - failed with exception"); |
|
|
|
throw_with_nested(e); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
vector<string> lists() |
|
|
|
{ |
|
|
|
pEpLog("called"); |
|
|
|
vector<string> ret; |
|
|
|
ResultSet rs; |
|
|
|
|
|
|
|
try { |
|
|
|
string sql; |
|
|
|
sql = "SELECT address FROM lists"; |
|
|
|
rs = SQLite3::execute(sql); |
|
|
|
} catch (...) { |
|
|
|
runtime_error e("lists() - failed with exception"); |
|
|
|
throw_with_nested(e); |
|
|
|
} |
|
|
|
|
|
|
|
for (const RSRecord& rec : rs) { |
|
|
|
ret.push_back(rec.at("address")); |
|
|
|
} |
|
|
|
|
|
|
|
return ret; |
|
|
|
} |
|
|
|
|
|
|
|
// Exceptions:
|
|
|
|
// * ListNotFound
|
|
|
|
string moderator(string list_address) |
|
|
|
{ |
|
|
|
pEpLog("called"); |
|
|
|
string ret; |
|
|
|
ResultSet rs; |
|
|
|
|
|
|
|
try { |
|
|
|
string sql; |
|
|
|
sql = "SELECT moderator_address FROM lists " |
|
|
|
"WHERE lists.address = '" + list_address + "';"; |
|
|
|
rs = SQLite3::execute(sql); |
|
|
|
} catch (...) { |
|
|
|
runtime_error e("lists() - failed with exception"); |
|
|
|
throw_with_nested(e); |
|
|
|
} |
|
|
|
|
|
|
|
if (!rs.empty()) { |
|
|
|
for (const RSRecord& rec : rs) { |
|
|
|
ret = rec.at("moderator_address"); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
return ret; |
|
|
|
} |
|
|
|
|
|
|
|
// Exceptions:
|
|
|
|
// * ListNotFound
|
|
|
|
vector<string> members(string list_address) |
|
|
|
{ |
|
|
|
pEpLog("called"); |
|
|
|
vector<string> ret; |
|
|
|
ResultSet rs; |
|
|
|
|
|
|
|
try { |
|
|
|
string sql; |
|
|
|
sql = "SELECT address FROM member_of " |
|
|
|
"WHERE list_address = '" + list_address + "'"; |
|
|
|
rs = SQLite3::execute(sql); |
|
|
|
} catch (...) { |
|
|
|
runtime_error e("lists() - failed with exception"); |
|
|
|
throw_with_nested(e); |
|
|
|
} |
|
|
|
|
|
|
|
if (!rs.empty()) { |
|
|
|
for (const RSRecord& rec : rs) { |
|
|
|
ret.push_back(rec.at("address")); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
return ret; |
|
|
|
} |
|
|
|
|
|
|
|
template<typename T> |
|
|
|
string vector_to_string(vector<T> v) |
|
|
|
{ |
|
|
|
stringstream ss; |
|
|
|
for (const T& elem : v) { |
|
|
|
ss << elem << endl; |
|
|
|
} |
|
|
|
return ss.str(); |
|
|
|
} |
|
|
|
|
|
|
|
int main(int argc, char *argv[]) |
|
|
|
{ |
|
|
|
pEp::Adapter::pEpLog::set_enabled(true); |
|
|
|
db_path = "test.db"; |
|
|
|
SQLite3::db_path = "test.db"; |
|
|
|
string dummy_in; |
|
|
|
|
|
|
|
|
|
|
|
try { |
|
|
|
delete_db(); |
|
|
|
create_or_open_db(); |
|
|
|
cin >> dummy_in; |
|
|
|
SQLite3::db_path = "test.db"; |
|
|
|
SQLite3::delete_db(); |
|
|
|
SQLite3::create_or_open_db(); |
|
|
|
db_config(); |
|
|
|
create_tables(); |
|
|
|
|
|
|
|
list_add("grp1@peptest.org", "alice@peptest.org"); |
|
|
|
cout << vector_to_string(lists()); |
|
|
|
list_add("grp2@peptest.org", "alice@peptest.org"); |
|
|
|
|
|
|
|
cout << vector_to_string(lists()); |
|
|
|
|
|
|
|
try { |
|
|
|
list_add("grp1@peptest.org", "bob@peptest.org"); |
|
|
|
assert(false); |
|
|
|
} catch (const exception &e) { |
|
|
|
} catch (const exception& e) { |
|
|
|
print_exception(e); |
|
|
|
} |
|
|
|
|
|
|
|
cin >> dummy_in; |
|
|
|
member_add("grp1@peptest.org", "bob@peptest.org"); |
|
|
|
member_add("grp1@peptest.org", "carol@peptest.org"); |
|
|
|
member_add("grp1@peptest.org", "joe@peptest.org"); |
|
|
|
|
|
|
|
cin >> dummy_in; |
|
|
|
cout << vector_to_string(members("grp1@peptest.org")) << endl; |
|
|
|
|
|
|
|
cout << moderator("grp1@peptest.org") << endl; |
|
|
|
member_remove("grp1@peptest.org", "joe@peptest.org"); |
|
|
|
|
|
|
|
cin >> dummy_in; |
|
|
|
list_delete("grp1@peptest.org"); |
|
|
|
|
|
|
|
cin >> dummy_in; |
|
|
|
} catch (const exception &e) { |
|
|
|
cout << vector_to_string(lists()); |
|
|
|
} catch (const exception& e) { |
|
|
|
print_exception(e); |
|
|
|
} |
|
|
|
sqlite3_close(db); |
|
|
|
SQLite3::close_db(); |
|
|
|
} |