335 lines
9.8 KiB
C++
335 lines
9.8 KiB
C++
#include "session.h"
|
|
|
|
Session::Session(QObject *parent) : QObject(parent)
|
|
{
|
|
|
|
}
|
|
|
|
Session::~Session() {
|
|
unload();
|
|
}
|
|
|
|
void Session::open_db(std::filesystem::path path) {
|
|
if(db) {
|
|
unload();
|
|
}
|
|
|
|
auto rc = sqlite3_open(path.c_str(), &db);
|
|
if(rc != SQLITE_OK) {
|
|
qDebug("cannot open database");
|
|
qDebug() << sqlite3_errmsg(db);
|
|
unload();
|
|
throw;
|
|
}
|
|
}
|
|
|
|
void Session::prepare(const char* query, sqlite3_stmt** stmt) {
|
|
auto rc = sqlite3_prepare_v2(db, query, strlen(query), stmt, 0);
|
|
|
|
if(rc != SQLITE_OK) {
|
|
qDebug() << "cannot prepare statement";
|
|
qDebug() << query;
|
|
qDebug() << sqlite3_errmsg(db);
|
|
unload();
|
|
throw;
|
|
}
|
|
}
|
|
|
|
bool Session::prepare_and_exec(const char* query) {
|
|
sqlite3_stmt* res;
|
|
prepare(query, &res);
|
|
bool result = sqlite3_step(res)==SQLITE_OK;
|
|
sqlite3_finalize(res);
|
|
return result;
|
|
}
|
|
|
|
bool Session::table_exists(std::string name) {
|
|
sqlite3_stmt* res;
|
|
|
|
prepare(get_tbl, &res);
|
|
bind_text(res, 1, name);
|
|
auto rc = sqlite3_step(res);
|
|
sqlite3_finalize(res);
|
|
|
|
return rc == SQLITE_ROW;
|
|
}
|
|
|
|
void Session::prepare_tables() {
|
|
if(table_exists("setting")) {
|
|
prepare(get_setting, &stmt_get_setting);
|
|
auto v = getSetting("version");
|
|
if(v) {
|
|
if(std::stoi(*v) != LITTLESNITCH_VERSION) {
|
|
qDebug() << "version " << v.value().c_str() << " is not supported!";
|
|
unload();
|
|
throw;
|
|
}
|
|
qDebug() << "loaded session with version " << v.value().c_str();
|
|
} else {
|
|
qDebug() << "setting table but no version found, aborting";
|
|
unload();
|
|
throw;
|
|
}
|
|
} else {
|
|
prepare_and_exec(create_request_tbl);
|
|
prepare_and_exec(create_request_header_tbl);
|
|
prepare_and_exec(create_response_tbl);
|
|
prepare_and_exec(create_response_header_tbl);
|
|
prepare_and_exec(create_flow_tbl);
|
|
prepare_and_exec(create_setting_tbl);
|
|
prepare(get_setting, &stmt_get_setting);
|
|
}
|
|
|
|
prepare(get_all_history, &stmt_get_all_history);
|
|
prepare(update_setting, &stmt_update_setting);
|
|
|
|
prepare(insert_request, &stmt_insert_request);
|
|
prepare(insert_request_header, &stmt_insert_request_header);
|
|
prepare(insert_response, &stmt_insert_response);
|
|
prepare(insert_response_header, &stmt_insert_response_header);
|
|
prepare(insert_flow, &stmt_insert_flow);
|
|
prepare(insert_setting, &stmt_insert_setting);
|
|
|
|
saveSetting("version", std::to_string(LITTLESNITCH_VERSION));
|
|
}
|
|
|
|
void Session::load(std::filesystem::path path)
|
|
{
|
|
open_db(path);
|
|
prepare_tables();
|
|
loaded = true;
|
|
}
|
|
|
|
void Session::unload() {
|
|
if(db) {
|
|
sqlite3_close(db);
|
|
}
|
|
loaded = false;
|
|
}
|
|
|
|
bool Session::isLoaded() {
|
|
return loaded;
|
|
}
|
|
|
|
std::optional<std::string> Session::getSetting(std::string key) {
|
|
sqlite3_reset(stmt_get_setting);
|
|
|
|
int j = 1;
|
|
|
|
bind_text(stmt_get_setting, j++, key);
|
|
|
|
auto rc = sqlite3_step(stmt_get_setting);
|
|
if(rc == SQLITE_ROW) {
|
|
auto value = std::string((const char*)sqlite3_column_text(stmt_get_setting, 0));
|
|
|
|
return value;
|
|
} else if(rc == SQLITE_DONE) {
|
|
return std::nullopt;
|
|
} else {
|
|
qDebug() << "getting setting failed " << rc;
|
|
qDebug() << sqlite3_errmsg(db);
|
|
return std::nullopt;
|
|
}
|
|
return std::nullopt;
|
|
}
|
|
|
|
std::vector<http::HistoryItem>* Session::getHistoryItems() {
|
|
sqlite3_reset(stmt_get_all_history);
|
|
|
|
std::vector<http::HistoryItem>* result = new std::vector<http::HistoryItem>();
|
|
|
|
auto rc = sqlite3_step(stmt_get_all_history);
|
|
while(rc == SQLITE_ROW) {
|
|
int j = 0;
|
|
http::HistoryItem i;
|
|
i.id = sqlite3_column_int(stmt_get_all_history, j++);
|
|
i.timestamp = sqlite3_column_double(stmt_get_all_history, j++);
|
|
i.method = std::string((const char*)sqlite3_column_text(stmt_get_all_history, j++));
|
|
i.scheme = std::string((const char*)sqlite3_column_text(stmt_get_all_history, j++));
|
|
i.host = std::string((const char*)sqlite3_column_text(stmt_get_all_history, j++));
|
|
i.port = sqlite3_column_int(stmt_get_all_history, j++);
|
|
i.path = std::string((const char*)sqlite3_column_text(stmt_get_all_history, j++));
|
|
i.status_code = sqlite3_column_int(stmt_get_all_history, j++);
|
|
i.reason = std::string((const char*)sqlite3_column_text(stmt_get_all_history, j++));
|
|
i.rtt = sqlite3_column_double(stmt_get_all_history, j++);
|
|
i.size = sqlite3_column_int(stmt_get_all_history, j++);
|
|
|
|
result->push_back(i);
|
|
|
|
rc = sqlite3_step(stmt_get_all_history);
|
|
}
|
|
if(rc != SQLITE_DONE) {
|
|
qDebug() << "error getting history items" << rc;
|
|
qDebug() << sqlite3_errmsg(db);
|
|
delete result;
|
|
return nullptr;
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
void Session::bind_text(sqlite3_stmt* stmt, int id, std::string text) {
|
|
//qDebug() << id << " " << text.c_str() << " " << strlen(text.c_str());
|
|
sqlite3_bind_text(stmt, id, text.c_str(), -1, SQLITE_TRANSIENT);
|
|
}
|
|
|
|
int Session::saveRequest(http::Flow flow) {
|
|
sqlite3_reset(stmt_insert_request);
|
|
|
|
int j = 1;
|
|
bind_text(stmt_insert_request, j++, flow.request.server_ip_address);
|
|
|
|
sqlite3_bind_int(stmt_insert_request, j++, flow.request.tls);
|
|
|
|
bind_text(stmt_insert_request, j++, flow.request.content);
|
|
bind_text(stmt_insert_request, j++, flow.request.scheme);
|
|
bind_text(stmt_insert_request, j++, flow.request.method);
|
|
bind_text(stmt_insert_request, j++, flow.request.host);
|
|
|
|
sqlite3_bind_int(stmt_insert_request, j++, flow.request.port);
|
|
|
|
bind_text(stmt_insert_request, j++, flow.request.http_version);
|
|
bind_text(stmt_insert_request, j++, flow.request.path);
|
|
|
|
sqlite3_bind_double(stmt_insert_request, j++, flow.request.timestamp_start);
|
|
sqlite3_bind_double(stmt_insert_request, j++, flow.request.timestamp_end);
|
|
|
|
bind_text(stmt_insert_request, j++, flow.request.error);
|
|
|
|
auto rc = sqlite3_step(stmt_insert_request);
|
|
if(rc != SQLITE_DONE) {
|
|
qDebug() << "inserting request failed" << rc;
|
|
qDebug() << sqlite3_errmsg(db);
|
|
return -1;
|
|
}
|
|
int id = sqlite3_last_insert_rowid(db);
|
|
|
|
for(auto&[k,v] : flow.request.headers) {
|
|
saveRequestHeader(k,v,id);
|
|
}
|
|
|
|
return id;
|
|
}
|
|
|
|
int Session::saveRequestHeader(std::string key, std::string value, int id) {
|
|
sqlite3_reset(stmt_insert_request_header);
|
|
|
|
int j = 1;
|
|
|
|
bind_text(stmt_insert_request_header, j++, key);
|
|
bind_text(stmt_insert_request_header, j++, value);
|
|
sqlite3_bind_int(stmt_insert_request_header, j++, id);
|
|
|
|
auto rc = sqlite3_step(stmt_insert_request_header);
|
|
if(rc != SQLITE_DONE) {
|
|
qDebug() << "inserting request header failed" << rc;
|
|
qDebug() << sqlite3_errmsg(db);
|
|
return -1;
|
|
}
|
|
|
|
return sqlite3_last_insert_rowid(db);
|
|
}
|
|
|
|
int Session::saveResponse(http::Flow flow) {
|
|
sqlite3_reset(stmt_insert_response);
|
|
|
|
int j = 1;
|
|
|
|
sqlite3_bind_int(stmt_insert_response, j++, flow.response.status_code);
|
|
bind_text(stmt_insert_response, j++, flow.response.http_version);
|
|
bind_text(stmt_insert_response, j++, flow.response.reason);
|
|
bind_text(stmt_insert_response, j++, flow.response.content);
|
|
|
|
sqlite3_bind_double(stmt_insert_response, j++, flow.response.timestamp_start);
|
|
sqlite3_bind_double(stmt_insert_response, j++, flow.response.timestamp_end);
|
|
|
|
auto rc = sqlite3_step(stmt_insert_response);
|
|
if(rc != SQLITE_DONE) {
|
|
qDebug() << "inserting response failed" << rc;
|
|
qDebug() << sqlite3_errmsg(db);
|
|
return -1;
|
|
}
|
|
int id = sqlite3_last_insert_rowid(db);
|
|
|
|
for(auto&[k,v] : flow.response.headers) {
|
|
saveResponseHeader(k,v,id);
|
|
}
|
|
|
|
return id;
|
|
}
|
|
|
|
int Session::saveResponseHeader(std::string key, std::string value, int id) {
|
|
sqlite3_reset(stmt_insert_response_header);
|
|
|
|
int j = 1;
|
|
|
|
bind_text(stmt_insert_response_header, j++, key);
|
|
bind_text(stmt_insert_response_header, j++, value);
|
|
sqlite3_bind_int(stmt_insert_response_header, j++, id);
|
|
|
|
auto rc = sqlite3_step(stmt_insert_response_header);
|
|
if(rc != SQLITE_DONE) {
|
|
qDebug() << "inserting response header failed" << rc;
|
|
qDebug() << sqlite3_errmsg(db);
|
|
return -1;
|
|
}
|
|
|
|
return sqlite3_last_insert_rowid(db);
|
|
}
|
|
|
|
int Session::saveFlow(http::Flow flow) {
|
|
sqlite3_reset(stmt_insert_flow);
|
|
|
|
int request_id = saveRequest(flow);
|
|
int response_id = saveResponse(flow);
|
|
|
|
int j = 1;
|
|
|
|
bind_text(stmt_insert_flow, j++, flow.uid);
|
|
sqlite3_bind_int(stmt_insert_flow, j++, request_id);
|
|
sqlite3_bind_int(stmt_insert_flow, j++, response_id);
|
|
|
|
auto rc = sqlite3_step(stmt_insert_flow);
|
|
if(rc != SQLITE_DONE) {
|
|
qDebug() << "inserting flow failed" << rc;
|
|
qDebug() << sqlite3_errmsg(db);
|
|
return -1;
|
|
}
|
|
|
|
return sqlite3_last_insert_rowid(db);
|
|
}
|
|
|
|
bool Session::saveSetting(std::string key, std::string value) {
|
|
auto old_value = getSetting(key);
|
|
if(!old_value) {
|
|
sqlite3_reset(stmt_insert_setting);
|
|
int j = 1;
|
|
|
|
bind_text(stmt_insert_setting, j++, key);
|
|
bind_text(stmt_insert_setting, j++, value);
|
|
|
|
auto rc = sqlite3_step(stmt_insert_setting);
|
|
if(rc != SQLITE_DONE) {
|
|
qDebug() << "inserting setting failed" << rc;
|
|
qDebug() << sqlite3_errmsg(db);
|
|
return false;
|
|
}
|
|
|
|
return sqlite3_last_insert_rowid(db);
|
|
} else {
|
|
sqlite3_reset(stmt_update_setting);
|
|
int j = 1;
|
|
|
|
bind_text(stmt_update_setting, j++, value);
|
|
bind_text(stmt_insert_setting, j++, key);
|
|
|
|
auto rc = sqlite3_step(stmt_update_setting);
|
|
if(rc != SQLITE_DONE) {
|
|
qDebug() << "updating setting failed" << rc;
|
|
qDebug() << sqlite3_errmsg(db);
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|