From 27b71314507bda4da8edeb870051863697ca8127 Mon Sep 17 00:00:00 2001 From: acetone Date: Sun, 14 Aug 2022 11:17:35 +0300 Subject: [PATCH] updated to new generation of lib and simplify code base --- cpp-lib | 2 +- httpserver.cpp | 2 +- httpserver.h | 2 +- jsonanswer.cpp | 3 ++ jsonanswer.h | 3 ++ main.cpp | 34 ++++++-------- socketrunnable.cpp | 113 ++++++++++++++------------------------------- socketrunnable.h | 5 +- zsc-daemon.pro | 5 -- 9 files changed, 61 insertions(+), 108 deletions(-) diff --git a/cpp-lib b/cpp-lib index 7e870d4..6df0c41 160000 --- a/cpp-lib +++ b/cpp-lib @@ -1 +1 @@ -Subproject commit 7e870d45ee01e92f4219221b109fdd97ebde629c +Subproject commit 6df0c4188bdc9a71b62b76dcc9968e1194af257f diff --git a/httpserver.cpp b/httpserver.cpp index b4e43b0..058fc78 100755 --- a/httpserver.cpp +++ b/httpserver.cpp @@ -1,4 +1,4 @@ -// 2022 (c) GPLv3, acetone at i2pmail.org +// GPLv3 (c) acetone, 2022 // Zero Storage Captcha #include "httpserver.h" diff --git a/httpserver.h b/httpserver.h index 4009d7d..46c8e25 100755 --- a/httpserver.h +++ b/httpserver.h @@ -1,4 +1,4 @@ -// 2022 (c) GPLv3, acetone at i2pmail.org +// GPLv3 (c) acetone, 2022 // Zero Storage Captcha #ifndef HTTPSERVER_H diff --git a/jsonanswer.cpp b/jsonanswer.cpp index 54f2698..88e52e3 100755 --- a/jsonanswer.cpp +++ b/jsonanswer.cpp @@ -1,3 +1,6 @@ +// GPLv3 (c) acetone, 2022 +// Zero Storage Captcha + #include "jsonanswer.h" #include diff --git a/jsonanswer.h b/jsonanswer.h index 1fa1b9d..0d1a9cc 100755 --- a/jsonanswer.h +++ b/jsonanswer.h @@ -1,3 +1,6 @@ +// GPLv3 (c) acetone, 2022 +// Zero Storage Captcha + #ifndef JSONANSWER_H #define JSONANSWER_H diff --git a/main.cpp b/main.cpp index ca08323..48c13eb 100755 --- a/main.cpp +++ b/main.cpp @@ -1,4 +1,4 @@ -// 2022 (c) GPLv3, acetone at i2pmail.org +// GPLv3 (c) acetone, 2022 // Zero Storage Captcha #include "httpserver.h" @@ -6,33 +6,29 @@ #include #include -const std::string COPYRIGHT = "2022 (c) GPLv3, acetone"; +const std::string COPYRIGHT = "GPLv3 (c) acetone, 2022"; void usage() { - std::cout << "Zero Storage Captcha selfhosted REST API service 0.1 usage:\n\n" + std::cout << "Zero Storage Captcha selfhosted REST API service 0.2 usage:\n\n" "RUN\n" - "# To start QApplication without X-server (non-GUI system) should use:\n" - "# \"export QT_QPA_PLATFORM=offscreen\" in plain shell\n" - "# or\n" - "# \"Environment=QT_QPA_PLATFORM=offscreen\" in systemd service, [Service] section\n" - " -a --address Address to bind\n" + " -a --address Address to bind (127.0.0.1 by default)\n" " -p --port Port to bind (7697 by default)\n" - " -t --threads Working threads (" << std::thread::hardware_concurrency() << " by default)\n" << std::endl; + " -t --threads Working threads (hardware count by default)\n" << std::endl; - std::cout << "API (GET request, JSON response)\n" + std::cout << "REST API (GET request)\n" "# Each response contains a boolean \"status\" that indicates the logic success of the operation.\n" "# If !status, read \"message\" field.\n" " 1. [Generate captcha]\n" - " -> /generate?length= [>0] &difficulty= [0-5] &output= [base64|file] IF output=file: &filepath= [file system path]\n" - " <- \"token\", IF output=base64: \"png\" (base64 encoded picture)\n" + " -> /generate?length= [>0] &difficulty= [0-2]\n" + " <- { \"token\": \"CAPTCHA_TOKEN\", \"png\": \"base64 encoded picture\" }\n" " 2. [Validate captcha]\n" " -> /validate?answer= [user's answer] &token = [user's token]\n" - " <- boolean \"valid\"\n" - " 3. [Settings]\n" - " 3.1 Tokens case sensitive to captcha answer:\n" + " <- { \"valid\": true|false }\n" + " 3. [Global settings]\n" + " 3.1 Tokens case sensitive to captcha answer (disabled by default):\n" " -> /settings?case_sensitive= [enable|disable]\n" - " 3.2 Only numbers mode:\n" + " 3.2 Numbers only mode (disabled by default):\n" " -> /settings?number_mode= [enable|disable]\n" << std::endl; std::cout << COPYRIGHT << std::endl; @@ -48,7 +44,7 @@ int main(int argc, char *argv[]) int threads = static_cast(std::thread::hardware_concurrency()); quint16 port = 7697; - QString address; + QString address = "127.0.0.1"; for (int i = 1; i < argc; i++) { @@ -63,7 +59,7 @@ int main(int argc, char *argv[]) port = QString(argv[i+1]).toUShort(&ok); if (not ok) { - throw std::invalid_argument("Port not int"); + throw std::invalid_argument("Port not a number"); } } else if ((param == "--threads" or param == "-t") and i+1 < argc) @@ -72,7 +68,7 @@ int main(int argc, char *argv[]) threads = QString(argv[i+1]).toInt(&ok); if (not ok or threads < 1) { - throw std::invalid_argument("Threads not int or less then 1"); + throw std::invalid_argument("Threads not a number or less then 1"); } } else if (param == "--help" or param == "-h") diff --git a/socketrunnable.cpp b/socketrunnable.cpp index c9635ce..c395599 100755 --- a/socketrunnable.cpp +++ b/socketrunnable.cpp @@ -1,4 +1,4 @@ -// 2022 (c) GPLv3, acetone at i2pmail.org +// GPLv3 (c) acetone, 2022 // Zero Storage Captcha #include "socketrunnable.h" @@ -7,7 +7,6 @@ #include #include -#include constexpr int URL_PATH_MAX_LENGTH {200}; @@ -39,10 +38,10 @@ void SocketRunnable::settings() else if (setCaseSensitive == "disable") enable = false; else { - writeJsonError("Invalid case_sensitive argument: expected enable or disable"); + writeError("Invalid case_sensitive argument: expected enable or disable"); return; } - ZeroStorageCaptchaCrypto::KeyHolder::setCaseSensitive(enable); + ZeroStorageCaptcha::setCaseSensitive(enable); } QString numberMode = getValue("number_mode"); @@ -53,15 +52,15 @@ void SocketRunnable::settings() else if (numberMode == "disable") enable = false; else { - writeJsonError("Invalid number_mode argument: expected enable or disable"); + writeError("Invalid number_mode argument: expected enable or disable"); return; } - ZeroStorageCaptcha::setOnlyNumbersMode(enable); + ZeroStorageCaptcha::setNumbersOnlyMode(enable); } if (setCaseSensitive.isEmpty() and numberMode.isEmpty()) { - writeJsonError("Invalid key: expected case_sensitive or number_mode"); + writeError("Invalid key: expected case_sensitive or number_mode"); } else { @@ -73,69 +72,34 @@ void SocketRunnable::settings() void SocketRunnable::generate() { - bool success = false; + JsonAnswer answer; + QString errorMessage; - int length = getValue("length").toInt(&success); - if (not success) + int length = getValue("length").toInt(); + if (length < 0) { - writeJsonError("Invalid length: expected int > 0"); - return; + errorMessage += "Length must be greater than 0;"; } - int difficulty = getValue("difficulty").toInt(&success); - if (not success) + int difficulty = getValue("difficulty").toInt(); + if (difficulty < 0 or difficulty > 2) { - writeJsonError("Invalid difficulty: expected int (0, 5)"); - return; - } - if (not success) - { - writeJsonError("Invalid difficulty: expected int (0, 5)"); - return; + errorMessage += "Difficulty can take values from 0 to 2;"; } - auto res = ZeroStorageCaptcha::getCaptcha(length, difficulty); - if (res.token().isEmpty()) - { - writeJsonError("Temporary issue: token cache is full"); - return; - } + ZeroStorageCaptcha captcha; + captcha.setDifficulty(difficulty); + captcha.generateAnswer(length); + captcha.render(); - JsonAnswer result; - QString output = getValue("output"); - if (output == "file") + answer.setValue("status", errorMessage.isEmpty()); + if (not errorMessage.isEmpty()) { - QString filepath = getValue("filepath"); - filepath = QByteArray::fromPercentEncoding(filepath.toUtf8()); - filepath.replace('+', ' '); - filepath.replace("\\", "/"); - if (filepath.isEmpty()) - { - writeJsonError("Accepted flag to save picture to disk, but \"filepath\" is empty"); - return; - } - QFile f(filepath); - if (not f.open(QIODevice::WriteOnly)) - { - writeJsonError("Can not write file " + filepath); - return; - } - f.write(res.picture()); - f.close(); + answer.setValue("message", errorMessage); } - else if (output == "base64") - { - result.setValue("png", QString(res.picture().toBase64())); - } - else - { - writeJsonError("Invalid output type: expected file or base64"); - return; - } - - result.setValue("status", true); - result.setValue("token", res.token()); - m_socket->write(result.document()); + answer.setValue("png", QString(captcha.picturePng().toBase64())); + answer.setValue("token", captcha.token()); + m_socket->write(answer.document()); } void SocketRunnable::validate() @@ -143,14 +107,14 @@ void SocketRunnable::validate() QString token = getValue("token"); if (token.isEmpty()) { - writeJsonError("Empty token"); + writeError("Empty token"); return; } QString answer = getValue("answer"); if (answer.isEmpty()) { - writeJsonError("Empty answer"); + writeError("Empty answer"); } bool valid = ZeroStorageCaptcha::validate(answer, token); @@ -170,7 +134,6 @@ QString SocketRunnable::readBeforeSpace() { if (counter > URL_PATH_MAX_LENGTH) { - wariningLog ("SocketRunnable::readBeforeSpace() size > " + QString::number(URL_PATH_MAX_LENGTH) + ": " + result); break; } result += symbol; @@ -186,34 +149,28 @@ QString SocketRunnable::getValue(const QString &key) const QString pattern = key+"="; if (not result.contains(pattern)) return QString(); - result.remove(QRegularExpression("^.*"+QRegularExpression::escape(pattern))); + static QRegularExpression rgx_allToPattern("^.*"+QRegularExpression::escape(pattern)); + result.remove(rgx_allToPattern); if (result.isEmpty() or result.startsWith("&")) return QString(); - result.remove(QRegularExpression("&.*$")); + static QRegularExpression rgx_allFromAmpersand("&.*$"); + result.remove(rgx_allFromAmpersand); return result; } -void SocketRunnable::wariningLog(const QString &str) const -{ - qInfo().noquote() << - "\n" - " " + str + "\n" - ""; -} - void SocketRunnable::reader() { QString reqType = readBeforeSpace(); if (reqType != "GET") { - writeJsonError("Invalid request: expected GET"); - m_socket->waitForBytesWritten(); + writeError("Invalid request: expected GET"); return; } QString urlPath = readBeforeSpace(); m_request = urlPath; - m_request.remove(QRegularExpression("^.*\\?")); + static QRegularExpression rgx_allToQuestionMark("^.*\\?"); + m_request.remove(rgx_allToQuestionMark); m_request.remove('\r'); m_request.remove('\n'); @@ -231,11 +188,11 @@ void SocketRunnable::reader() } else { - writeJsonError("Invalid request path: expected /generate, /validate or /settings"); + writeError("Invalid request path: expected /generate, /validate or /settings"); } } -void SocketRunnable::writeJsonError(const QString &text) +void SocketRunnable::writeError(const QString &text) { JsonAnswer answer; answer.setError(text); diff --git a/socketrunnable.h b/socketrunnable.h index c99b93e..a27847d 100755 --- a/socketrunnable.h +++ b/socketrunnable.h @@ -1,4 +1,4 @@ -// 2022 (c) GPLv3, acetone at i2pmail.org +// GPLv3 (c) acetone, 2022 // Zero Storage Captcha #ifndef SOCKETRUNNABLE_H @@ -28,9 +28,8 @@ private: inline QString readBeforeSpace(); inline QString getValue(const QString& key) const; - void wariningLog(const QString& str) const; void reader(); - void writeJsonError(const QString& text); + void writeError(const QString &text); }; #endif // SOCKETRUNNABLE_H diff --git a/zsc-daemon.pro b/zsc-daemon.pro index c50100d..cd7e577 100755 --- a/zsc-daemon.pro +++ b/zsc-daemon.pro @@ -11,15 +11,10 @@ SOURCES += \ jsonanswer.cpp \ main.cpp \ socketrunnable.cpp \ - cpp-lib/zerostoragecaptchacrypto.cpp \ cpp-lib/zerostoragecaptcha.cpp HEADERS += \ httpserver.h \ jsonanswer.h \ socketrunnable.h \ - cpp-lib/zerostoragecaptchacrypto.h \ cpp-lib/zerostoragecaptcha.h - -LIBS += \ - -lcrypto