aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorNiklas Halle <niklas@niklashalle.net>2020-08-24 19:04:13 +0200
committerNiklas Halle <niklas@niklashalle.net>2020-08-24 19:04:13 +0200
commit38e20390ddf38edd74447ef7db2af660b8e0ff32 (patch)
tree19139ea991ae5d99b28d5c1f9f509ff0ebf4b151
parent61464b6d069513eda07e4fc2638c8889cbce98f5 (diff)
downloadn_core-38e20390ddf38edd74447ef7db2af660b8e0ff32.tar.gz
n_core-38e20390ddf38edd74447ef7db2af660b8e0ff32.zip
add all available commands (in this branch? @Max where are the others?)
-rw-r--r--CMakeLists.txt18
-rw-r--r--include/Enum2String.hpp10
-rw-r--r--include/GetEssen.hpp47
-rw-r--r--include/Handler.hpp21
-rw-r--r--include/RelationshipHandler.hpp17
-rw-r--r--include/Response.hpp20
-rw-r--r--include/SimpleHandlers.hpp13
-rw-r--r--include/Utilities.hpp147
-rw-r--r--include/sqdb.hpp227
-rw-r--r--src/GetEssen.cpp282
-rw-r--r--src/Handler.cpp16
-rw-r--r--src/RelationshipHandler.cpp88
-rw-r--r--src/Response.cpp36
-rw-r--r--src/SimpleHandlers.cpp66
-rw-r--r--src/main.cpp173
-rw-r--r--src/sqdb.cpp437
16 files changed, 1459 insertions, 159 deletions
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 5bd1b0b..b68be2f 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -13,15 +13,29 @@ IF (CMAKE_VERSION VERSION_LESS "3.7.0")
ENDIF ()
find_package(Boost REQUIRED COMPONENTS system)
+find_package(Boost REQUIRED COMPONENTS date_time)
add_executable(${PROJECT_NAME}
# Headers
- include/AnnotationTypes.hpp
+ include/sqdb.hpp
+ include/Handler.hpp
+ include/GetEssen.hpp
+ include/Response.hpp
+ include/Utilities.hpp
include/Enum2String.hpp
+ include/SimpleHandlers.hpp
+ include/AnnotationTypes.hpp
+ include/RelationshipHandler.hpp
# Sources
src/main.cpp
+ src/sqdb.cpp
+ src/Handler.cpp
+ src/GetEssen.cpp
+ src/Response.cpp
src/Enum2String.cpp
+ src/SimpleHandlers.cpp
+ src/RelationshipHandler.cpp
)
target_include_directories(${PROJECT_NAME}
@@ -39,6 +53,8 @@ target_link_directories(${PROJECT_NAME} PRIVATE
target_link_libraries(${PROJECT_NAME} PRIVATE
${Boost_LIBRARIES}
pthread
+ sqlite3
+ curl
)
#target_compile_definitions(${PROJECT_NAME} PRIVATE N_CORE)
diff --git a/include/Enum2String.hpp b/include/Enum2String.hpp
index 4992e0d..da3ab94 100644
--- a/include/Enum2String.hpp
+++ b/include/Enum2String.hpp
@@ -3,13 +3,13 @@
#undef END_ENUM
#ifndef GENERATE_ENUM_STRINGS
-#define DECL_ENUM_ELEMENT( element ) element
-#define BEGIN_ENUM( ENUM_NAME ) typedef enum tag##ENUM_NAME
-#define END_ENUM( ENUM_NAME ) ENUM_NAME; \
+#define DECL_ENUM_ELEMENT(element) element
+#define BEGIN_ENUM(ENUM_NAME) typedef enum tag##ENUM_NAME
+#define END_ENUM(ENUM_NAME) ENUM_NAME; \
std::string GetString##ENUM_NAME(enum tag##ENUM_NAME index);
#else
#define DECL_ENUM_ELEMENT( element ) #element
- #define BEGIN_ENUM( ENUM_NAME ) std::string gs_##ENUM_NAME [] =
- #define END_ENUM( ENUM_NAME ) ; std::string GetString##ENUM_NAME(\
+#define BEGIN_ENUM( ENUM_NAME ) std::string gs_##ENUM_NAME [] =
+#define END_ENUM( ENUM_NAME ) ; std::string GetString##ENUM_NAME(\
tag##ENUM_NAME index){ return gs_##ENUM_NAME [index]; }
#endif \ No newline at end of file
diff --git a/include/GetEssen.hpp b/include/GetEssen.hpp
new file mode 100644
index 0000000..ff75029
--- /dev/null
+++ b/include/GetEssen.hpp
@@ -0,0 +1,47 @@
+#pragma once
+
+#include <iostream>
+#include <sys/stat.h>
+
+#include <curl/curl.h>
+
+#include <boost/date_time/date_formatting.hpp>
+#include <boost/date_time/gregorian/gregorian.hpp>
+#include <boost/date_time/posix_time/posix_time.hpp>
+
+#include "Handler.hpp"
+#include "Response.hpp"
+
+namespace Handler {
+ namespace MensaHandler {
+ using std::string;
+ using boost::posix_time::ptime;
+
+ struct MensaEssen {
+ int ID{191};
+ string Message{"nothing got back! There was an error!!"};
+ ptime Datum;
+ ptime LastModified;
+ };
+
+ enum mensa {
+ Adlershof = 191, Nord = 147, Sued = 367
+ };
+
+
+ bool db_update_mensa_message(const int &mensaID, const ptime &mensaDatum, const string &message);
+
+ bool db_insert_mensa_message(const int &mensaID, const ptime &mensaDatum, const string &message);
+
+ MensaEssen db_get_mensa_message(const int &mensaID, const ptime &mensaDatum);
+
+ string getEssen(const int mensa_ID, const int offset);
+
+ string read_mensa_message_from_file(const int &mensa_id_file);
+ }
+
+ json mensaHandler(std::string const &arguments, std::string const &session, void *payload);
+}
+
+
+
diff --git a/include/Handler.hpp b/include/Handler.hpp
new file mode 100644
index 0000000..3d4b830
--- /dev/null
+++ b/include/Handler.hpp
@@ -0,0 +1,21 @@
+#pragma once
+
+#include "crow.h"
+
+namespace Handler {
+ typedef crow::json::wvalue json;
+ typedef std::function<json(std::string const &arguments, std::string const &session,
+ void *payload)> handler_function;
+
+ struct CommandHandler {
+ [[nodiscard]] json exec(std::string const &arguments, std::string const &session) const {
+ return func(arguments, session, payload);
+ }
+
+ handler_function func{nullptr};
+ std::string description;
+ void *payload{nullptr};
+ };
+
+ std::vector<std::string> tokenizeArguments(std::string const &arguments, std::string const &delimiter = " ");
+} \ No newline at end of file
diff --git a/include/RelationshipHandler.hpp b/include/RelationshipHandler.hpp
new file mode 100644
index 0000000..5b609f4
--- /dev/null
+++ b/include/RelationshipHandler.hpp
@@ -0,0 +1,17 @@
+#pragma once
+
+#include "Handler.hpp"
+
+namespace Handler {
+ namespace RelationShipHandler {
+ std::vector<int> buildCalArr(std::vector<std::string> nameV);
+
+ int calculate(std::vector<std::string> names);
+
+ bool isAlphaNum(const std::string &str);
+
+ std::string rsStart(std::vector<std::string> const &names);
+ }
+
+ json relationShipHandler(std::string const &arguments, std::string const &session, void *payload);
+}
diff --git a/include/Response.hpp b/include/Response.hpp
new file mode 100644
index 0000000..53043c8
--- /dev/null
+++ b/include/Response.hpp
@@ -0,0 +1,20 @@
+#pragma once
+
+#include "crow.h"
+
+#include "AnnotationTypes.hpp"
+
+namespace Response {
+ using Reply::AnnotationType;
+ typedef crow::json::wvalue json;
+
+ json simple_response(std::string const &text, std::string const &session = "null", bool success = true);
+
+ json create_annotation(AnnotationType annotation = AnnotationType::none, std::string const &extra = "");
+
+ json create_text(std::string const &text);
+
+ json create_text(std::string const &text, std::vector<json> &&annotations);
+
+ json create_response(std::vector<json> &&reply, std::string const &session = "null", bool success = true);
+} \ No newline at end of file
diff --git a/include/SimpleHandlers.hpp b/include/SimpleHandlers.hpp
new file mode 100644
index 0000000..06dfcd1
--- /dev/null
+++ b/include/SimpleHandlers.hpp
@@ -0,0 +1,13 @@
+#pragma once
+
+#include "Handler.hpp"
+
+namespace Handler {
+ json helpHandler(std::string const &arguments, std::string const &session, void *payload);
+
+ json stopHandler(std::string const &arguments, std::string const &session, void *payload);
+
+ json wikiHandler(std::string const &arguments, std::string const &session, void *payload);
+
+ json klingerHandler(std::string const &arguments, std::string const &session, void *payload);
+} \ No newline at end of file
diff --git a/include/Utilities.hpp b/include/Utilities.hpp
new file mode 100644
index 0000000..781b9fb
--- /dev/null
+++ b/include/Utilities.hpp
@@ -0,0 +1,147 @@
+#pragma once
+
+#include <ctime>
+#include <string>
+#include <curl/curl.h>
+#include <boost/date_time.hpp>
+
+#define NOW std::time(0)
+#define LOG_TIME_FORMAT "%y-%m-%d %H:%M:%S"
+#define HR_TIME_FORMAT "%d.%m.%y, %H:%M:%S UHR"
+#define NOW_STRING TimeStampToHReadble(NOW, false)
+
+static std::string TimeStampToHReadble(const time_t rawTime, bool HR_READABLE) {
+ struct tm *dt;
+ char buffer[30];
+ dt = localtime(&rawTime);
+ if (HR_READABLE) {
+ strftime(buffer, sizeof(buffer), HR_TIME_FORMAT, dt);
+ } else {
+ strftime(buffer, sizeof(buffer), LOG_TIME_FORMAT, dt);
+ }
+ return std::string(buffer);
+}
+
+#include <fstream>
+
+static std::string getApiTokenFromFile(const std::string &filePath) {
+ std::string token = "NO-TOKEN";
+
+ std::ifstream inFile(filePath);
+
+ if (inFile) {
+ // TODO: validate, at least a little - currently we just assume the file contains the correct data
+ std::getline(inFile, token);
+ } else {
+ return "";
+ }
+
+ return token;
+}
+
+#ifdef NDEBUG
+
+static std::string getProductionApiToken() {
+ return getApiTokenFromFile("../release.api");
+}
+
+#else
+
+static std::string getDebugApiToken() {
+ return getApiTokenFromFile("../debug.api");
+}
+
+#endif
+
+static CURLcode downloadFile(const std::string &url, const std::string &fileName) {
+
+ CURL *curl;
+ FILE *fp;
+ CURLcode res;
+ curl = curl_easy_init();
+
+ if (curl) {
+ fp = fopen(fileName.c_str(), "wb");
+
+ curl_easy_setopt(curl, CURLOPT_URL, url.c_str());
+ curl_easy_setopt(curl, CURLOPT_USERAGENT,
+ "Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/51.0.2704.106 Safari/537.36"); //so apparently the Website stw.berlin blocks the IPwhen accessing it very often with the bot...
+ curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, NULL);
+ curl_easy_setopt(curl, CURLOPT_WRITEDATA, fp);
+ res = curl_easy_perform(curl);
+ if (res != CURLE_OK) {
+ //TODO: return int as status instead of boolean - maybe even this curl error?
+ return CURLE_COULDNT_CONNECT;
+ }
+ curl_easy_cleanup(curl);
+ fclose(fp);
+ return CURLE_OK;
+ } else {
+ return CURLE_COULDNT_CONNECT;
+ }
+}
+
+static CURLcode downloadMensaFile(const int &ID, const boost::posix_time::ptime &datum) {
+
+ std::string datumString =
+ std::to_string(datum.date().year()) + "-" + std::to_string(datum.date().month().as_number()) + "-" +
+ std::to_string(datum.date().day());
+
+ std::string postfield = "resources_id=" + std::to_string(ID) + "&date=" + datumString;
+
+ CURL *curl;
+ FILE *fp;
+ CURLcode res;
+ curl = curl_easy_init();
+
+ if (curl) {
+ fp = fopen(std::to_string(ID).c_str(), "wb");
+
+ struct curl_slist *slist1;
+
+ slist1 = nullptr;
+ slist1 = curl_slist_append(slist1,
+ "Cookie: seitenbereich=mensen; _ga=GA1.2.1719763776.1545837849; filterkennz=[]; PHPSESSID=9fa00137a2ad065fe595196ddb34e0ba; _gid=GA1.2.226483670.1549982576; loadDataForResource=191; storedPathname=/mensen.html; _gat=1");
+ slist1 = curl_slist_append(slist1, "Origin: https://www.stw.berlin");
+ slist1 = curl_slist_append(slist1, "Accept-Encoding: gzip, deflate, br");
+ slist1 = curl_slist_append(slist1, "Accept-Language: de-DE,de;q=0.9,en-US;q=0.8,en;q=0.7");
+ slist1 = curl_slist_append(slist1,
+ "User-Agent: Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/72.0.3626.81 Safari/537.36");
+ slist1 = curl_slist_append(slist1, "Content-Type: application/x-www-form-urlencoded; charset=UTF-8");
+ slist1 = curl_slist_append(slist1, "Accept: */*");
+ slist1 = curl_slist_append(slist1, "Referer: https://www.stw.berlin/mensen.html");
+ slist1 = curl_slist_append(slist1, "X-Requested-With: XMLHttpRequest");
+ slist1 = curl_slist_append(slist1, "Connection: keep-alive");
+ slist1 = curl_slist_append(slist1, "DNT: 1");
+
+ curl = curl_easy_init();
+ curl_easy_setopt(curl, CURLOPT_BUFFERSIZE, 102400L);
+ curl_easy_setopt(curl, CURLOPT_URL, "https://www.stw.berlin/xhr/speiseplan-wochentag.html");
+ curl_easy_setopt(curl, CURLOPT_TIMEOUT, 5L);
+ curl_easy_setopt(curl, CURLOPT_NOPROGRESS, 1L);
+ curl_easy_setopt(curl, CURLOPT_POSTFIELDS, postfield.c_str());
+ curl_easy_setopt(curl, CURLOPT_POSTFIELDSIZE_LARGE, (curl_off_t) 32);
+ curl_easy_setopt(curl, CURLOPT_HTTPHEADER, slist1);
+ curl_easy_setopt(curl, CURLOPT_USERAGENT, "curl/7.63.0");
+ curl_easy_setopt(curl, CURLOPT_MAXREDIRS, 50L);
+ curl_easy_setopt(curl, CURLOPT_HTTP_VERSION, (long) CURL_HTTP_VERSION_2TLS);
+ curl_easy_setopt(curl, CURLOPT_ACCEPT_ENCODING, "");
+ curl_easy_setopt(curl, CURLOPT_SSH_KNOWNHOSTS, "/home/max/.ssh/known_hosts");
+ curl_easy_setopt(curl, CURLOPT_TCP_KEEPALIVE, 1L);
+
+ curl_easy_setopt(curl, CURLOPT_WRITEDATA, fp);
+ res = curl_easy_perform(curl);
+ if (res != CURLE_OK) {
+ //TODO: return int as status instead of boolean - maybe even this curl error?
+ return res;
+ }
+ curl_easy_cleanup(curl);
+ curl = nullptr;
+ curl_slist_free_all(slist1);
+ slist1 = nullptr;
+ fclose(fp);
+ return CURLE_OK;
+ } else {
+ return CURLE_FAILED_INIT;
+ }
+}
diff --git a/include/sqdb.hpp b/include/sqdb.hpp
new file mode 100644
index 0000000..b92cac6
--- /dev/null
+++ b/include/sqdb.hpp
@@ -0,0 +1,227 @@
+#pragma once
+
+#include <string>
+
+#include "sqlite3.h"
+
+#ifdef _WIN32
+# include <tchar.h>
+# define SQDB_MAKE_TEXT(x) _TEXT(x)
+# define SQDB_STRLEN _tcslen
+# define SQDB_STRDUP _tcsdup
+#else
+# define SQDB_MAKE_TEXT(x) (x)
+# define SQDB_STRLEN strlen
+# define SQDB_STRDUP strdup
+#endif
+
+#if !defined(SQDB_UTF16) && !defined(SQDB_UTF8)
+# ifdef _WIN32
+# if defined(UNICODE) || defined(_UNICODE)
+# define SQDB_UTF16
+# else
+# define SQDB_UTF8
+# endif
+# else
+# define SQDB_UTF8
+# endif
+#endif
+
+#ifdef SQDB_UTF8
+# define SQDB_CHAR char
+# define SQDB_STD_STRING std::string
+#endif
+
+#ifdef SQDB_UTF16
+# define SQDB_CHAR TCHAR
+# define SQDB_STD_STRING std::wstring
+#endif
+
+namespace sqdb {
+
+ class Exception {
+ public:
+ Exception(sqlite3 *db);
+
+ Exception(sqlite3 *db, int errorCode);
+
+ Exception(const SQDB_CHAR *errorMsg);
+
+ ~Exception();
+
+ int GetErrorCode() const;
+
+ const SQDB_CHAR *GetErrorMsg() const;
+
+ private:
+ int m_errorCode;
+ SQDB_CHAR *m_errorMsg;
+ };
+
+#define CHECK(db, returnCode) \
+ if ( (returnCode) != SQLITE_OK ) throw Exception(db, returnCode)
+
+ class RefCount {
+ protected:
+ RefCount();
+
+ RefCount(const RefCount &x);
+
+ RefCount &operator=(const RefCount &x);
+
+ void IncRef();
+
+ unsigned DecRef();
+
+ private:
+ unsigned *m_refCount;
+ };
+
+ class Blob : public RefCount {
+ public:
+ Blob(const void *data, int size);
+
+ Blob(const Blob &x);
+
+ Blob &operator=(const Blob &x);
+
+ int GetSize() const;
+
+ const char *GetData() const;
+
+ ~Blob();
+
+ private:
+ char *m_data;
+ int m_size;
+ };
+
+ class Convertor {
+ public:
+ Convertor(sqlite3 *db, sqlite3_stmt *stmt, int field);
+
+ operator int() const;
+
+ operator unsigned long() const;
+
+ operator long long() const;
+
+ operator double() const;
+
+ operator SQDB_STD_STRING() const;
+
+ operator const SQDB_CHAR *() const;
+
+ operator Blob() const;
+
+ int GetInt() const;
+
+ unsigned long GetUnsignedLong() const;
+
+ long long GetLongLong() const;
+
+ double GetDouble() const;
+
+ SQDB_STD_STRING GetString() const;
+
+ const SQDB_CHAR *GetText() const;
+
+ Blob GetBlob() const;
+
+ private:
+ sqlite3 *m_db;
+ sqlite3_stmt *m_stmt;
+ int m_field;
+ };
+
+ class Statement : public RefCount {
+ public:
+ Statement(sqlite3 *db, sqlite3_stmt *stmt);
+
+ Statement(const Statement &x);
+
+ Statement &operator=(const Statement &x);
+
+ bool Next();
+
+ Convertor GetField(int field) const;
+
+ template<class T>
+ void Bind(int i, const T &value) {
+ if (m_needReset)
+ Reset();
+ DoBind(i, value);
+ }
+
+ void BindBlob(int i, const void *value, int n);
+
+ void BindNull(int i);
+
+ ~Statement();
+
+ private:
+ void DoBind(int i, int value);
+
+ void DoBind(int i, long long value);
+
+ void DoBind(int i, double value);
+
+ void DoBind(int i, const SQDB_STD_STRING &value);
+
+ void DoBind(int i, const SQDB_CHAR *value);
+
+ // Bind blob.
+ void DoBind(int i, const void *value, int n);
+
+ // Bind null.
+ void DoBind(int i);
+
+ // Reset binders so that new values can be bound.
+ void Reset();
+
+ sqlite3 *m_db;
+ sqlite3_stmt *m_stmt;
+ bool m_needReset;
+ };
+
+ class QueryStr {
+ public:
+ QueryStr();
+
+ const SQDB_CHAR *Format(const SQDB_CHAR *fmt, ...);
+
+ const SQDB_CHAR *Get() const;
+
+ ~QueryStr();
+
+ private:
+ SQDB_CHAR *m_buf;
+ };
+
+ class Db : public RefCount {
+ public:
+ Db(const SQDB_CHAR *fileName);
+
+ void BeginTransaction();
+
+ void CommitTransaction();
+
+ void RollbackTransaction();
+
+ bool TableExists(const SQDB_CHAR *tableName);
+
+ Statement Query(const SQDB_CHAR *queryStr);
+
+ long long LastId();
+
+ Db(const Db &x);
+
+ Db &operator=(const Db &x);
+
+ ~Db();
+
+ private:
+ sqlite3 *m_db;
+ };
+
+}
diff --git a/src/GetEssen.cpp b/src/GetEssen.cpp
new file mode 100644
index 0000000..3825c5c
--- /dev/null
+++ b/src/GetEssen.cpp
@@ -0,0 +1,282 @@
+#include "GetEssen.hpp"
+
+#include "sqdb.hpp"
+#include "Utilities.hpp"
+
+using std::string;
+
+bool
+Handler::MensaHandler::db_update_mensa_message(const int &mensaID, const ptime &mensaDatum, const string &message) {
+ sqdb::Db db("../main.sqlite");
+ string newTime = to_simple_string(ptime(boost::posix_time::second_clock::local_time()));
+ string mensaDatumString =
+ std::to_string(mensaDatum.date().year()) + "-" + mensaDatum.date().month().as_short_string() + "-" +
+ std::to_string(mensaDatum.date().day().as_number());
+ sqdb::QueryStr str;
+ db.Query(str.Format(
+ SQDB_MAKE_TEXT("update mensa set Message = '%s', LastModified = '%s' where ID = %i and Datum = '%s';"),
+ message.c_str(), newTime.c_str(), mensaID, mensaDatumString.c_str())).Next();
+ return true;
+}
+
+bool
+Handler::MensaHandler::db_insert_mensa_message(const int &mensaID, const ptime &mensaDatum, const string &message) {
+ sqdb::Db db("../main.sqlite");
+ string newTime = to_simple_string(ptime(boost::posix_time::second_clock::local_time()));
+ string mensaDatumString =
+ std::to_string(mensaDatum.date().year()) + "-" + mensaDatum.date().month().as_short_string() + "-" +
+ std::to_string(mensaDatum.date().day().as_number());
+ sqdb::QueryStr str;
+ db.Query(str.Format(SQDB_MAKE_TEXT("insert into mensa values(%i, '%s', '%s', '%s');"), mensaID, message.c_str(),
+ mensaDatumString.c_str(), newTime.c_str())).Next();
+ return true;
+}
+
+Handler::MensaHandler::MensaEssen
+Handler::MensaHandler::db_get_mensa_message(const int &mensaID, const ptime &mensaDatum) {
+ MensaEssen out;
+ sqdb::Db db("../main.sqlite");
+
+ sqdb::QueryStr str;
+ string mensaDatumString =
+ std::to_string(mensaDatum.date().year()) + "-" + mensaDatum.date().month().as_short_string() + "-" +
+ std::to_string(mensaDatum.date().day().as_number());
+ sqdb::Statement s = db.Query(
+ str.Format(SQDB_MAKE_TEXT("SELECT * from mensa WHERE Datum = '%s' AND ID = %i;"), mensaDatumString.c_str(),
+ mensaID));
+
+ try {
+ if (s.Next()) {
+ int ID = s.GetField(0);
+ string Message = s.GetField(1);
+ string Datum = s.GetField(2);
+ string LastModified = s.GetField(3);
+
+
+ ptime LMDate(boost::posix_time::time_from_string(LastModified));
+
+ out.ID = ID;
+ out.Message = Message;
+ out.Datum = mensaDatum;
+ out.LastModified = LMDate;
+ return out;
+ }
+ } catch (sqdb::Exception &exception) {
+ std::cerr << "Error code: " << exception.GetErrorCode() << " Error Message: " << exception.GetErrorMsg()
+ << std::endl;
+ }
+ out.ID = -2;
+ return out;
+
+}
+
+string Handler::MensaHandler::getEssen(const int mensa_ID, const int offset) {
+ MensaEssen Essen;
+ if (offset >= 0) {
+
+ if (offset > 28) {
+ return "Ich fühle mich geschmeichelt, dass du denkst, dass ich so weit in die Zukunft gucken kann (0 bis 28 Tage)";
+ }
+
+ ptime datumNow = boost::posix_time::second_clock::local_time();
+ ptime datumEssen = datumNow + (boost::gregorian::days(offset));
+
+ Essen = db_get_mensa_message(mensa_ID, datumEssen);
+ if (Essen.ID == -2) { // SQL got nothing back :/
+ //log("sql got nothing back!");
+ if (downloadMensaFile(mensa_ID, datumEssen) != CURLE_OK) {
+ std::cerr << "Error downloading file" << std::endl;
+ return "Error downloading file!";
+ }
+ db_insert_mensa_message(mensa_ID, datumEssen, read_mensa_message_from_file(mensa_ID));
+ Essen = db_get_mensa_message(mensa_ID, datumEssen);
+
+ string out;
+ switch (Essen.ID) {
+ case mensa::Adlershof :
+ out = "<b>Mensa Adlershof</b> am " + std::to_string(Essen.Datum.date().day().as_number()) + "." +
+ std::to_string(Essen.Datum.date().month().as_number()) + "." +
+ std::to_string(Essen.Datum.date().year()) + ":";
+ break;
+ case mensa::Nord:
+ out = "<b>Mensa Nord</b> am " + std::to_string(Essen.Datum.date().day().as_number()) + "." +
+ std::to_string(Essen.Datum.date().month().as_number()) + "." +
+ std::to_string(Essen.Datum.date().year()) + ":";
+ break;
+ case mensa::Sued :
+ out = "<b>Mensa Sued</b> am " + std::to_string(Essen.Datum.date().day().as_number()) + "." +
+ std::to_string(Essen.Datum.date().month().as_number()) + "." +
+ std::to_string(Essen.Datum.date().year()) + ":";
+ break;
+ default:
+ out = "Mensa Unbekannt am " + std::to_string(Essen.Datum.date().day().as_number()) + "." +
+ std::to_string(Essen.Datum.date().month().as_number()) + "." +
+ std::to_string(Essen.Datum.date().year()) + ":";
+ break;
+
+ }
+ out += Essen.Message;
+ out += "\n (Stand: " + to_simple_string(Essen.LastModified) + ")\n";
+
+ return out;
+ }
+
+ if (Essen.LastModified + boost::posix_time::hours(2) < datumNow) { //LastModified is more than 2 hours old
+ if (downloadMensaFile(mensa_ID, datumEssen) != CURLE_OK) {
+ return "Error downloading file!";
+ }
+
+ db_update_mensa_message(mensa_ID, datumEssen, read_mensa_message_from_file(mensa_ID));
+ Essen = db_get_mensa_message(mensa_ID, datumEssen);
+ }
+ string out;
+ switch (Essen.ID) {
+ case mensa::Adlershof :
+ out = "<b>Mensa Adlershof</b> am " + std::to_string(Essen.Datum.date().day().as_number()) + "." +
+ std::to_string(Essen.Datum.date().month().as_number()) + "." +
+ std::to_string(Essen.Datum.date().year()) +
+ ":";
+ break;
+ case mensa::Nord :
+ out = "<b>Mensa Nord</b> am " + std::to_string(Essen.Datum.date().day().as_number()) + "." +
+ std::to_string(Essen.Datum.date().month().as_number()) + "." +
+ std::to_string(Essen.Datum.date().year()) +
+ ":";
+ break;
+ case mensa::Sued :
+ out = "<b>Mensa Sued</b> am " + std::to_string(Essen.Datum.date().day().as_number()) + "." +
+ std::to_string(Essen.Datum.date().month().as_number()) + "." +
+ std::to_string(Essen.Datum.date().year()) +
+ ":";
+ break;
+ default:
+ out = "Mensa Unbekannt am " + std::to_string(Essen.Datum.date().day().as_number()) + "." +
+ std::to_string(Essen.Datum.date().month().as_number()) + "." +
+ std::to_string(Essen.Datum.date().year()) +
+ ":";
+ break;
+
+ }
+ out += Essen.Message;
+ out += "\n (Stand: " + to_simple_string(Essen.LastModified) + ")\n";
+ return out;
+ }
+
+ return "Wer denkst du bin ich? Ein Zeitreisender?\nIch kann zwar in die Zukunft gucken, aber nur 28 Tage";
+}
+
+string Handler::MensaHandler::read_mensa_message_from_file(const int &mensa_id_file) {
+ string s;
+ string out;
+ bool serious = false;
+
+ std::ifstream inFile(std::to_string(mensa_id_file));
+ while (std::getline(inFile, s)) {
+ //out += mensa;
+ if (s.find("15.png") != string::npos && serious) {
+ string vegan;
+
+ int vegan_byte[4] = {0xF0, 0x9F, 0x8C, 0xB1};
+ for (int i : vegan_byte) {
+ vegan += (char) i;
+ }
+
+ out += vegan;
+ }
+
+ if (s.find("1.png") != string::npos && serious) {
+ string vegetarian;
+
+ int vegetarian_byte[4] = {0xF0, 0x9F, 0x8C, 0xBD};
+ for (int i : vegetarian_byte) {
+ vegetarian += (char) i;
+ }
+
+ out += vegetarian;
+ }
+ if (s.find("splIcon") != string::npos && serious && s.find("1.png") == string::npos &&
+ s.find("15.png") == string::npos) {
+ string emptyEmoji;
+ int emptyEmoji_byte[6] = {0xE2, 0x98, 0xA3, 0xEF, 0xB8, 0x8F};
+ for (int i : emptyEmoji_byte) {
+ emptyEmoji += (char) i;
+ }
+
+ out += emptyEmoji;
+ }
+
+ if (s.find("splGroup\"") != string::npos) { //Getting the Category of the Meal
+ for (int i = 0, j = 0; (unsigned) i < s.length(); ++i) {
+ if (s.at(i) == '>') {
+ j = i + 1;
+ }
+ if (s.at(i) == '<' && j != 0) {
+ s = s.substr(j, i - j);
+ j = 0;
+ }
+
+ }
+ serious = (s == "Aktionen" || s == "Essen" || s == "Beilagen");
+ if (serious) {
+ out += "\n<b>" + s + "</b>\n";
+ }
+ }
+ if (s.find("class=\"bold\"") != string::npos && serious) { //Getting the Meal
+ for (int i = 0, j = 0; (unsigned) i < s.length(); ++i) {
+ if (s.at(i) == '>') {
+ j = i + 1;
+ //logStatus("Zeichen > gefunden in Position" + std::std::to_string(i));
+ }
+ if (s.at(i) == '<' && j != 0) {
+ s = s.substr(j, i - j);
+ j = 0;
+ }
+
+ }
+
+ out += "\t" + s;
+ }
+
+ if (s.find("euro;") != string::npos && serious) { //geting the price of each Meal
+ for (int i = 0, j = 0; (unsigned) i < s.length(); ++i) {
+ if (s.at(i) == ';') {
+ j = i + 1;
+ }
+ if (s.at(i) == '/') {
+ s = s.substr(j, i - j);
+ j = 0;
+ }
+ }
+ out += "\t<b>" + s + "</b>€\n";
+ }
+ }
+
+ if (out.empty()) {
+ return "Heute kein Essen";
+ }
+ return out;
+
+}
+
+Handler::json Handler::mensaHandler(const std::string &arguments, const std::string &session, void *payload) {
+ Handler::MensaHandler::mensa Mensa_ID;
+
+ auto args = tokenizeArguments(arguments);
+
+ std::string mensa_name = (args.size() > 0 ? args.at(0) : "adlershof");
+ std::string offset_request = (args.size() > 1 ? args.at(1) : "0");
+
+ if (mensa_name == "adlershof") {
+ Mensa_ID = Handler::MensaHandler::mensa::Adlershof;
+ } else if (mensa_name == "nord") {
+ Mensa_ID = Handler::MensaHandler::mensa::Nord;
+ } else if (mensa_name == "sued" || mensa_name == "süd") {
+ Mensa_ID = Handler::MensaHandler::mensa::Sued;
+ } else {
+ Mensa_ID = Handler::MensaHandler::mensa::Adlershof;
+ }
+
+ int offset = std::stoi(offset_request);
+
+ return Response::simple_response(getEssen(Mensa_ID, offset));
+}
diff --git a/src/Handler.cpp b/src/Handler.cpp
new file mode 100644
index 0000000..1c0cd12
--- /dev/null
+++ b/src/Handler.cpp
@@ -0,0 +1,16 @@
+#include "Handler.hpp"
+
+std::vector<std::string> Handler::tokenizeArguments(const std::string &arguments, const std::string &delimiter) {
+ std::vector<std::string> tokens{};
+
+ auto start = 0U;
+ auto end = arguments.find(delimiter);
+ while (end != std::string::npos) {
+ tokens.emplace_back(arguments.substr(start, end - start));
+ start = end + delimiter.length();
+ end = arguments.find(delimiter, start);
+ }
+ tokens.emplace_back(arguments.substr(start, end));
+
+ return tokens;
+}
diff --git a/src/RelationshipHandler.cpp b/src/RelationshipHandler.cpp
new file mode 100644
index 0000000..5aab81b
--- /dev/null
+++ b/src/RelationshipHandler.cpp
@@ -0,0 +1,88 @@
+#include "Response.hpp"
+#include "RelationshipHandler.hpp"
+
+using std::string;
+using std::vector;
+
+vector<int> Handler::RelationShipHandler::buildCalArr(vector<string> nameV) {
+ string ges = nameV.at(0) + nameV.at(1);
+
+ for (auto &c : ges) {
+ c = tolower(c);
+ }
+
+ vector<int> v{};
+ for (auto c1 : ges) {
+ int count = 0;
+ for (auto c2: ges) {
+ if (c1 == c2) {
+ ++count;
+ }
+ }
+ v.push_back(count);
+ }
+
+ return v;
+}
+
+int Handler::RelationShipHandler::calculate(vector<string> names) {
+ sort(names.begin(), names.end());
+ vector<int> nums = buildCalArr(names);
+
+ while (nums.size() > 2) {
+ vector<int> tempNums{};
+ for (std::size_t i = 0; i < nums.size() / 2; i++) {
+ int temp = nums.at(i) + nums.at(nums.size() - i - 1);
+ if (nums.size() % 2 == 1 && i == nums.size() / 2) {
+ tempNums.push_back(nums.at(i));
+ } else {
+ if (temp < 10) {
+ tempNums.push_back(temp);
+ } else {
+ tempNums.push_back(temp / 10);
+ tempNums.push_back(temp % 10);
+ }
+ }
+ }
+ if (nums.size() % 2 == 1) {
+ tempNums.push_back(nums.at(nums.size() / 2));
+ }
+ nums.clear();
+ for (auto i: tempNums) {
+ nums.push_back(i);
+ }
+ }
+
+ return 10 * nums.at(0) + nums.at(1);
+}
+
+bool Handler::RelationShipHandler::isAlphaNum(const string &str) {
+ for (char i: str) {
+ if (!isalnum(i)) {
+ return false;
+ }
+ }
+ return true;
+}
+
+string Handler::RelationShipHandler::rsStart(vector<string> const &names) {
+ //vector<string> names = removeDupWord(str);
+ if (names.size() != 2) {
+ return "Gib zwei Namen ein Du Troll!";
+ }
+ if (!isAlphaNum(names.at(0))) {
+ return "Seit wann enthælt ein Name Sonderzeichen oder Zahlen? Spüre den Zorn von N!";
+ }
+ if (!isAlphaNum(names.at(1))) {
+ return "Seit wann enthælt ein Name Sonderzeichen oder Zahlen? Spüre den Zorn von N!";
+ }
+
+ int result = calculate(names);
+ return (names.at(0) + " und " + names.at(1) + " passen nach Angaben von N zu " + std::to_string(result) +
+ "% zusammen. Gratuliere!\n");
+}
+
+Handler::json Handler::relationShipHandler(const string &arguments, const string &session, void *payload) {
+ (void) payload;
+ return Response::simple_response(RelationShipHandler::rsStart(tokenizeArguments(arguments)));
+}
diff --git a/src/Response.cpp b/src/Response.cpp
new file mode 100644
index 0000000..050a537
--- /dev/null
+++ b/src/Response.cpp
@@ -0,0 +1,36 @@
+#include "Response.hpp"
+
+Response::json Response::simple_response(std::string const &text, std::string const &session, bool success) {
+ std::vector<json> reply_vec;
+ reply_vec.emplace_back(create_text(text));
+
+ return create_response(std::move(reply_vec), session, success);
+}
+
+Response::json Response::create_annotation(Reply::AnnotationType annotationType, std::string const &extra) {
+ json annotation(crow::json::type::Object);
+ annotation["type"] = Reply::GetStringAnnotationType(annotationType);
+ annotation["extra"] = extra;
+ return annotation;
+}
+
+Response::json Response::create_text(std::string const &text) {
+ std::vector<json> annotations;
+ annotations.emplace_back(create_annotation());
+ return create_text(text, std::move(annotations));
+}
+
+Response::json Response::create_text(std::string const &text, std::vector<json> &&annotations) {
+ json reply(crow::json::type::Object);
+ reply["text"] = text;
+ reply["annotations"] = std::move(annotations);
+ return reply;
+}
+
+Response::json Response::create_response(std::vector<json> &&reply, std::string const &session, bool success) {
+ json response(crow::json::type::Object);
+ response["success"] = success;
+ response["session"] = session;
+ response["reply"] = std::move(reply);
+ return response;
+} \ No newline at end of file
diff --git a/src/SimpleHandlers.cpp b/src/SimpleHandlers.cpp
new file mode 100644
index 0000000..033da53
--- /dev/null
+++ b/src/SimpleHandlers.cpp
@@ -0,0 +1,66 @@
+#include "SimpleHandlers.hpp"
+#include "Response.hpp"
+
+using namespace Response;
+
+Handler::json Handler::klingerHandler(std::string const &arguments, std::string const &session, void *payload) {
+ (void) payload;
+ return simple_response("Bonjour!");
+}
+
+Handler::json Handler::wikiHandler(std::string const &arguments, std::string const &session, void *payload) {
+ (void) payload;
+
+ std::vector<json> linkAnnotations;
+ linkAnnotations.emplace_back(create_annotation(Reply::AnnotationType::link, "Wikipedia"));
+ linkAnnotations.emplace_back(create_annotation(Reply::AnnotationType::underline));
+
+ std::vector<json> reply_vec;
+ reply_vec.emplace_back(create_text("Visit "));
+ reply_vec.emplace_back(create_text("https://eo.wikipedia.org/", std::move(linkAnnotations)));
+ reply_vec.emplace_back(create_text(" today!"));
+
+ return create_response(std::move(reply_vec), session, true);
+}
+
+Handler::json Handler::helpHandler(std::string const &arguments, std::string const &session, void *payload) {
+ auto commands = *(std::unordered_map<std::string, CommandHandler> *) payload;
+
+ std::vector<json> reply_vec;
+ reply_vec.emplace_back(create_text(
+ "Reading from the gospel according to saint N,\n"
+ "And you shall feast upon the fruits and vegetables as I do in the offering for they will make you strong.\n"
+ "\n"
+ "Dear my fellow users, these are my official commands, which are offered to my believers.\n"
+ "\n"));
+
+ for (auto const &itor : commands) {
+ std::vector<json> commandAnnotations;
+ commandAnnotations.emplace_back(create_annotation(Reply::AnnotationType::command));
+
+ reply_vec.emplace_back(create_text("- "));
+ reply_vec.emplace_back(create_text(itor.first, std::move(commandAnnotations)));
+ reply_vec.emplace_back(create_text(" " + itor.second.description + "\n"));
+ }
+
+ reply_vec.emplace_back(create_text(
+ "\n"
+ "Thanks for using me. Takk skal du ha.\n"
+ "Bussy\n"
+ "N"));
+
+ return create_response(std::move(reply_vec), session, true);
+}
+
+Handler::json Handler::stopHandler(std::string const &arguments, std::string const &session, void *payload) {
+ auto app = (crow::SimpleApp *) payload;
+ app->stop();
+
+ std::vector<json> annotations;
+ annotations.emplace_back(create_annotation(Reply::AnnotationType::bold));
+
+ std::vector<json> reply_vec;
+ reply_vec.emplace_back(create_text("stopped", std::move(annotations)));
+
+ return create_response(std::move(reply_vec), session, true);
+} \ No newline at end of file
diff --git a/src/main.cpp b/src/main.cpp
index 6e39f1c..72fd9a0 100644
--- a/src/main.cpp
+++ b/src/main.cpp
@@ -3,63 +3,32 @@
#include <unordered_map>
#include "crow.h"
+#include "sqdb.hpp"
-#include "AnnotationTypes.hpp"
+#include "Handler.hpp"
+#include "Response.hpp"
-#define NULL_STRING std::string("null")
-#define EMPTY_STRING std::string("")
-#define NO_SUCH_COMMAND std::string("no_such_command")
+/* BEGIN Handlers */
+#include "GetEssen.hpp"
+#include "SimpleHandlers.hpp"
+#include "RelationshipHandler.hpp"
-typedef crow::json::wvalue json;
-typedef std::function<json(std::string const &arguments, std::string const &session, void *payload)> handler_function;
-
-struct CommandHandler {
- [[nodiscard]] json exec(std::string const &arguments, std::string const &session) const {
- return func(arguments, session, payload);
- }
-
- handler_function func{nullptr};
- std::string description;
- void *payload{nullptr};
-};
-
-/* ------------------------------------------------------------------------------------------------------------------ */
-
-json helpHandler(std::string const &arguments, std::string const &session, void *payload);
-
-json stopHandler(std::string const &arguments, std::string const &session, void *payload);
-
-json wikiHandler(std::string const &arguments, std::string const &session, void *payload);
-
-json klingerHandler(std::string const &arguments, std::string const &session, void *payload);
-
-/* ------------------------------------------------------------------------------------------------------------------ */
-
-json simple_response(std::string const &text, std::string const &session = NULL_STRING, bool success = true);
-
-json create_annotation(Reply::AnnotationType annotation = Reply::AnnotationType::none,
- std::string const &extra = EMPTY_STRING);
-
-json create_text(std::string const &text);
-
-json create_text(std::string const &text, std::vector<json> &&annotations);
-
-json create_response(std::vector<json> &&reply, std::string const &session = NULL_STRING, bool success = true);
-
-/* ------------------------------------------------------------------------------------------------------------------ */
+/* END Handlers */
int main() {
crow::SimpleApp app;
// command --> handler
- std::unordered_map<std::string, CommandHandler> commands{
- {"stop", {.func = stopHandler, .description = "Stops the bot", .payload = &app}},
- {"wiki", {.func = wikiHandler, .description = "Send you to wikipedia"}},
- {"klinger", {.func = klingerHandler, .description = "Greats in french. Bonjour!"}},
+ std::unordered_map<std::string, Handler::CommandHandler> commands{
+ {"wiki", {.func = Handler::wikiHandler, .description = "Send you to wikipedia"}},
+ {"stop", {.func = Handler::stopHandler, .description = "Stops the bot", .payload = &app}},
+ {"mensa", {.func = Handler::mensaHandler, .description = "{adlershof, nord, sued} Shows the daily menu of the mensa at various places."}},
+ {"klinger", {.func = Handler::klingerHandler, .description = "Greats in french. Bonjour!"}},
+ {"relation", {.func = Handler::relationShipHandler, .description = "[name1] [name2] Shows the result of an odd astrological religious pseudo-algorithm, based on the fact how much blessing a relationship receives by the glorious N."}},
};
commands.insert({"help",
- {.func = helpHandler, .description = "This is my holy manual.", .payload = &commands}});
+ {.func = Handler::helpHandler, .description = "This is my holy manual.", .payload = &commands}});
CROW_ROUTE(app, "/")
.methods("POST"_method)
@@ -72,122 +41,20 @@ int main() {
if (!data.count("command"))
return crow::response(400, "malformed request: missing `command` field\n");
- std::string command = (data.count("command") ? data["command"].s() : NO_SUCH_COMMAND);
- std::string arguments = (data.count("arguments") ? data["arguments"].s() : EMPTY_STRING);
- std::string session = (data.count("session") ? data["session"].s() : NULL_STRING);
+ std::string command = (data.count("command") ? data["command"].s() : std::string(
+ "No such command!"));
+ std::string arguments = (data.count("arguments") ? data["arguments"].s() : std::string(""));
+ std::string session = (data.count("session") ? data["session"].s() : std::string("null"));
if (auto itor = commands.find(command); itor != commands.end()) {
auto handler = itor->second;
return crow::response{handler.exec(arguments, session)};
}
- return crow::response{simple_response("No such command!", NULL_STRING, false)};
+ return crow::response{Response::simple_response("No such command!", "null", false)};
});
app.port(18080).multithreaded().run();
std::cout << "Stopped successfully" << std::endl;
}
-
-/* ------------------------------------------------------------------------------------------------------------------ */
-
-json klingerHandler(std::string const &arguments, std::string const &session, void *payload) {
- (void) payload;
- return simple_response("Bonjour!");
-}
-
-json wikiHandler(std::string const &arguments, std::string const &session, void *payload) {
- (void) payload;
-
- std::vector<json> linkAnnotations;
- linkAnnotations.emplace_back(create_annotation(Reply::AnnotationType::link, "Wikipedia"));
- linkAnnotations.emplace_back(create_annotation(Reply::AnnotationType::underline));
-
- std::vector<json> reply_vec;
- reply_vec.emplace_back(create_text("Visit "));
- reply_vec.emplace_back(create_text("https://eo.wikipedia.org/", std::move(linkAnnotations)));
- reply_vec.emplace_back(create_text(" today!"));
-
- return create_response(std::move(reply_vec), session, true);
-}
-
-json helpHandler(std::string const &arguments, std::string const &session, void *payload) {
- auto commands = *(std::unordered_map<std::string, CommandHandler> *) payload;
-
- std::vector<json> reply_vec;
- reply_vec.emplace_back(create_text(
- "Reading from the gospel according to saint N,\n"
- "And you shall feast upon the fruits and vegetables as I do in the offering for they will make you strong.\n"
- "\n"
- "Dear my fellow users, these are my official commands, which are offered to my believers.\n"
- "\n"));
-
- for (auto const &itor : commands) {
- std::vector<json> commandAnnotations;
- commandAnnotations.emplace_back(create_annotation(Reply::AnnotationType::command));
-
- reply_vec.emplace_back(create_text("- "));
- reply_vec.emplace_back(create_text(itor.first, std::move(commandAnnotations)));
- reply_vec.emplace_back(create_text(" " + itor.second.description + "\n"));
- }
-
- reply_vec.emplace_back(create_text(
- "\n"
- "Thanks for using me. Takk skal du ha.\n"
- "Bussy\n"
- "N"));
-
- return create_response(std::move(reply_vec), session, true);
-}
-
-json stopHandler(std::string const &arguments, std::string const &session, void *payload) {
- auto app = (crow::SimpleApp *) payload;
- app->stop();
-
- std::vector<json> annotations;
- annotations.emplace_back(create_annotation(Reply::AnnotationType::bold));
-
- std::vector<json> reply_vec;
- reply_vec.emplace_back(create_text("stopped", std::move(annotations)));
-
- return create_response(std::move(reply_vec), session, true);
-}
-
-/* ------------------------------------------------------------------------------------------------------------------ */
-
-json simple_response(std::string const &text, std::string const &session, bool success) {
- std::vector<json> reply_vec;
- reply_vec.emplace_back(create_text(text));
-
- return create_response(std::move(reply_vec), session, success);
-}
-
-json create_annotation(Reply::AnnotationType annotationType, std::string const &extra) {
- json annotation(crow::json::type::Object);
- annotation["type"] = Reply::GetStringAnnotationType(annotationType);
- annotation["extra"] = extra;
- return annotation;
-}
-
-json create_text(std::string const &text) {
- std::vector<json> annotations;
- annotations.emplace_back(create_annotation());
- return create_text(text, std::move(annotations));
-}
-
-json create_text(std::string const &text, std::vector<json> &&annotations) {
- json reply(crow::json::type::Object);
- reply["text"] = text;
- reply["annotations"] = std::move(annotations);
- return reply;
-}
-
-json create_response(std::vector<json> &&reply, std::string const &session, bool success) {
- json response(crow::json::type::Object);
- response["success"] = success;
- response["session"] = session;
- response["reply"] = std::move(reply);
- return response;
-}
-
-// curl -d '{"command":"klinger", "b":"12"}' -H "Content-Type: application/json" -X POST http://localhost:18080/
diff --git a/src/sqdb.cpp b/src/sqdb.cpp
new file mode 100644
index 0000000..4a04f05
--- /dev/null
+++ b/src/sqdb.cpp
@@ -0,0 +1,437 @@
+#include <cassert>
+#include <cstdio>
+#include <cstring>
+#include <memory>
+
+#include <cstdlib>
+
+#include "sqdb.hpp"
+
+#include <unistd.h>
+
+using namespace sqdb;
+
+Exception::Exception(sqlite3 *db) {
+ m_errorCode = sqlite3_errcode(db);
+ auto *c = (SQDB_CHAR *)
+#ifdef SQDB_UTF8
+ sqlite3_errmsg
+#else
+ sqlite3_errmsg16
+#endif
+ (db);
+ m_errorMsg = SQDB_STRDUP(c);
+}
+
+Exception::Exception(sqlite3 *db, int errorCode)
+ : m_errorCode(errorCode) {
+ auto *c = (SQDB_CHAR *)
+#ifdef SQDB_UTF8
+ sqlite3_errmsg
+#else
+ sqlite3_errmsg16
+#endif
+ (db);
+ m_errorMsg = SQDB_STRDUP(c);
+}
+
+Exception::Exception(const SQDB_CHAR *errorMsg)
+ : m_errorCode(-1) {
+ m_errorMsg = SQDB_STRDUP(errorMsg);
+}
+
+Exception::~Exception() {
+ free(m_errorMsg);
+}
+
+int Exception::GetErrorCode() const {
+ return m_errorCode;
+}
+
+const SQDB_CHAR *Exception::GetErrorMsg() const {
+ return m_errorMsg;
+}
+
+RefCount::RefCount()
+ : m_refCount(nullptr) {
+}
+
+RefCount::RefCount(const RefCount &x)
+ : m_refCount(x.m_refCount) {
+}
+
+RefCount &RefCount::operator=(const RefCount &x) {
+ if (this != &x) {
+ m_refCount = x.m_refCount;
+ }
+ return *this;
+}
+
+void RefCount::IncRef() {
+ if (!m_refCount) {
+ m_refCount = new unsigned;
+ *m_refCount = 0;
+ }
+ ++*m_refCount;
+}
+
+unsigned RefCount::DecRef() {
+ assert(m_refCount);
+ unsigned value = --*m_refCount;
+ if (value == 0) {
+ delete m_refCount;
+ m_refCount = nullptr;
+ }
+ return value;
+}
+
+Blob::Blob(const void *data, int size)
+ : m_size(size) {
+ m_data = new char[size];
+ std::uninitialized_copy((char *) data, (char *) data + size, m_data);
+ IncRef();
+}
+
+Blob::Blob(const Blob &x)
+ : RefCount(x), m_data(x.m_data), m_size(x.m_size) {
+ IncRef();
+}
+
+Blob &Blob::operator=(const Blob &x) {
+ if (this != &x) {
+ RefCount::operator=(x);
+ IncRef();
+ m_data = x.m_data;
+ m_size = x.m_size;
+ }
+ return *this;
+}
+
+int Blob::GetSize() const {
+ return m_size;
+}
+
+const char *Blob::GetData() const {
+ return m_data;
+}
+
+Blob::~Blob() {
+ if (DecRef() == 0) {
+ delete[] m_data;
+ }
+}
+
+Convertor::Convertor(sqlite3 *db, sqlite3_stmt *stmt, int field)
+ : m_db(db), m_stmt(stmt), m_field(field) {
+}
+
+Convertor::operator int() const {
+ return GetInt();
+}
+
+Convertor::operator unsigned long() const {
+ return GetUnsignedLong();
+}
+
+Convertor::operator long long() const {
+ return GetLongLong();
+}
+
+Convertor::operator double() const {
+ return GetDouble();
+}
+
+Convertor::operator SQDB_STD_STRING() const {
+ return GetString();
+}
+
+Convertor::operator const SQDB_CHAR *() const {
+ return GetText();
+}
+
+Convertor::operator Blob() const {
+ return GetBlob();
+}
+
+int Convertor::GetInt() const {
+ assert(m_stmt);
+ return sqlite3_column_int(m_stmt, m_field);
+}
+
+unsigned long Convertor::GetUnsignedLong() const {
+ assert(m_stmt);
+ const char *data = (char *) sqlite3_column_blob(m_stmt, m_field);
+ return strtoul(data, nullptr, 10);
+}
+
+long long Convertor::GetLongLong() const {
+ assert(m_stmt);
+ return sqlite3_column_int64(m_stmt, m_field);
+}
+
+double Convertor::GetDouble() const {
+ assert(m_stmt);
+ return sqlite3_column_double(m_stmt, m_field);
+}
+
+SQDB_STD_STRING Convertor::GetString() const {
+ assert(m_stmt);
+ const auto *result = (const SQDB_CHAR *)
+#ifdef SQDB_UTF8
+ sqlite3_column_text
+#else
+ sqlite3_column_text16
+#endif
+ (m_stmt, m_field);
+ return result;
+}
+
+const SQDB_CHAR *Convertor::GetText() const {
+ assert(m_stmt);
+ const auto *result = (const SQDB_CHAR *)
+#ifdef SQDB_UTF8
+ sqlite3_column_text
+#else
+ sqlite3_column_text16
+#endif
+ (m_stmt, m_field);
+ return result;
+}
+
+Blob Convertor::GetBlob() const {
+ assert(m_stmt);
+ const void *data = sqlite3_column_blob(m_stmt, m_field);
+ int size = sqlite3_column_bytes(m_stmt, m_field);
+ return Blob(data, size);
+}
+
+Statement::Statement(sqlite3 *db, sqlite3_stmt *stmt)
+ : RefCount(), m_db(db), m_stmt(stmt), m_needReset(false) {
+ IncRef();
+}
+
+Statement::Statement(const Statement &x)
+ : RefCount(x), m_db(x.m_db), m_stmt(x.m_stmt), m_needReset(false) {
+ IncRef();
+}
+
+Statement &Statement::operator=(const Statement &x) {
+ if (this != &x) {
+ RefCount::operator=(x);
+ IncRef();
+ m_db = x.m_db;
+ m_stmt = x.m_stmt;
+ m_needReset = x.m_needReset;
+ }
+ return *this;
+}
+
+bool Statement::Next() {
+ assert(m_stmt);
+ int ret = SQLITE_BUSY;
+
+ while (ret == SQLITE_BUSY) {
+ ret = sqlite3_step(m_stmt);
+ m_needReset = true;
+ if (ret == SQLITE_DONE) {
+ return false;
+ } else if (ret == SQLITE_ROW) {
+ return true;
+ } else if (ret == SQLITE_BUSY) {
+ sleep(1);
+ continue;
+ } else {
+ throw Exception(m_db, ret);
+ }
+ }
+ return false;
+}
+
+Convertor Statement::GetField(int field) const {
+ return {m_db, m_stmt, field};
+}
+
+Statement::~Statement() {
+ if (DecRef() == 0) {
+ sqlite3_finalize(m_stmt);
+ }
+}
+
+void Statement::BindBlob(int i, const void *value, int n) {
+ if (m_needReset)
+ Reset();
+ DoBind(i, value, n);
+}
+
+void Statement::BindNull(int i) {
+ if (m_needReset)
+ Reset();
+ DoBind(i);
+}
+
+void Statement::DoBind(int i, int value) {
+ const int ret = sqlite3_bind_int(m_stmt, i, value);
+ CHECK(m_db, ret);
+}
+
+void Statement::DoBind(int i, long long value) {
+ const int ret = sqlite3_bind_int64(m_stmt, i, value);
+ CHECK(m_db, ret);
+}
+
+void Statement::DoBind(int i, double value) {
+ const int ret = sqlite3_bind_double(m_stmt, i, value);
+ CHECK(m_db, ret);
+}
+
+void Statement::DoBind(int i, const SQDB_STD_STRING &value) {
+ const int ret =
+#ifdef SQDB_UTF8
+ sqlite3_bind_text
+#else
+ sqlite3_bind_text16
+#endif
+ (m_stmt, i, value.c_str(), value.size() * sizeof(SQDB_STD_STRING::value_type), SQLITE_TRANSIENT);
+ CHECK(m_db, ret);
+}
+
+void Statement::DoBind(int i, const SQDB_CHAR *value) {
+ const int len = SQDB_STRLEN(value);
+
+ const int ret =
+#ifdef SQDB_UTF8
+ sqlite3_bind_text
+#else
+ sqlite3_bind_text16
+#endif
+ (m_stmt, i, value, len * sizeof(SQDB_CHAR), SQLITE_TRANSIENT);
+
+ CHECK(m_db, ret);
+}
+
+void Statement::DoBind(int i, const void *value, int n) {
+ const int ret = sqlite3_bind_blob(m_stmt, i, value, n, SQLITE_TRANSIENT);
+ CHECK(m_db, ret);
+}
+
+void Statement::DoBind(int i) {
+ const int ret = sqlite3_bind_null(m_stmt, i);
+ CHECK(m_db, ret);
+}
+
+void Statement::Reset() {
+ assert(m_needReset);
+ assert(m_stmt);
+
+ sqlite3_reset(m_stmt);
+ m_needReset = false;
+}
+
+QueryStr::QueryStr()
+ : m_buf(nullptr) {
+}
+
+const SQDB_CHAR *QueryStr::Format(const SQDB_CHAR *fmt, ...) {
+ va_list va;
+ va_start(va, fmt);
+#ifdef SQDB_UTF8
+ sqlite3_free(m_buf);
+ m_buf = sqlite3_vmprintf(fmt, va);
+#else
+ free(m_buf);
+ int len = _vscwprintf(fmt, va) + 1;
+ m_buf = (SQDB_CHAR*)malloc(len * sizeof(SQDB_CHAR));
+ vswprintf(m_buf, len, fmt, va);
+#endif
+
+ va_end(va);
+
+ return m_buf;
+}
+
+const SQDB_CHAR *QueryStr::Get() const {
+ return m_buf;
+}
+
+QueryStr::~QueryStr() {
+#ifdef SQDB_UTF8
+ sqlite3_free(m_buf);
+#else
+ free(m_buf);
+#endif
+}
+
+Db::Db(const SQDB_CHAR *fileName)
+ : RefCount() {
+#ifdef SQDB_UTF8
+ const int ret = sqlite3_open(fileName, &m_db);
+#else
+ const int ret = sqlite3_open16(fileName, &m_db);
+#endif
+ CHECK(m_db, ret);
+
+ IncRef();
+}
+
+void Db::BeginTransaction() {
+ Query(SQDB_MAKE_TEXT("BEGIN;")).Next();
+}
+
+void Db::CommitTransaction() {
+ Query(SQDB_MAKE_TEXT("COMMIT;")).Next();
+}
+
+void Db::RollbackTransaction() {
+ Query(SQDB_MAKE_TEXT("ROLLBACK;")).Next();
+}
+
+bool Db::TableExists(const SQDB_CHAR *tableName) {
+ QueryStr str;
+ Statement s =
+ Query(
+ str.Format(SQDB_MAKE_TEXT("select count(*) from sqlite_master where type='table' and name='%s';"),
+ tableName));
+ s.Next();
+ const int count = s.GetField(0);
+ return count > 0;
+}
+
+Statement Db::Query(const SQDB_CHAR *queryStr) {
+ sqlite3_stmt *stmt = nullptr;
+#ifdef SQDB_UTF8
+ const int ret = sqlite3_prepare(m_db, queryStr, -1, &stmt, nullptr);
+#else
+ const int ret = sqlite3_prepare16(m_db, queryStr, -1, &stmt, NULL);
+#endif
+ CHECK(m_db, ret);
+
+ return Statement(m_db, stmt);
+}
+
+long long Db::LastId() {
+ long long ret = sqlite3_last_insert_rowid(m_db);
+ return ret;
+}
+
+Db::Db(const Db &x)
+ : RefCount(x),
+ m_db(x.m_db) {
+ IncRef();
+}
+
+Db &Db::operator=(const Db &x) {
+ if (this != &x) {
+ RefCount::operator=(x);
+
+ IncRef();
+ m_db = x.m_db;
+ }
+ return *this;
+}
+
+Db::~Db() {
+ if (DecRef() == 0) {
+ sqlite3_close(m_db);
+ }
+}
+