diff --git a/src/cpp/HTTPInterface/ElopageWebhook.cpp b/src/cpp/HTTPInterface/ElopageWebhook.cpp index 74f8b2955..a092ca778 100644 --- a/src/cpp/HTTPInterface/ElopageWebhook.cpp +++ b/src/cpp/HTTPInterface/ElopageWebhook.cpp @@ -16,6 +16,7 @@ using namespace Poco::Data::Keywords; #include "../tasks/SendEmailTask.h" #include "../model/EmailVerificationCode.h" +#include "../model/ElopageBuy.h" @@ -100,6 +101,8 @@ void ElopageWebhook::handleRequest(Poco::Net::HTTPServerRequest& request, Poco:: if (event == "lesson.viewed") { return; } + + // write stream result also to file static Poco::Mutex mutex; @@ -175,6 +178,7 @@ void HandleElopageRequestTask::writeUserIntoDB() } } + int HandleElopageRequestTask::getUserIdFromDB() { auto cm = ConnectionManager::getInstance(); @@ -203,87 +207,118 @@ int HandleElopageRequestTask::run() return 0; } - mEmail = mRequestData.get("payer[email]", ""); - mFirstName = mRequestData.get("payer[first_name]", ""); - mLastName = mRequestData.get("payer[last_name]", ""); - std::string order_id = mRequestData.get("order_id", ""); + // elopage buy + Poco::AutoPtr elopageBuy(new ElopageBuy(mRequestData)); + if (elopageBuy->errorCount() > 0) { + getErrors(elopageBuy); + } + UniLib::controller::TaskPtr saveElopageBuy(new ModelInsertTask(elopageBuy)); + saveElopageBuy->scheduleTask(saveElopageBuy); + // check product id + Poco::UInt64 product_id = 0; + try { + product_id = stoull(mRequestData.get("product[id]", "0")); + } + catch (const std::invalid_argument& ia) { + std::cerr << __FUNCTION__ << "Invalid argument: " << ia.what() << '\n'; + } + catch (const std::out_of_range& oor) { + std::cerr << __FUNCTION__ << "Out of Range error: " << oor.what() << '\n'; + } + catch (const std::logic_error & ler) { + std::cerr << __FUNCTION__ << "Logical error: " << ler.what() << '\n'; + } + catch (...) { + std::cerr << __FUNCTION__ << "Unknown error" << '\n'; + } + std::string order_id = mRequestData.get("order_id", ""); addError(new ParamError("HandleElopageRequestTask", "order_id", order_id.data())); - // validate input - if (!validateInput()) { - // if input is invalid we can stop now - sendErrorsAsEmail(); - return -1; + // only for product 36001 and 43741 create user accounts and send emails + if (product_id == 36001 || product_id == 43741) { + mEmail = mRequestData.get("payer[email]", ""); + mFirstName = mRequestData.get("payer[first_name]", ""); + mLastName = mRequestData.get("payer[last_name]", ""); + + // validate input + if (!validateInput()) { + // if input is invalid we can stop now + sendErrorsAsEmail(); + return -1; + } + + // if user exist we can stop now + if (getUserIdFromDB()) { + sendErrorsAsEmail(); + return -2; + } + + // if user with this email didn't exist + // we can create a new user and send a email to him + + // prepare email in advance + // create connection to email server + UniLib::controller::TaskPtr prepareEmail(new PrepareEmailTask(ServerConfig::g_CPUScheduler)); + prepareEmail->scheduleTask(prepareEmail); + + // write user entry into db + writeUserIntoDB(); + + // get user id from db + int user_id = getUserIdFromDB(); + // we didn't get a user_id, something went wrong + if (!user_id) { + addError(new Error("User loadEntryDBId", "user_id is zero")); + sendErrorsAsEmail(); + return -3; + } + + // email verification code + Poco::AutoPtr emailVerification(new EmailVerificationCode(user_id)); + + // create email verification code + if (!emailVerification->getCode()) { + // exit if email verification code is empty + addError(new Error("Email verification", "code is empty, error in random?")); + sendErrorsAsEmail(); + return -4; + } + + // write email verification code into db + UniLib::controller::TaskPtr saveEmailVerificationCode(new ModelInsertTask(emailVerification)); + saveEmailVerificationCode->scheduleTask(saveEmailVerificationCode); + + // send email to user + auto message = new Poco::Net::MailMessage; + + message->addRecipient(Poco::Net::MailRecipient(Poco::Net::MailRecipient::PRIMARY_RECIPIENT, mEmail)); + message->setSubject("Gradido: E-Mail Verification"); + std::stringstream ss; + ss << "Hallo " << mFirstName << " " << mLastName << "," << std::endl << std::endl; + ss << "Du oder jemand anderes hat sich soeben mit dieser E-Mail Adresse bei Gradido registriert. " << std::endl; + ss << "Wenn du es warst, klicke bitte auf den Link: " << ServerConfig::g_serverPath << "/checkEmail/" << emailVerification->getCode() << std::endl; + //ss << "oder kopiere den Code: " << mEmailVerificationCode << " selbst dort hinein." << std::endl; + ss << "oder kopiere den obigen Link in Dein Browserfenster." << std::endl; + ss << std::endl; + ss << "Mit freundlichen Grüße" << std::endl; + ss << "Dario, Gradido Server Admin" << std::endl; + + message->addContent(new Poco::Net::StringPartSource(ss.str())); + + UniLib::controller::TaskPtr sendEmail(new SendEmailTask(message, ServerConfig::g_CPUScheduler, 1)); + sendEmail->setParentTaskPtrInArray(prepareEmail, 0); + sendEmail->setParentTaskPtrInArray(saveEmailVerificationCode, 1); + sendEmail->scheduleTask(sendEmail); } - // if user exist we can stop now - if (getUserIdFromDB()) { - sendErrorsAsEmail(); - return -2; - } - - // if user with this email didn't exist - // we can create a new user and send a email to him - // prepare email in advance - // create connection to email server - UniLib::controller::TaskPtr prepareEmail(new PrepareEmailTask(ServerConfig::g_CPUScheduler)); - prepareEmail->scheduleTask(prepareEmail); - // write user entry into db - writeUserIntoDB(); - // get user id from db - int user_id = getUserIdFromDB(); - // we didn't get a user_id, something went wrong - if (!user_id) { - addError(new Error("User loadEntryDBId", "user_id is zero")); - sendErrorsAsEmail(); - return -3; - } - - Poco::AutoPtr emailVerification(new EmailVerificationCode(user_id)); - - // create email verification code - if (!emailVerification->getCode()) { - // exit if email verification code is empty - addError(new Error("Email verification", "code is empty, error in random?")); - sendErrorsAsEmail(); - return -4; - } - - // write email verification code into db - UniLib::controller::TaskPtr saveEmailVerificationCode(new ModelInsertTask(emailVerification)); - saveEmailVerificationCode->scheduleTask(saveEmailVerificationCode); - - // send email to user - auto message = new Poco::Net::MailMessage; - - message->addRecipient(Poco::Net::MailRecipient(Poco::Net::MailRecipient::PRIMARY_RECIPIENT, mEmail)); - message->setSubject("Gradido: E-Mail Verification"); - std::stringstream ss; - ss << "Hallo " << mFirstName << " " << mLastName << "," << std::endl << std::endl; - ss << "Du oder jemand anderes hat sich soeben mit dieser E-Mail Adresse bei Elopage für Gradido angemeldet. " << std::endl; - ss << "Um dein Gradido Konto anzulegen und deine E-Mail zu bestätigen," << std::endl; - ss << "klicke bitte auf den Link: https://gradido2.dario-rekowski.de/account/checkEmail/" << emailVerification->getCode() << std::endl; - ss << "oder kopiere den Code: " << emailVerification->getCode() << " selbst dort hinein." << std::endl << std::endl; - ss << "Mit freundlichen Grüße" << std::endl; - ss << "Dario, Gradido Server Admin" << std::endl; - - - message->addContent(new Poco::Net::StringPartSource(ss.str())); - - UniLib::controller::TaskPtr sendEmail(new SendEmailTask(message, ServerConfig::g_CPUScheduler, 1)); - sendEmail->setParentTaskPtrInArray(prepareEmail, 0); - sendEmail->setParentTaskPtrInArray(saveEmailVerificationCode, 1); - sendEmail->scheduleTask(sendEmail); - // if errors occured, send via email - //if (errorCount() > 1) { + if (errorCount() > 1) { sendErrorsAsEmail(); - //} - + } return 0; } \ No newline at end of file diff --git a/src/cpp/HTTPInterface/PageRequestHandlerFactory.cpp b/src/cpp/HTTPInterface/PageRequestHandlerFactory.cpp index dab43617a..8efd024c4 100644 --- a/src/cpp/HTTPInterface/PageRequestHandlerFactory.cpp +++ b/src/cpp/HTTPInterface/PageRequestHandlerFactory.cpp @@ -179,15 +179,15 @@ Poco::Net::HTTPRequestHandler* PageRequestHandlerFactory::handleCheckEmail(Sessi auto str = uri.substr(pos + 1); verificationCode = stoull(uri.substr(pos + 1)); } catch (const std::invalid_argument& ia) { - std::cerr << "Invalid argument: " << ia.what() << '\n'; + std::cerr << __FUNCTION__ << "Invalid argument: " << ia.what() << '\n'; } catch (const std::out_of_range& oor) { - std::cerr << "Out of Range error: " << oor.what() << '\n'; + std::cerr << __FUNCTION__ << "Out of Range error: " << oor.what() << '\n'; } catch (const std::logic_error & ler) { - std::cerr << "Logical error: " << ler.what() << '\n'; + std::cerr << __FUNCTION__ << "Logical error: " << ler.what() << '\n'; } catch (...) { - std::cerr << "Unknown error" << '\n'; + std::cerr << __FUNCTION__ << "Unknown error" << '\n'; } } diff --git a/src/cpp/MySQL/MysqlTable.cpp b/src/cpp/MySQL/MysqlTable.cpp index 1dee2e32c..150c1c074 100644 --- a/src/cpp/MySQL/MysqlTable.cpp +++ b/src/cpp/MySQL/MysqlTable.cpp @@ -2,6 +2,8 @@ #include "Poco/Mutex.h" #include +#include "../ServerConfig.h" + MysqlTable::MysqlTable(size_t fieldCount) : mFieldCount(fieldCount), mHeader(nullptr) { @@ -114,7 +116,7 @@ time_t MysqlTable::parseFromMysqlDateTime(const char* mysql_date_time) struct tm * parsedTime; // used because localtime return an internal pointer, not thread safe - static Poco::Mutex timeMutex; + Poco::Mutex& timeMutex = ServerConfig::g_TimeMutex; int year, month, day, hour, minute, second; // ex: 2009-10-29 diff --git a/src/cpp/ServerConfig.cpp b/src/cpp/ServerConfig.cpp index f356a4191..770067cf2 100644 --- a/src/cpp/ServerConfig.cpp +++ b/src/cpp/ServerConfig.cpp @@ -39,6 +39,7 @@ namespace ServerConfig { int g_SessionTimeout = SESSION_TIMEOUT_DEFAULT; std::string g_serverPath; std::string g_php_serverPath; + Poco::Mutex g_TimeMutex; bool loadMnemonicWordLists() { diff --git a/src/cpp/ServerConfig.h b/src/cpp/ServerConfig.h index ac38bbe6b..bd29d95a2 100644 --- a/src/cpp/ServerConfig.h +++ b/src/cpp/ServerConfig.h @@ -38,6 +38,7 @@ namespace ServerConfig { extern int g_SessionTimeout; extern std::string g_serverPath; extern std::string g_php_serverPath; + extern Poco::Mutex g_TimeMutex; bool loadMnemonicWordLists(); diff --git a/src/cpp/model/ElopageBuy.cpp b/src/cpp/model/ElopageBuy.cpp new file mode 100644 index 000000000..e76004401 --- /dev/null +++ b/src/cpp/model/ElopageBuy.cpp @@ -0,0 +1,76 @@ +#include "ElopageBuy.h" + +using namespace Poco::Data::Keywords; + +const static std::string g_requestFieldsNames[] = { + "product[affiliate_program_id]", "publisher[id]", "order_id", "product_id", + "product[price]", "payer[email]", "payment_state", "success_date", "event" }; + +ElopageBuy::ElopageBuy(const Poco::Net::NameValueCollection& elopage_webhook_requestData) + : mPayed(false) +{ + memset(mIDs, 0, ELOPAGE_BUY_MAX * sizeof(Poco::Int32)); + for (int i = 0; i < 5; i++) { + std::string temp = elopage_webhook_requestData.get(g_requestFieldsNames[i], "0"); + //printf("get: %s for field: %s (%d)\n", temp.data(), g_requestFieldsNames[i].data(), i); + try { + if (i == 4) { + mIDs[i+1] = static_cast(round(stof(temp) * 100.0f)); + } + else { + mIDs[i+1] = stoul(temp); + } + } + catch (const std::invalid_argument& ia) {addError(new ParamError("ElopageBuy", "parse string to number, invalid argument", ia.what()));} + catch (const std::out_of_range& oor) { addError(new ParamError("ElopageBuy", "parse string to number, Out of Range error", oor.what()));} + catch (const std::logic_error & ler) { addError(new ParamError("ElopageBuy", "parse string to number, Logical error", ler.what()));} + catch (...) {addError(new Error("ElopageBuy", "parse string to number, unknown error"));} + } + mPayerEmail = elopage_webhook_requestData.get(g_requestFieldsNames[5], ""); + std::string payed = elopage_webhook_requestData.get(g_requestFieldsNames[6], ""); + // payment_state = paid + if (payed == "paid") mPayed = true; + + mSuccessDate = parseElopageDate(elopage_webhook_requestData.get(g_requestFieldsNames[7], "")); + mEvent = elopage_webhook_requestData.get(g_requestFieldsNames[8], ""); +} + +ElopageBuy::~ElopageBuy() +{ + +} + +/* +ELOPAGE_BUY_AFFILIATE_PROGRAM_ID, +ELOPAGE_BUY_PUBLISHER_ID, +ELOPAGE_BUY_ORDER_ID, +ELOPAGE_BUY_PRODUCT_ID, +ELOPAGE_BUY_PRODUCT_PRICE +*/ + +Poco::Data::Statement ElopageBuy::insertIntoDB(Poco::Data::Session session) +{ + Poco::Data::Statement insert(session); + + lock(); + insert << "INSERT INTO " << getTableName() + << " (affiliate_program_id, publisher_id, order_id, product_id, product_price, payer_email, payed, success_date, event) " + << " VALUES(?,?,?,?,?,?,?,?,?)" + , bind(mIDs[ELOPAGE_BUY_AFFILIATE_PROGRAM_ID]), bind(mIDs[ELOPAGE_BUY_PUBLISHER_ID]) + , bind(mIDs[ELOPAGE_BUY_ORDER_ID]), bind(mIDs[ELOPAGE_BUY_PRODUCT_ID]), bind(mIDs[ELOPAGE_BUY_PRODUCT_PRICE]) + , bind(mPayerEmail), bind(mPayed), bind(mSuccessDate), bind(mEvent); + unlock(); + return insert; + +} +Poco::Data::Statement ElopageBuy::updateIntoDB(Poco::Data::Session session) +{ + throw Poco::Exception("ElopageBuy::updateIntoDB not implemented"); +} +Poco::Data::Statement ElopageBuy::loadFromDB(Poco::Data::Session session, std::string& fieldName) +{ +// Poco::Data::Statement select(session); + + throw Poco::Exception("ElopageBuy::loadFromDB not implemented"); + +} \ No newline at end of file diff --git a/src/cpp/model/ElopageBuy.h b/src/cpp/model/ElopageBuy.h new file mode 100644 index 000000000..c24dcdf1a --- /dev/null +++ b/src/cpp/model/ElopageBuy.h @@ -0,0 +1,51 @@ +#ifndef GRADIDO_LOGIN_SERVER_MODEL_ELOPAGE_BUY_INCLUDE +#define GRADIDO_LOGIN_SERVER_MODEL_ELOPAGE_BUY_INCLUDE + +/*! + * @author: Dario Rekowski + * + * @date: 31.10.2019 + * + * @brief: Model for handling Elopage publisher + * + */ + +#include "ModelBase.h" +#include "Poco/Types.h" +#include "Poco/Net/NameValueCollection.h" + + +enum ElopageBuyId { + ELOPAGE_BUY_ID, + ELOPAGE_BUY_AFFILIATE_PROGRAM_ID, + ELOPAGE_BUY_PUBLISHER_ID, + ELOPAGE_BUY_ORDER_ID, + ELOPAGE_BUY_PRODUCT_ID, + ELOPAGE_BUY_PRODUCT_PRICE, + + ELOPAGE_BUY_MAX +}; + +class ElopageBuy : public ModelBase +{ +public: + ElopageBuy(const Poco::Net::NameValueCollection& elopage_webhook_requestData); + ~ElopageBuy(); + + // generic db operations + const char* getTableName() { return "elopage_buys"; } + Poco::Data::Statement insertIntoDB(Poco::Data::Session session); + Poco::Data::Statement updateIntoDB(Poco::Data::Session session); + Poco::Data::Statement loadFromDB(Poco::Data::Session session, std::string& fieldName); + + + +protected: + Poco::Int32 mIDs[ELOPAGE_BUY_MAX]; + std::string mPayerEmail; + bool mPayed; + Poco::DateTime mSuccessDate; + std::string mEvent; +}; + +#endif //GRADIDO_LOGIN_SERVER_MODEL_ELOPAGE_BUY_INCLUDE \ No newline at end of file diff --git a/src/cpp/model/ModelBase.cpp b/src/cpp/model/ModelBase.cpp index c5882c7d2..925f696e0 100644 --- a/src/cpp/model/ModelBase.cpp +++ b/src/cpp/model/ModelBase.cpp @@ -1,9 +1,12 @@ #include "ModelBase.h" +#include "sodium.h" #include "../ServerConfig.h" #include "../SingletonManager/ConnectionManager.h" +#include "Poco/URI.h" + ModelInsertTask::ModelInsertTask(Poco::AutoPtr model) : UniLib::controller::CPUTask(ServerConfig::g_CPUScheduler), mModel(model) { @@ -50,4 +53,41 @@ void ModelBase::release() } unlock(); +} + +Poco::DateTime ModelBase::parseElopageDate(std::string dateString) +{ + std::string decodedDateString = ""; + Poco::URI::decode(dateString, decodedDateString); + + struct tm * parsedTime; + // used because localtime return an internal pointer, not thread safe + Poco::Mutex& timeMutex = ServerConfig::g_TimeMutex; + + int year, month, day, hour, minute, second; + // ex: 2009-10-29 + if (sscanf(decodedDateString.data(), "%d-%d-%dT%d:%dZ", &year, &month, &day, &hour, &minute) != EOF) { + time_t rawTime; + time(&rawTime); + + // static, used for every thread + timeMutex.lock(); + parsedTime = localtime(&rawTime); + + // tm_year is years since 1900 + parsedTime->tm_year = year - 1900; + // tm_months is months since january + parsedTime->tm_mon = month - 1; + parsedTime->tm_mday = day; + parsedTime->tm_hour = hour; + parsedTime->tm_min = minute; + parsedTime->tm_sec = 0; + + rawTime = mktime(parsedTime); + timeMutex.unlock(); + // rawTime is in seconds, poco timestamp in microseconds + return Poco::DateTime(Poco::Timestamp(rawTime* 1000000)); + } + + return Poco::DateTime(Poco::Timestamp()); } \ No newline at end of file diff --git a/src/cpp/model/ModelBase.h b/src/cpp/model/ModelBase.h index 7232d3cb2..a51edaf71 100644 --- a/src/cpp/model/ModelBase.h +++ b/src/cpp/model/ModelBase.h @@ -25,6 +25,8 @@ public: inline void setID(int id) { lock(); mID = id; unlock(); } inline int getID() { lock(); int id = mID; unlock(); return id; } + static Poco::DateTime parseElopageDate(std::string dateString); + // for poco auto ptr void duplicate(); void release(); diff --git a/src/cpsp/header.cpsp b/src/cpsp/header.cpsp index 591e8529c..baa816ce0 100644 --- a/src/cpsp/header.cpsp +++ b/src/cpsp/header.cpsp @@ -50,7 +50,7 @@ label:not(.grd_radio_label) {

Login Server in Entwicklung

-

Alpha 0.4.3

+

Alpha 0.4.4