#include "ElopageWebhook.h" #include "Poco/Net/HTTPServerRequest.h" #include "Poco/Net/HTTPServerResponse.h" #include "Poco/URI.h" #include "Poco/Logger.h" #include "Poco/Data/Binding.h" //#include "Poco/Data/MySQL/MySQLException.h" using namespace Poco::Data::Keywords; #include "../SingletonManager/ConnectionManager.h" #include "../SingletonManager/ErrorManager.h" #include "../SingletonManager/SessionManager.h" #include "../SingletonManager/EmailManager.h" #include "../ServerConfig.h" #include "../controller/EmailVerificationCode.h" #include "../model/table/ElopageBuy.h" #include "../lib/DataTypeConverter.h" void ElopageWebhook::handleRequest(Poco::Net::HTTPServerRequest& request, Poco::Net::HTTPServerResponse& response) { // simply write request to file for later lookup //ServerConfig::writeToFile(request.stream(), "elopage_webhook_requests.txt"); // empty response, we didn't need to set anything //response.setStatus(Poco::Net::HTTPResponse::HTTP_NO_CONTENT); std::ostream& _responseStream = response.send(); _responseStream << "200 OK"; // don't use it anymore, register now direct // elopage request are only logged from gpt server //return; std::istream& stream = request.stream(); std::string completeRequest; Poco::Net::NameValueCollection elopageRequestData; int breakCount = 100; while (stream.good() && breakCount > 0) { // char dummy; //char keyBuffer[30]; memset(keyBuffer, 0, 30); //char valueBuffer[75]; memset(valueBuffer, 0, 75); std::vector keyBuffer(30); std::vector valueBuffer(75); /*stream.get(keyBuffer, 30, '=').get(dummy) .get(valueBuffer, 35, '&').get(dummy);*/ std::string line; std::getline(stream, line); int mode = 0; for (int i = 0; i < line.size(); i++) { char c = line.at(i); completeRequest += c; if (c == '\n') break; if (c == '+') { c = ' '; } if (c == '&') { mode = 0; valueBuffer.push_back('\0'); keyBuffer.push_back('\0'); std::string urlDecodedValue; try { Poco::URI::decode(valueBuffer.data(), urlDecodedValue); } catch (Poco::Exception& ex) { Poco::Logger& logging(Poco::Logger::get("errorLog")); logging.error("[ElopageWebhook::handleRequest] error decoding URI: %s, exception: %s", std::string(valueBuffer.data()), ex.displayText()); urlDecodedValue = valueBuffer.data(); } elopageRequestData.set(keyBuffer.data(), urlDecodedValue); valueBuffer.clear(); keyBuffer.clear(); continue; } switch (mode) { case 0: // read key if (c == '=') { mode = 1; continue; } keyBuffer.push_back(c); break; case 1: // read value valueBuffer.push_back(c); break; } } valueBuffer.push_back('\0'); keyBuffer.push_back('\0'); // last key-value pair std::string urlDecodedValue; try { Poco::URI::decode(valueBuffer.data(), urlDecodedValue); } catch (Poco::Exception& ex) { Poco::Logger& logging(Poco::Logger::get("errorLog")); logging.error("[ElopageWebhook::handleRequest] error decoding URI (last): %s, exception: %s", std::string(valueBuffer.data()), ex.displayText()); urlDecodedValue = valueBuffer.data(); } if (strcmp(keyBuffer.data(), "")) { elopageRequestData.set(keyBuffer.data(), urlDecodedValue); } //printf("[ElopageWebhook::handleRequest] key: %s, value: %s\n", keyBuffer, valueBuffer); /// elopageRequestData.set(keyBuffer, valueBuffer); stream.good(); breakCount--; } // check event type std::string event = elopageRequestData.get("event", ""); if (event == "lesson.viewed") { printf("elopage request was lesson viewed\n"); return; } // write stream result also to file static Poco::Mutex mutex; Profiler timeUsed; mutex.lock(); Poco::FileOutputStream file("elopage_webhook_requests.txt", std::ios::out | std::ios::app); if (!file.good()) { Poco::Logger& logging(Poco::Logger::get("errorLog")); logging.error("[ElopageWebhook::handleRequest] error creating file with name: elopage_webhook_requests.txt"); //printf("[ElopageWebhook::handleRequest] error creating file with name: elopage_webhook_requests.txt\n"); mutex.unlock(); return; } Poco::LocalDateTime now; std::string dateTimeStr = Poco::DateTimeFormatter::format(now, Poco::DateTimeFormat::ISO8601_FORMAT); file << dateTimeStr << std::endl; file << completeRequest << std::endl; file << std::endl; file.close(); std::string timeUsedStr = timeUsed.string(); printf("[%s] time for elopage request write to file: %s\n", dateTimeStr.data(), timeUsedStr.data()); mutex.unlock(); UniLib::controller::TaskPtr handleElopageTask(new HandleElopageRequestTask(elopageRequestData)); handleElopageTask->scheduleTask(handleElopageTask); } // **************************************************************************** HandleElopageRequestTask::HandleElopageRequestTask(Poco::Net::NameValueCollection& requestData) : CPUTask(ServerConfig::g_CPUScheduler), mRequestData(requestData) { #ifdef _UNI_LIB_DEBUG setName(mRequestData.get("order_id", "").data()); #endif } bool HandleElopageRequestTask::validateInput() { auto sm = SessionManager::getInstance(); if (mEmail == "" || !sm->isValid(mEmail, VALIDATE_EMAIL)) { addError(new Error(__FUNCTION__, "email is invalid or empty")); return false; } if (mFirstName == "" || !sm->isValid(mFirstName, VALIDATE_NAME)) { addError(new Error(__FUNCTION__, "first name is invalid or empty")); return false; } if (mLastName == "" || !sm->isValid(mLastName, VALIDATE_NAME)) { addError(new Error(__FUNCTION__, "last name is invalid or empty")); return false; } return true; } void HandleElopageRequestTask::writeUserIntoDB() { printf("HandleElopageRequestTask::writeUserIntoDB\n"); auto cm = ConnectionManager::getInstance(); auto session = cm->getConnection(CONNECTION_MYSQL_LOGIN_SERVER); Poco::Data::Statement insert(session); insert << "INSERT INTO users (email, first_name, last_name) VALUES(?, ?, ?);", use(mEmail), use(mFirstName), use(mLastName); try { insert.execute(); //printf("user written into db\n"); } catch (Poco::Exception& ex) { addError(new ParamError(__FUNCTION__, "mysql error", ex.displayText().data())); } } int HandleElopageRequestTask::getUserIdFromDB(bool checkEmail /* = false*/) { auto cm = ConnectionManager::getInstance(); auto session = cm->getConnection(CONNECTION_MYSQL_LOGIN_SERVER); Poco::Data::Statement select(session); std::vector user_ids; std::vector email_checked; if (checkEmail) { select << "SELECT id, email_checked from users where email = ?;", into(user_ids), into(email_checked), use(mEmail); } else { select << "SELECT id from users where email = ?;", into(user_ids), use(mEmail); } try { select.execute(); } catch (Poco::Data::ConnectionFailedException& ex) { addError(new ParamError(__FUNCTION__, "[ConnectionFailedException] mysql error selecting from db", ex.displayText().data())); addError(new ParamError(__FUNCTION__, "email: ", mEmail.data())); } catch (Poco::Data::NotConnectedException& ex) { addError(new ParamError(__FUNCTION__, "[NotConnectedException] mysql error selecting from db", ex.displayText().data())); addError(new ParamError(__FUNCTION__, "email: ", mEmail.data())); } catch (Poco::Exception& ex) { addError(new ParamError(__FUNCTION__, "mysql error selecting from db", ex.displayText().data())); addError(new ParamError(__FUNCTION__, "email: ", mEmail.data())); } if (user_ids.size() > 1) { std::string duplicateIds("duplicate user ids for email: "); duplicateIds += mEmail; duplicateIds += ": "; for (int i = 0; i < user_ids.size(); i++) { if (i > 0) { duplicateIds += ", "; } duplicateIds += std::to_string(user_ids[i]); } addError(new Error("HandleElopageRequestTask::getUserIdFromDB", duplicateIds.data())); sendErrorsAsEmail(); } if (user_ids.size() >= 1) { if (email_checked.size() >= 1 && email_checked[0]) { addError(new Error("HandleElopageRequestTask::getUserIdFromDB", "user account already activated")); return 0; } return user_ids[0]; } return 0; } int HandleElopageRequestTask::run() { // get input data // check event type std::string event = mRequestData.get("event", ""); if (event == "lesson.viewed" || event == "lesson.completed" || event == "lesson.commented") { return 0; } // elopage buy Poco::AutoPtr elopageBuy(new model::table::ElopageBuy(mRequestData)); if (elopageBuy->errorCount() > 0) { getErrors(elopageBuy); } UniLib::controller::TaskPtr saveElopageBuy(new model::table::ModelInsertTask(elopageBuy, false)); saveElopageBuy->scheduleTask(saveElopageBuy); // check product id unsigned long long product_id = 0; DataTypeConverter::strToInt(mRequestData.get("product[id]", "0"), product_id); std::string order_id = mRequestData.get("order_id", ""); auto param_error_order_id = new ParamError("HandleElopageRequestTask", "order_id", order_id.data()); /*! * Registrierung – Schritt 1 von 3, 36001 Gradido-Basis, 43741 Premium-Mitgliedschaft, 43870 Gold-Mitgliedschaft, 43944 Business-Mitgliedschaft, 43960 Förderbeitrag: 49106 * */ static const int valid_product_ids[] = { 36001, 43741, 43870, 43944, 43960, 49106 }; bool valid_product_id = false; for (int i = 0; i < sizeof(valid_product_ids) / sizeof(int); i++) { if (valid_product_ids[i] == product_id) { valid_product_id = true; break; } } // only for product 36001 and 43741 create user accounts and send emails if (valid_product_id) { mEmail = mRequestData.get("payer[email]", ""); mFirstName = mRequestData.get("payer[first_name]", ""); mLastName = mRequestData.get("payer[last_name]", ""); auto newUser = controller::User::create(mEmail, mFirstName, mLastName); /* printf("LastName: %s\n", mLastName.data()); for (int i = 0; i < mLastName.size(); i++) { char c = mLastName.data()[i]; printf("%d ", c); } printf("\n\n"); */ // validate input if (!validateInput()) { // if input is invalid we can stop now addError(param_error_order_id); sendErrorsAsEmail(); return -1; } // if user exist we can stop now if (getUserIdFromDB()) { //addError(param_error_order_id); //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(true); // we didn't get a user_id, something went wrong // maybe user already exist if (!user_id) { addError(new Error("User loadEntryDBId", "user_id is zero")); addError(param_error_order_id); sendErrorsAsEmail(); return -3; } // email verification code auto emailVerification = controller::EmailVerificationCode::create(user_id, model::table::EMAIL_OPT_IN_REGISTER_DIRECT); //Poco::AutoPtr emailVerification(new model::table::EmailOptIn(user_id)); // create email verification code if (!emailVerification->getModel()->getCode()) { // exit if email verification code is empty addError(new Error("Email verification", "code is empty, error in random?")); addError(param_error_order_id); sendErrorsAsEmail(); return -4; } auto em = EmailManager::getInstance(); if (emailVerification->getModel()->insertIntoDB(false)) { int noEMail = 0; DataTypeConverter::strToInt(mRequestData.get("noEmail", "0"), noEMail); if (noEMail != 1) { em->addEmail(new model::Email(emailVerification, newUser, model::EMAIL_USER_VERIFICATION_CODE)); } } } // if errors occured, send via email if (errorCount() > 1) { addError(param_error_order_id); sendErrorsAsEmail(); } else { delete param_error_order_id; } return 0; }