diff options
author | Max Kusatz <max@trialserver.de> | 2020-08-22 14:08:46 +0200 |
---|---|---|
committer | Max Kusatz <max@trialserver.de> | 2020-08-22 14:08:46 +0200 |
commit | 598218e9e1d6e97b7793b0cff2c0d50cab879fa4 (patch) | |
tree | b0c80ca9c038195f4f473a34cb99d0c01479676a /src | |
parent | 537a243882f49cd95b304ecf283de950440fd66a (diff) | |
download | n_core-598218e9e1d6e97b7793b0cff2c0d50cab879fa4.tar.gz n_core-598218e9e1d6e97b7793b0cff2c0d50cab879fa4.zip |
Mensa working sort of with JSON
Diffstat (limited to 'src')
-rw-r--r-- | src/N-Commands/KlingerHandler.cpp | 7 | ||||
-rw-r--r-- | src/N-Commands/KlingerHandler.hpp (renamed from src/N-Commands/KlingerHandler.h) | 3 | ||||
-rw-r--r-- | src/N-Commands/RelationshipHandler.cpp | 10 | ||||
-rw-r--r-- | src/N-Commands/RelationshipHandler.h | 10 | ||||
-rw-r--r-- | src/N-Commands/RelationshipHandler.hpp | 10 | ||||
-rw-r--r-- | src/Utilities/GetEssen.cpp | 250 | ||||
-rw-r--r-- | src/Utilities/GetEssen.hpp | 38 | ||||
-rw-r--r-- | src/Utilities/Logger.cpp | 48 | ||||
-rw-r--r-- | src/Utilities/Logger.hpp | 38 | ||||
-rw-r--r-- | src/Utilities/Utilities.hpp | 142 | ||||
-rw-r--r-- | src/Utilities/sqdb.cpp | 506 | ||||
-rw-r--r-- | src/Utilities/sqdb.hpp | 207 | ||||
-rw-r--r-- | src/main.cpp | 32 |
13 files changed, 1273 insertions, 28 deletions
diff --git a/src/N-Commands/KlingerHandler.cpp b/src/N-Commands/KlingerHandler.cpp index 448532c..c596952 100644 --- a/src/N-Commands/KlingerHandler.cpp +++ b/src/N-Commands/KlingerHandler.cpp @@ -2,9 +2,10 @@ // Created by max on 11.08.20. // -#include "KlingerHandler.h" +#include "KlingerHandler.hpp" + +void KlingerHandler::onCall(const Mongoose::Request& request, Mongoose::JsonResponse& response) { -void KlingerHandler::onCall(const Mongoose::Request& request, Mongoose::StreamResponse& response) { std::cout << "Klinger was called\n"; - response << "Bonjour!"; + response["text"] = "Bonjour!"; } diff --git a/src/N-Commands/KlingerHandler.h b/src/N-Commands/KlingerHandler.hpp index 2fc5267..c078303 100644 --- a/src/N-Commands/KlingerHandler.h +++ b/src/N-Commands/KlingerHandler.hpp @@ -2,6 +2,7 @@ // Created by max on 11.08.20. // #include <mongoose/Server.h> +#include <mongoose/JsonResponse.h> #include <string> #ifndef DORGODBOTBACKEND_KLINGERHANDLER_H #define DORGODBOTBACKEND_KLINGERHANDLER_H @@ -9,7 +10,7 @@ class KlingerHandler { public: - void onCall(const Mongoose::Request& request, Mongoose::StreamResponse& response) ; + void onCall(const Mongoose::Request& request, Mongoose::JsonResponse& response) ; }; diff --git a/src/N-Commands/RelationshipHandler.cpp b/src/N-Commands/RelationshipHandler.cpp index 808e17c..46991a3 100644 --- a/src/N-Commands/RelationshipHandler.cpp +++ b/src/N-Commands/RelationshipHandler.cpp @@ -1,4 +1,4 @@ -#include "RelationshipHandler.h" +#include "RelationshipHandler.hpp" #include<string> #include<vector> #include <algorithm> @@ -111,12 +111,14 @@ string rsStart(vector<string> names) { return (names.at(0) + " und " + names.at(1) + " passen nach Angaben von N zu " + to_string(result) + "% zusammen. Gratuliere!\n"); } -void RelationshipHandler::onCall(Mongoose::Request& request, Mongoose::StreamResponse& response) { +void RelationshipHandler::onCall(Mongoose::Request& request, Mongoose::JsonResponse& response) { - // only react when command was issued after this boot std::cout << "/relation was called\n"; - response << rsStart(vector<string>{request.get("name1", "Lukas"), request.get("name2", "cpp")}); + response["text"] = rsStart(vector<string>{request.get("name1", "Lukas"), request.get("name2", "cpp")}); + + response["success"] = "1"; + response["session"] = "NULL"; /*if (messagePtr->date > telegram->getBootDate()) { log(messagePtr->from->username + ": "+messagePtr->text); diff --git a/src/N-Commands/RelationshipHandler.h b/src/N-Commands/RelationshipHandler.h deleted file mode 100644 index 251cabf..0000000 --- a/src/N-Commands/RelationshipHandler.h +++ /dev/null @@ -1,10 +0,0 @@ -#pragma once -#include <mongoose.h> -#include <mongoose/Request.h> -#include <mongoose/StreamResponse.h> - -class RelationshipHandler { -public: - void onCall(Mongoose::Request& request, Mongoose::StreamResponse& response); - -}; diff --git a/src/N-Commands/RelationshipHandler.hpp b/src/N-Commands/RelationshipHandler.hpp new file mode 100644 index 0000000..fe9c0ea --- /dev/null +++ b/src/N-Commands/RelationshipHandler.hpp @@ -0,0 +1,10 @@ +#pragma once +#include <mongoose.h> +#include <mongoose/Request.h> +#include <mongoose/JsonResponse.h> + +class RelationshipHandler { +public: + void onCall(Mongoose::Request& request, Mongoose::JsonResponse& response); + +}; diff --git a/src/Utilities/GetEssen.cpp b/src/Utilities/GetEssen.cpp new file mode 100644 index 0000000..56742cc --- /dev/null +++ b/src/Utilities/GetEssen.cpp @@ -0,0 +1,250 @@ +// +// Created by max on 18.12.18. +// + +#include "Utilities/GetEssen.hpp" + + +using namespace boost::posix_time; + +void returnEssen(Mongoose::Request &request, Mongoose::JsonResponse &response){ + std::cout << "mensa was called\n"; + mensa Mensa_ID; + std::string mensa_name = request.get("argument1", "adlershof"); + std::string offset_request = request.get("argument2", "0"); + + + if(mensa_name == "adlershof"){ + Mensa_ID = mensa::Adlershof; + } else if(mensa_name == "nord"){ + Mensa_ID = mensa::Nord; + }else if (mensa_name == "sued" || mensa_name == "süd"){ + Mensa_ID = mensa::Sued; + } + + int offset = std::stoi(offset_request); + + + response["success"] = "1"; + response["session"] = "NULL"; + response["text"] = getEssen(Mensa_ID, offset); + + +} + +bool db_update_mensa_message(const int& mensaID, const ptime& mensaDatum, const string& message){ + sqdb::Db db("../main.sqlite"); + string newTime = to_simple_string(ptime(second_clock::local_time())); + string mensaDatumString = to_string(mensaDatum.date().year()) + "-" + mensaDatum.date().month().as_short_string() + "-" + 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 db_insert_mensa_message(const int& mensaID, const ptime& mensaDatum, const string& message){ + sqdb::Db db("../main.sqlite"); + string newTime = to_simple_string(ptime(second_clock::local_time())); + string mensaDatumString = to_string(mensaDatum.date().year()) + "-" + mensaDatum.date().month().as_short_string() + "-" + 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; +} + +Mensa_Essen db_get_mensa_message(const int& mensaID, const ptime& mensaDatum) { + Mensa_Essen out; + sqdb::Db db("../main.sqlite"); + + sqdb::QueryStr str; + string mensaDatumString = to_string(mensaDatum.date().year()) + "-" + mensaDatum.date().month().as_short_string() + "-" + 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(time_from_string(LastModified)); + + out.ID = ID; + out.Message = Message; + out.Datum = mensaDatum; + out.LastModified = LMDate; + return out; + } + } catch (sqdb::Exception &exception){ + log("Error code: " + to_string(exception.GetErrorCode()) + " Error Message: " + exception.GetErrorMsg() + "\n"); + } + out.ID = -2; + return out; + +} + +string getEssen(const int mensa_ID, const int offset) { + Mensa_Essen 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 = 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){ + log("Error downloading file"); + 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 " + to_string(Essen.Datum.date().day().as_number()) + "." + to_string(Essen.Datum.date().month().as_number()) + "." + to_string(Essen.Datum.date().year()) + ":"; + break; + case mensa ::Nord: + out = "<b>Mensa Nord</b> am " + to_string(Essen.Datum.date().day().as_number()) + "." + to_string(Essen.Datum.date().month().as_number()) + "." + to_string(Essen.Datum.date().year()) + ":"; + break; + case mensa::Sued : + out = "<b>Mensa Sued</b> am " + to_string(Essen.Datum.date().day().as_number()) + "." + to_string(Essen.Datum.date().month().as_number()) + "." + to_string(Essen.Datum.date().year()) + ":"; + break; + default: + out = "Mensa Unbekannt am " + to_string(Essen.Datum.date().day().as_number()) + "." + to_string(Essen.Datum.date().month().as_number()) + "." + to_string(Essen.Datum.date().year()) + ":"; + break; + + } + out += Essen.Message; + out += "\n (Stand: " + to_simple_string(Essen.LastModified) + ")\n"; + + return out; + } + + if (Essen.LastModified + 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 " + to_string(Essen.Datum.date().day().as_number()) + "." + to_string(Essen.Datum.date().month().as_number()) + "." + to_string(Essen.Datum.date().year()) + ":"; + break; + case mensa::Nord : + out = "<b>Mensa Nord</b> am " + to_string(Essen.Datum.date().day().as_number()) + "." + to_string(Essen.Datum.date().month().as_number()) + "." + to_string(Essen.Datum.date().year()) + ":"; + break; + case mensa::Sued : + out = "<b>Mensa Sued</b> am " + to_string(Essen.Datum.date().day().as_number()) + "." + to_string(Essen.Datum.date().month().as_number()) + "." + to_string(Essen.Datum.date().year()) + ":"; + break; + default: + out = "Mensa Unbekannt am " + to_string(Essen.Datum.date().day().as_number()) + "." + to_string(Essen.Datum.date().month().as_number()) + "." + to_string(Essen.Datum.date().year()) + ":"; + break; + + } + out += Essen.Message; + out += "\n (Stand: " + to_simple_string(Essen.LastModified) + ")\n"; + return out; + } else return "Wer denkst du bin ich? Ein Zeitreisender?\nIch kann zwar in die Zukunft gucken, aber nur 28 Tage"; + +} + +string 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::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; + +} diff --git a/src/Utilities/GetEssen.hpp b/src/Utilities/GetEssen.hpp new file mode 100644 index 0000000..813d115 --- /dev/null +++ b/src/Utilities/GetEssen.hpp @@ -0,0 +1,38 @@ +#pragma once + +#include "sqdb.hpp" +#include "Utilities/Utilities.hpp" +#include "Utilities/Logger.hpp" +//#include <tgbot/tgbot.h> +#include <boost/date_time/posix_time/posix_time.hpp> +#include <boost/date_time/gregorian/gregorian.hpp> +#include <boost/date_time/date_formatting.hpp> +#include <curl/curl.h> +#include <iostream> +#include <sys/stat.h> +#include <iostream> +#include <string> +#include <mongoose/JsonResponse.h> +#include <mongoose/Server.h> + +using namespace std; +using namespace boost::posix_time; + +void returnEssen(Mongoose::Request &request, Mongoose::JsonResponse &response); + +string getEssen(int mensa_ID, int offset); + +string read_mensa_message_from_file(const int& mensa_id_file); + +struct Mensa_Essen{ + int ID{191}; + string Message{"nothing got back! There was an error!!"}; + boost::posix_time::ptime Datum; + boost::posix_time::ptime LastModified; +}; +enum mensa { + Adlershof = 191, Nord = 147, Sued = 367 +}; + + + diff --git a/src/Utilities/Logger.cpp b/src/Utilities/Logger.cpp new file mode 100644 index 0000000..333ae16 --- /dev/null +++ b/src/Utilities/Logger.cpp @@ -0,0 +1,48 @@ +#include "Logger.hpp" + +/*void Logger::Log(const TelegramMessage& message, LogLevel logLevel) { + Log("[" + message->from->username + "] " + message->text, logLevel); +}*/ + +void Logger::Log(const string& inMsg, LogLevel logLevel) { + string outMsg; + +#ifdef NDEBUG + if (logLevel == LogLevel::lDEBUG) { + // don't log debug messages in release mode + return; + } +#endif + + // prefix message with time + outMsg = "[" + NOW_STRING + "]"; + + // TODO: let at least some (ERROR, WARN) levels go to stderr, not stdout + // prefix log level - TODO: allow filtering based on globally set level? + switch (logLevel) { + case LogLevel::lSTATUS: + outMsg += " [STATUS] " + inMsg; + break; + case LogLevel::lDEBUG: + outMsg += " [DEBUG] " + inMsg; + break; + case LogLevel::lINFO: + outMsg += " [INFO] " + inMsg; + break; + case LogLevel::lWARN: + outMsg += " [WARN] " + inMsg; + break; + case LogLevel::lERROR: + outMsg += " [ERROR] " + inMsg; + break; + } + + Print(outMsg); +} + +void Logger::Print(const string& msg) { + // console output + cout << msg << endl; + + // file output | TODO: Logger needs state for that - or at least needs to pretend to have one +}
\ No newline at end of file diff --git a/src/Utilities/Logger.hpp b/src/Utilities/Logger.hpp new file mode 100644 index 0000000..f319d93 --- /dev/null +++ b/src/Utilities/Logger.hpp @@ -0,0 +1,38 @@ +#pragma once + +#include <string> +#include <iostream> + + +#include "Utilities.hpp" + +using std::string; +using std::cout; +using std::endl; + +#define logWarn(x) Logger::Log(x, Logger::LogLevel::lWARN) +#define logDebug(x) Logger::Log(x, Logger::LogLevel::lDEBUG) +#define logError(x) Logger::Log(x, Logger::LogLevel::lERROR) + +#define logStatus(x) Logger::Log(x, Logger::LogLevel::lSTATUS) + +#define log(x) logI(x) +#define logI(x) Logger::Log(x, Logger::LogLevel::lINFO) + +class Logger { +public: + enum class LogLevel { + lSTATUS, // general bot status messages, everything it logs o it's own + lDEBUG, // messages logged for development purposes + lINFO, // messages logged on certain events (eg. command executed) + lWARN, + lERROR + }; + + static void Log(const string& msg, LogLevel logLevel); + //static void Log(const TelegramMessage& message, LogLevel logLevel); + +private: + static void Print(const string& msg); +}; + diff --git a/src/Utilities/Utilities.hpp b/src/Utilities/Utilities.hpp new file mode 100644 index 0000000..0c9f435 --- /dev/null +++ b/src/Utilities/Utilities.hpp @@ -0,0 +1,142 @@ +#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/src/Utilities/sqdb.cpp b/src/Utilities/sqdb.cpp new file mode 100644 index 0000000..0b88139 --- /dev/null +++ b/src/Utilities/sqdb.cpp @@ -0,0 +1,506 @@ +#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); + } +} + diff --git a/src/Utilities/sqdb.hpp b/src/Utilities/sqdb.hpp new file mode 100644 index 0000000..ea38787 --- /dev/null +++ b/src/Utilities/sqdb.hpp @@ -0,0 +1,207 @@ +#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/main.cpp b/src/main.cpp index b11a8e7..8a08b4a 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -1,33 +1,45 @@ -#include "N-Commands/KlingerHandler.h" -#include "N-Commands/RelationshipHandler.h" +#include "N-Commands/KlingerHandler.hpp" +#include "N-Commands/RelationshipHandler.hpp" +#include "Utilities/GetEssen.hpp" +#include "Utilities/Logger.hpp" +#include "Utilities/Utilities.hpp" +#include "Utilities/sqdb.hpp" #include <mongoose/Server.h> #include <mongoose/WebController.h> +#include <mongoose/JsonController.h> using namespace std; using namespace Mongoose; -class MyController : public WebController +class MyController : public JsonController { public: - void hello(Request &request, StreamResponse &response) + void hello(Request &request, JsonResponse &response) { - response << "Hello " << htmlEntities(request.get("name", "... what's your name ?")) << endl; + response["text"] = "Hello " + htmlEntities(request.get("name", "... what's your name ?")) + "\n"; + response["success"] = "1"; + response["session"] = "NULL"; } - void klinger(Request &request, StreamResponse &response){ + void klinger(Request &request, JsonResponse &response){ KlingerHandler klinger; klinger.onCall(request, response); } - void relation(Request &request, StreamResponse &response){ + void relation(Request &request, JsonResponse &response){ RelationshipHandler relation; relation.onCall(request, response); } + void mensa(Request &request, JsonResponse &response){ + returnEssen(request, response); + } void setup() { - addRoute("GET", "/hello", MyController, hello); - addRoute("GET", "/klinger", MyController, klinger); - addRoute("GET", "/relation", MyController, relation); + setPrefix("/api/v1"); + addRouteResponse("GET", "/hello", MyController, hello, JsonResponse); + addRouteResponse("GET", "/klinger", MyController, klinger, JsonResponse); + addRouteResponse("GET", "/relation", MyController, relation, JsonResponse); + addRouteResponse("GET", "/mensa", MyController, mensa, JsonResponse); } }; |