#include #include #include #include #include "crow.h" #include "crow/middlewares/cookie_parser.h" #include "database.cpp" #include "utilities.cpp" #include "regularTaskExecution.cpp" #include "templateConstCollection.cpp" #include "smtpManager.cpp" using namespace std; using namespace TemplateConstCollection; int main(int argc, char *argv[]) { Utilities::config configuration; if (argc > 1) configuration.configPath = argv[1]; if (configuration.readConfigFile()) { cout << "ERROR: Unable to read configuration file: " << configuration.configPath << endl; cout << "Aborting Startup!" << endl; return 1; } //simple email test, header validation via https://mailheader.org/ //SMTPManager::sendEmail(configuration, "mail@mail.de", "testsubject", "

bluetest


blue
yellow"); //return 0; // Create app with Middleware crow::App app; // starts thread that executes modules thread threadRegularExecution(RegularTaskExecution::regularExecution, configuration); /* * Freelancer Profile listing for customers */ CROW_ROUTE(app, "/").methods("POST"_method, "GET"_method) ([&, configuration](const crow::request& request) { int selectedPage = 1; if (!request.url_params.keys().empty() && request.url_params.get("page") != nullptr) { string selectedPageString = request.url_params.get("page"); if (!selectedPageString.empty()) selectedPage = stoi(selectedPageString); } auto page = crow::mustache::load(TEMPLATE_CUSTOMER_INDEX_FREELANCER_LISTING); crow::mustache::context ctx(Utilities::getFreelancerListing(configuration, selectedPage)); auto& cookieCtx = app.get_context(request); if (Utilities::checkCookieLoginState(configuration, cookieCtx)) ctx[MUSTACHE_COOKIE_LOGGED_IN] = true; if (configuration.itemsPerPage > 0) { ctx[MUSTACHE_PAGINATION] = true; vector pages = Utilities::getFreelancerIndexPagination(configuration); ctx[MUSTACHE_PAGINATION_NUMBERS] = pages; if (selectedPage <= 1) ctx[MUSTACHE_PAGINATION_PREVIOUS] = 1; else ctx[MUSTACHE_PAGINATION_PREVIOUS] = selectedPage - 1; if (selectedPage >= pages.size()) ctx[MUSTACHE_PAGINATION_NEXT] = pages.size(); else ctx[MUSTACHE_PAGINATION_NEXT] = selectedPage + 1; } return page.render(ctx); }); /* * Freelancer Profile listing for customers */ CROW_ROUTE(app, "/@") ([configuration](string alias) { crow::mustache::context ctx(Utilities::getAlias(configuration, alias)); auto page = crow::mustache::load(TEMPLATE_ALIAS_REDIRECT); return page.render(ctx); }); /* * Freelancer Profile Page for customers */ CROW_ROUTE(app, "/customer/").methods("POST"_method) ([configuration](const crow::request& postRequest, string freelancerName ) { //Parses the request body and extracts the specified value int freelancerID = stoi(Utilities::extractSingleValueFromRequestBody(postRequest.body.c_str(), "freelancerID")); pqxx::connection databaseConnection(configuration.databaseConnectionString); Database::prepareStatements(databaseConnection, { ID_SELECT_FREELANCER, ID_SELECT_FREELANCER_TEMPLATES }); pqxx::result resultFreelancer = Database::executePreparedStatement_SELECT_FREELANCER(databaseConnection, freelancerID); //error handling if invalid ID was delivered string freelancerHTML = TEMPLATE_CUSTOMER_FREELANCER_LISTING_NOT_FOUND; crow::json::wvalue resultJsonFreelancerTemplate; if (!resultFreelancer.empty()){ freelancerHTML = TEMPLATE_CUSTOMER_FREELANCER_LISTING; pqxx::result resultFreelancerTemplates = Database::executePreparedStatement_SELECT_FREELANCER_TEMPLATES(databaseConnection, freelancerID); resultJsonFreelancerTemplate = Database::convertResultToJSON(resultFreelancerTemplates, "templates"); } auto page = crow::mustache::load(freelancerHTML); crow::mustache::context ctx(resultJsonFreelancerTemplate); if (!resultFreelancer.empty()){ /* * puts freelancer data into context */ for (int i = 0; i < resultFreelancer.columns(); ++i) { string freelancerColumn = resultFreelancer.column_name(i); ctx["freelancer" + freelancerColumn] = resultFreelancer.at(0).at(i).c_str(); } } ctx["freelancerid"] = freelancerID; ctx["selectedfreelancername"] = freelancerName; return page.render(ctx); }); /* * Page representing a freelancers Template */ CROW_ROUTE(app, "/customer//template/").methods("POST"_method) ([configuration](const crow::request& postRequest, string freelancerName, string templateName ) { int templateID = stoi(Utilities::extractSingleValueFromRequestBody(postRequest.body.c_str(), "templateID")); pqxx::connection databaseConnection(configuration.databaseConnectionString); Database::prepareStatements(databaseConnection, { ID_SELECT_TEMPLATE, ID_SELECT_FREELANCER_COMMISSION_STATE }); pqxx::result resultTemplate = Database::executePreparedStatement_SELECT_TEMPLATE(databaseConnection, templateID); crow::json::wvalue resultJsonTemplate = Database::convertResultRowToJSON(resultTemplate); auto page = crow::mustache::load(TEMPLATE_CUSTOMER_FREELANCER_TEMPLATE); crow::mustache::context ctx(resultJsonTemplate); bool commissionState = false; if (!resultTemplate.empty()) { //use freelancerID based on SQL_STATEMENT_SELECT_TEMPLATE pqxx::result resultCommissionState = Database::executePreparedStatement_SELECT_FREELANCER_COMMISSION_STATE(databaseConnection, stoi(resultTemplate.at(0).at(2).c_str())); //the commission-state if (resultCommissionState.empty() || stoi(resultCommissionState.at(0).at(0).c_str()) == 1) commissionState = true; } ctx[MUSTACHE_ERROR_COMMISSIONS_CLOSED] = commissionState; return page.render(ctx); }); /* * Page for entry of request data */ CROW_ROUTE(app, "/customer//template//request").methods("POST"_method) ([configuration](const crow::request& postRequest, string freelancerName, string templateName ) { int templateID = stoi(Utilities::extractSingleValueFromRequestBody(postRequest.body.c_str(), "templateID")); pqxx::connection databaseConnection(configuration.databaseConnectionString); Database::prepareStatement(databaseConnection, ID_SELECT_TEMPLATE); pqxx::result resultTemplate = Database::executePreparedStatement_SELECT_TEMPLATE(databaseConnection, templateID); crow::json::wvalue resultJsonTemplate = Database::convertResultRowToJSON(resultTemplate); auto page = crow::mustache::load(TEMPLATE_CUSTOMER_FREELANCER_TEMPLATE_REQUEST); crow::mustache::context ctx(resultJsonTemplate); return page.render(ctx); }); /* * executes the creation of a new request. */ CROW_ROUTE(app, "/customer//template//request/fulfilment").methods("POST"_method) ([configuration](const crow::request& postRequest, string freelancerName, string templateName ) { pqxx::connection databaseConnection(configuration.databaseConnectionString); string templateHTML = TEMPLATE_CUSTOMER_FREELANCER_TEMPLATE_REQUEST_FULFILMENT_ERROR; Database::requestsItem newRequest; crow::mustache::context ctx; ctx["freelancername"] = freelancerName; ctx["templatename"] = templateName; string postRequestBody = postRequest.body.c_str(); Utilities::decodeString(postRequestBody); vector splitPostRequestBody = Utilities::splitStringIntoVector(postRequestBody, '&'); bool requestFillCompletion = false; for (const string& item : splitPostRequestBody) { vector splitItem = Utilities::splitStringIntoVector(item, '='); if (splitItem.at(0) == "customername") newRequest.customerName = splitItem.at(1); if (splitItem.at(0) == "email") newRequest.customerEmailAddress = splitItem.at(1); if (splitItem.at(0) == "customercontactdetails") newRequest.customerContactDetails = splitItem.at(1); if (splitItem.at(0) == "requestdescription") newRequest.requestDescription = splitItem.at(1); if (splitItem.at(0) == "templateID") newRequest.templateID = stoi(splitItem.at(1)); } if (newRequest.customerName != "" && (newRequest.customerEmailAddress != "" || newRequest.customerContactDetails != "") && newRequest.requestDescription != "" ) requestFillCompletion = true; ctx["templateid"] = newRequest.templateID; Database::prepareStatements(databaseConnection, { ID_SELECT_TEMPLATE_FLAT, ID_SELECT_FREELANCER_COMMISSION_STATE, ID_INSERT_ITEM_IN_REQUESTS, ID_SELECT_FREELANCER_EMAIL }); pqxx::result resultTemplate = Database::executePreparedStatement_SELECT_TEMPLATE_FLAT(databaseConnection, newRequest.templateID); if (!resultTemplate.empty() && requestFillCompletion) { for (int i = 0; i < resultTemplate.columns(); ++i) { //freelancerid newRequest.freelancerID = stoi(resultTemplate.at(0).at(0).c_str()); //currencypreference newRequest.currencyPreference = resultTemplate.at(0).at(1).c_str(); //priceupfront newRequest.priceUpFront = resultTemplate.at(0).at(2).c_str(); //priceondeliver newRequest.priceOnDeliver = resultTemplate.at(0).at(3).c_str(); } pqxx::result resultCommissionState = Database::executePreparedStatement_SELECT_FREELANCER_COMMISSION_STATE(databaseConnection, newRequest.freelancerID); //the commission-state if (resultCommissionState.empty() || stoi(resultCommissionState.at(0).at(0).c_str()) == 1) { ctx[MUSTACHE_ERROR_COMMISSIONS_CLOSED] = true; } else { int resultInsertOperation = Database::executePreparedStatement_INSERT_ITEM_IN_REQUESTS(databaseConnection, newRequest); if (resultInsertOperation == 0) { templateHTML = TEMPLATE_CUSTOMER_FREELANCER_TEMPLATE_REQUEST_FULFILMENT; pqxx::result resultEmailAddress = Database::executePreparedStatement_SELECT_FREELANCER_EMAIL(databaseConnection, newRequest.freelancerID); if (!resultEmailAddress.empty()) SMTPManager::sendEmail(configuration, resultEmailAddress.at(0).at(0).c_str(), "NEW REQUEST", newRequest.toJSONString()); } else { ctx[MUSTACHE_ERROR_UNABLE_TO_CREATE_REQUEST] = true; } } } else { if (requestFillCompletion) ctx[MUSTACHE_ERROR_TEMPLATE_NOT_FOUND] = true; else ctx[MUSTACHE_REQUEST_NOT_FILLED] = true; } auto page = crow::mustache::load(templateHTML); return page.render(ctx); }); /* * redirect to /freelancer/login */ CROW_ROUTE(app, "/login") ([](crow::response& res) { res.redirect("/freelancer/login"); res.end(); }); /* * redirect to /freelancer/logout */ CROW_ROUTE(app, "/logout") ([](crow::response& res) { res.redirect("/freelancer/logout"); res.end(); }); /* * Logs out a freelancer by replacing validation key and expiring cookies */ CROW_ROUTE(app, "/freelancer/logout") ([&, configuration](const crow::request& getRequest, crow::response& res) { auto& ctx = app.get_context(getRequest); if (Utilities::checkCookieLoginState(configuration, ctx)) { std::string freelancerEmail = ctx.get_cookie("freelancerEmail"); pqxx::connection databaseConnection(configuration.databaseConnectionString); Database::prepareStatement(databaseConnection, ID_UPDATE_LOGIN_VALIDATION_KEY); Database::executePreparedStatement_UPDATE_LOGIN_VALIDATION_KEY(databaseConnection, "EXPIRED", freelancerEmail); ctx.set_cookie(COOKIE_LOGIN_KEY, Utilities::generateExpiredCookie()); ctx.set_cookie(COOKIE_FREELANCER_EMAIL, Utilities::generateExpiredCookie()); } res.redirect("/"); res.end(); }); /* * Page for freelancer to log in */ CROW_ROUTE(app, "/freelancer/login") ([&,configuration](const crow::request& getRequest ) { auto& ctx = app.get_context(getRequest); string templateHTML = TEMPLATE_FREELANCER_LOGIN; if (Utilities::checkCookieLoginState(configuration, ctx)) templateHTML = TEMPLATE_FREELANCER_REDIRECT_PROFILE; auto page = crow::mustache::load(templateHTML); return page.render(); }); /* * Page for freelancer to request a password reset email */ CROW_ROUTE(app, "/freelancer/login/passwordreset") ([]() { auto page = crow::mustache::load(TEMPLATE_FREELANCER_LOGIN_PASSWORD_RESET); return page.render(); }); /* * Page for freelancer password reset fulfilment */ CROW_ROUTE(app, "/freelancer/login/passwordreset/fulfilment") ([&, configuration](const crow::request& postRequest ) { crow::mustache::context ctx; string postRequestBody = postRequest.body; Utilities::decodeString(postRequestBody); vector splitPostRequestBody = Utilities::splitStringIntoVector(postRequestBody, '&'); string email; for (const string& item : splitPostRequestBody) { vector splitItem = Utilities::splitStringIntoVector(item, '='); if (splitItem.at(0) == "freelanceremail") email = splitItem.at(1); } if (!email.empty()) { pqxx::connection databaseConnection(configuration.databaseConnectionString); Database::prepareStatements(databaseConnection, { ID_SELECT_CHECK_EMAIL_EXISTS, ID_SELECT_CHECK_FREELANCER_RESET_KEY, ID_DELETE_FREELANCER_RESET_KEY, ID_INSERT_FREELANCER_RESET_KEY }); pqxx::result checkFreelancerExists = Database::executePreparedStatement_SELECT_CHECK_EMAIL_EXISTS(databaseConnection, email); int checkFreelancerExistsExtracted = stoi(checkFreelancerExists.at(0).at(0).c_str()); if (checkFreelancerExistsExtracted == 1) { std::string passwordResetKey = Utilities::generateRandomHashValueSHA256(); pqxx::result checkFreelancerResetKeyExists = Database::executePreparedStatement_SELECT_CHECK_FREELANCER_RESET_KEY(databaseConnection, email); int checkFreelancerResetKeyExistsExtracted = stoi(checkFreelancerResetKeyExists.at(0).at(0).c_str()); if (checkFreelancerResetKeyExistsExtracted > 0) Database::executePreparedStatement_DELETE_FREELANCER_RESET_KEY(databaseConnection, email); Database::executePreparedStatement_INSERT_FREELANCER_RESET_KEY(databaseConnection, email, passwordResetKey); if (SMTPManager::sendPasswordResetEmail(configuration, email, passwordResetKey) == 1) ctx["passwordresetmailsent"] = true; } ctx["resetemail"] = email; } auto page = crow::mustache::load(TEMPLATE_FREELANCER_LOGIN_PASSWORD_RESET_FULFILMENT); return page.render(ctx); }); /* * Page for freelancer to reset a password from a requested PasswordReset */ CROW_ROUTE(app, "/passwordreset/") ([&, configuration](string passwordResetKey) { crow::mustache::context ctx; pqxx::connection databaseConnection(configuration.databaseConnectionString); Database::prepareStatement(databaseConnection, ID_SELECT_FREELANCER_EMAIL_FROM_PASSWORD_RESET_KEY); pqxx::result freelancerEmail = Database::executePreparedStatement_SELECT_FREELANCER_EMAIL_FROM_PASSWORD_RESET_KEY(databaseConnection, passwordResetKey); if (!freelancerEmail.empty()) { ctx["freelanceremail"] = freelancerEmail.at(0).at(0).c_str(); ctx["passwordresetkey"] = passwordResetKey; } else { ctx[MUSTACHE_PASSWORD_RESET_DOES_NOT_EXIST] = true; } auto page = crow::mustache::load(TEMPLATE_PASSWORD_RESET); return page.render(ctx); }); /* * Page for fulfillment of password reset */ CROW_ROUTE(app, "/passwordreset//fulfilment").methods("POST"_method) ([&, configuration](const crow::request& postRequest, string passwordResetKey) { crow::mustache::context ctx; string postRequestBody = postRequest.body; Utilities::decodeString(postRequestBody); vector splitPostRequestBody = Utilities::splitStringIntoVector(postRequestBody, '&'); string password, passwordConfirmation; for (const string& item : splitPostRequestBody) { vector splitItem = Utilities::splitStringIntoVector(item, '='); if (splitItem.at(0) == "freelancerpassword") password = splitItem.at(1); if (splitItem.at(0) == "freelancerpasswordconfirmation") passwordConfirmation = splitItem.at(1); } pqxx::connection databaseConnection(configuration.databaseConnectionString); Database::prepareStatements(databaseConnection, { ID_SELECT_FREELANCER_EMAIL_FROM_PASSWORD_RESET_KEY, ID_SELECT_CHECK_FREELANCER_RESET_KEY_EXPIRED, ID_DELETE_FREELANCER_RESET_KEY, ID_UPDATE_FREELANCER_PASSWORD_HASH }); pqxx::result freelancerEmail = Database::executePreparedStatement_SELECT_FREELANCER_EMAIL_FROM_PASSWORD_RESET_KEY(databaseConnection, passwordResetKey); if (!freelancerEmail.empty() && !password.empty() && password.compare(passwordConfirmation) != 0) { string email = freelancerEmail.at(0).at(0).c_str(); pqxx::result keyExpiration = Database::executePreparedStatement_SELECT_CHECK_FREELANCER_RESET_KEY_EXPIRED(databaseConnection, email); if (stoi(keyExpiration.at(0).at(0).c_str()) == 0) { Database::executePreparedStatement_DELETE_FREELANCER_RESET_KEY(databaseConnection, email); string pwsalt = Utilities::generateSalt(); string pwhash = Utilities::hashPassword(pwsalt, password); int errorLevel = Database::executePreparedStatement_UPDATE_FREELANCER_PASSWORD_HASH(databaseConnection, pwhash, pwsalt, email); if (errorLevel == 0) { ctx[MUSTACHE_RESET_SUCCESS] = true; ctx["freelanceremail"] = email; } else { ctx[MUSTACHE_RESET_ERROR] = true; if (errorLevel == 1) ctx[MUSTACHE_RESET_ERROR_QUERY] = true; if (errorLevel == 2) ctx[MUSTACHE_RESET_ERROR_CRITICAL] = true; } } else { ctx[MUSTACHE_PASSWORD_RESET_EXPIRED] = true; } } else { if (password.empty()) ctx[MUSTACHE_PASSWORD_EMPTY] = true; if (freelancerEmail.empty()) ctx[MUSTACHE_PASSWORD_RESET_DOES_NOT_EXIST] = true; if (password.compare(passwordConfirmation) != 0) ctx[MUSTACHE_PASSWORD_RESET_PASS_CONFIRMATION] = true; } auto page = crow::mustache::load(TEMPLATE_PASSWORD_RESET_FULFILMENT); return page.render(ctx); }); /* * Page for freelancer to log in fulfillment */ CROW_ROUTE(app, "/freelancer/login/fulfilment").methods("POST"_method) ([&, configuration](const crow::request& postRequest ) { crow::mustache::context ctx; string postRequestBody = postRequest.body; Utilities::decodeString(postRequestBody); vector splitPostRequestBody = Utilities::splitStringIntoVector(postRequestBody, '&'); string email, password; bool stayLoggedIn; for (const string& item : splitPostRequestBody) { vector splitItem = Utilities::splitStringIntoVector(item, '='); if (splitItem.at(0) == "freelanceremail") email = splitItem.at(1); if (splitItem.at(0) == "freelancerpassword") password = splitItem.at(1); if (splitItem.at(0) == "stayloggedin") stayLoggedIn = !splitItem.at(1).empty(); //if checkbox not set result is empty ie stay logged in is false, if it is set result is "on" ie not empty ie stay logged in is true } //check if login data is complete if (!email.empty() && !password.empty()){ //check if freelancer exists pqxx::connection databaseConnection(configuration.databaseConnectionString); Database::prepareStatements(databaseConnection, { ID_SELECT_CHECK_EMAIL_EXISTS, ID_SELECT_FREELANCER_SALT, ID_SELECT_CHECK_HASH_VALID, ID_UPDATE_LOGIN_VALIDATION_KEY, ID_SELECT_CHECK_LOGIN_LOCK_OUT, ID_SELECT_GET_LOGIN_LOCK_OUT_MINUTES }); pqxx::result checkloginLockedOut = Database::executePreparedStatement_SELECT_CHECK_LOGIN_LOCK_OUT(databaseConnection, email); string checkloginLockedOutExtracted = "f"; if (!checkloginLockedOut.empty()) checkloginLockedOutExtracted = checkloginLockedOut.at(0).at(0).c_str(); if (checkloginLockedOutExtracted != "t") { pqxx::result checkFreelancerExists = Database::executePreparedStatement_SELECT_CHECK_EMAIL_EXISTS(databaseConnection, email); int checkFreelancerExistsExtracted = stoi(checkFreelancerExists.at(0).at(0).c_str()); if (checkFreelancerExistsExtracted == 1) { pqxx::result freelancerSalt = Database::executePreparedStatement_SELECT_FREELANCER_SALT(databaseConnection, email); string salt = freelancerSalt.at(0).at(0).c_str(); string hash = Utilities::hashPassword(salt, password); pqxx::result checkFreelancerHash = Database::executePreparedStatement_SELECT_CHECK_HASH_VALID(databaseConnection, email, hash); int checkFreelancerHashExtracted = stoi(checkFreelancerHash.at(0).at(0).c_str()); if (checkFreelancerHashExtracted == 1) { //create secureCookie auto& cookieCtx = app.get_context(postRequest); std::string loginKeyValue = Utilities::generateRandomHashValueSHA512(); Database::executePreparedStatement_UPDATE_LOGIN_VALIDATION_KEY(databaseConnection, loginKeyValue, email); std::string loginKeyCookieValue = Utilities::generateSecureCookieLoginKeyValue(loginKeyValue, stayLoggedIn); std::string freelancerEmailCookieValue = Utilities::generateSecureCookieFreelancerEmailValue(email, stayLoggedIn); cookieCtx.set_cookie(COOKIE_LOGIN_KEY, loginKeyCookieValue); cookieCtx.set_cookie(COOKIE_FREELANCER_EMAIL,freelancerEmailCookieValue); ctx[MUSTACHE_LOGIN_SUCCESS] = true; ctx[MUSTACHE_COOKIE_LOGGED_IN] = true; } else { ctx[MUSTACHE_LOGIN_ERROR] = true; ctx[MUSTACHE_LOGIN_ERROR_LOGIN_DATA_INVALID] = true; Utilities::loginLockOutIncrement(configuration, databaseConnection, email); } } else { ctx[MUSTACHE_LOGIN_ERROR] = true; ctx[MUSTACHE_LOGIN_ERROR_LOGIN_DATA_INVALID] = true; } } else { pqxx::result loginLockOutInMinutes = Database::executePreparedStatement_SELECT_GET_LOGIN_LOCK_OUT_MINUTES(databaseConnection, email); ctx[MUSTACHE_LOGIN_ERROR] = true; ctx[MUSTACHE_LOGIN_ERROR_LOCKED_OUT] = true; ctx[MUSTACHE_LOGIN_ERROR_LOCKED_OUT_MINUTES] = loginLockOutInMinutes.at(0).at(0).c_str(); } } else { ctx[MUSTACHE_LOGIN_ERROR] = true; ctx[MUSTACHE_LOGIN_ERROR_EMAIL_PASS_NOT_FILLED] = true; } auto page = crow::mustache::load(TEMPLATE_FREELANCER_LOGIN_FULFILMENT); return page.render(ctx); }); /* * Page for freelancer to sign up */ CROW_ROUTE(app, "/freelancer/signup") ([&, configuration](const crow::request& getRequest ) { auto& ctx = app.get_context(getRequest); string templateHTML = TEMPLATE_FREELANCER_SIGN_UP; if (Utilities::checkCookieLoginState(configuration, ctx)) templateHTML = TEMPLATE_FREELANCER_REDIRECT_PROFILE; auto page = crow::mustache::load(templateHTML); return page.render(); }); /* * Page for freelancer to sign up fulfillment */ CROW_ROUTE(app, "/freelancer/signup/fulfilment").methods("POST"_method) ([configuration](const crow::request& postRequest ) { crow::mustache::context ctx; string postRequestBody = postRequest.body; Utilities::decodeString(postRequestBody); vector splitPostRequestBody = Utilities::splitStringIntoVector(postRequestBody, '&'); string name, email, password, passwordConfirmation; bool requestFillCompletion = false; for (const string& item : splitPostRequestBody) { vector splitItem = Utilities::splitStringIntoVector(item, '='); if (splitItem.at(0) == "freelancername") name = splitItem.at(1); if (splitItem.at(0) == "freelanceremail") email = splitItem.at(1); if (splitItem.at(0) == "freelancerpassword") password = splitItem.at(1); if (splitItem.at(0) == "freelancerpasswordconfirmation") passwordConfirmation = splitItem.at(1); } //check if signup data is complete if (!email.empty() && !name.empty() && !password.empty() && password.compare(passwordConfirmation) != 0) requestFillCompletion = true; if (requestFillCompletion) { //check if email address hasn't been registered yet pqxx::connection databaseConnection(configuration.databaseConnectionString); Database::prepareStatements(databaseConnection, { ID_SELECT_CHECK_EMAIL_EXISTS, ID_INSERT_NEW_FREELANCER }); pqxx::result checkResult = Database::executePreparedStatement_SELECT_CHECK_EMAIL_EXISTS(databaseConnection, email); int checkResultExtracted = stoi(checkResult.at(0).at(0).c_str()); if (checkResultExtracted == 0) { string pwsalt, pwhash; pwsalt = Utilities::generateSalt(); pwhash = Utilities::hashPassword(pwsalt, password); int errorLevel = Database::executePreparedStatement_INSERT_NEW_FREELANCER(databaseConnection, email, name, pwsalt, pwhash); if (errorLevel == 0) { ctx[MUSTACHE_REGISTRATION_SUCCESS] = true; ctx["freelancername"] = name; ctx["freelanceremail"] = email; } else { ctx[MUSTACHE_REGISTRATION_ERROR] = true; if (errorLevel == 1) ctx[MUSTACHE_REGISTRATION_ERROR_QUERY] = true; if (errorLevel == 2) ctx[MUSTACHE_REGISTRATION_ERROR_CRITICAL] = true; } } else { ctx[MUSTACHE_REGISTRATION_ERROR] = true; ctx["freelanceremail"] = email; ctx[MUSTACHE_REGISTRATION_ERROR_EMAIL_ALREADY_IN_USE] = true; } } else { ctx[MUSTACHE_REGISTRATION_ERROR] = true; if (password.compare(passwordConfirmation) != 0) ctx[MUSTACHE_REGISTRATION_ERROR_PASS_CONFIRMATION] = true; ctx[MUSTACHE_REGISTRATION_ERROR_EMAIL_NAME_PASS_NOT_FILLED] = true; } auto page = crow::mustache::load(TEMPLATE_FREELANCER_SIGN_UP_FULFILMENT); return page.render(ctx); }); /* * Freelancer Profile page */ CROW_ROUTE(app, "/freelancer/profile") ([&, configuration](const crow::request& getRequest ) { auto& cookieCtx = app.get_context(getRequest); crow::mustache::context ctx; string templateHTML = TEMPLATE_FREELANCER_PROFILE; if (Utilities::checkCookieLoginState(configuration, cookieCtx)) ctx[MUSTACHE_COOKIE_LOGGED_IN] = true; auto page = crow::mustache::load(templateHTML); return page.render(ctx); }); /* * Page for freelancer to create/select Templates */ CROW_ROUTE(app, "/freelancer/templateManagement") ([&, configuration](const crow::request& getRequest ) { auto& cookieCtx = app.get_context(getRequest); crow::mustache::context ctx; if (Utilities::checkCookieLoginState(configuration, cookieCtx)) { ctx = Utilities::getFreelancerTemplates(configuration, cookieCtx.get_cookie(COOKIE_FREELANCER_EMAIL)); ctx["freelanceremail"] = cookieCtx.get_cookie(COOKIE_FREELANCER_EMAIL); ctx[MUSTACHE_COOKIE_LOGGED_IN] = true; } auto page = crow::mustache::load(TEMPLATE_FREELANCER_TEMPLATE_MANAGEMENT); return page.render(ctx); }); /* * Page for freelancer to create new Template */ CROW_ROUTE(app, "/freelancer/templateManagement/template/new") ([&, configuration](const crow::request& getRequest ) { auto& cookieCtx = app.get_context(getRequest); crow::mustache::context ctx; if (Utilities::checkCookieLoginState(configuration, cookieCtx)) { ctx[MUSTACHE_COOKIE_LOGGED_IN] = true; } auto page = crow::mustache::load(TEMPLATE_FREELANCER_TEMPLATE_MANAGEMENT_CREATE_NEW); return page.render(ctx); }); /* * Page for freelancer to create new Template fulfilment */ CROW_ROUTE(app, "/freelancer/templateManagement/template/new/fulfilment").methods("POST"_method) ([&, configuration](const crow::request& postRequest ) { auto& cookieCtx = app.get_context(postRequest); crow::mustache::context ctx; if (Utilities::checkCookieLoginState(configuration, cookieCtx)) { ctx[MUSTACHE_COOKIE_LOGGED_IN] = true; Utilities::templateItem newTemplate; string postRequestBody = postRequest.body; Utilities::decodeString(postRequestBody); newTemplate.parseRequestBodyIntoItem(postRequestBody); pqxx::connection databaseConnection(configuration.databaseConnectionString); Database::prepareStatement(databaseConnection, ID_INSERT_FREELANCER_TEMPLATE); int errorLevel = Database::executePreparedStatement_INSERT_FREELANCER_TEMPLATE( databaseConnection, newTemplate.name, newTemplate.content, newTemplate.contactdata, newTemplate.contactinformation, newTemplate.currencypreference, newTemplate.priceupfront, newTemplate.priceondeliver, cookieCtx.get_cookie(COOKIE_FREELANCER_EMAIL)); if (errorLevel == 0) { ctx["templatename"] = newTemplate.name; ctx["contactdata"] = newTemplate.contactdata; ctx["contactinformation"] = newTemplate.contactinformation; ctx["currencypreference"] = newTemplate.currencypreference; ctx["priceupfront"] = newTemplate.priceupfront; ctx["priceondeliver"] = newTemplate.priceondeliver; ctx["content"] = newTemplate.content; } else { ctx[MUSTACHE_FREELANCER_TEMPLATE_CREATION_ERROR] = true; } } auto page = crow::mustache::load(TEMPLATE_FREELANCER_TEMPLATE_MANAGEMENT_CREATE_NEW_FULFILMENT); return page.render(ctx); }); /* * Page for freelancer to view/delete/edit a Template */ CROW_ROUTE(app, "/freelancer/templateManagement/template/").methods("POST"_method) ([&, configuration](const crow::request& postRequest, string templateName ) { auto& cookieCtx = app.get_context(postRequest); string postRequestBody = postRequest.body; Utilities::decodeString(postRequestBody); vector splitRequest = Utilities::splitStringIntoVector(postRequestBody, '='); vector splitValues = Utilities::splitStringIntoVector(splitRequest.at(1), '|'); bool cookieLoginStateValid = Utilities::checkCookieLoginState(configuration, cookieCtx); pqxx::connection databaseConnection(configuration.databaseConnectionString); crow::json::wvalue resultJsonTemplate; /* * only selects a template if the login state is valid, it is possible to get the template of another user loaded since they are by design public * but that should be a non issue since any operation that changes data will be performed with explicit ownership validation */ if (cookieLoginStateValid) { Database::prepareStatement(databaseConnection, ID_SELECT_TEMPLATE); pqxx::result resultTemplate = Database::executePreparedStatement_SELECT_TEMPLATE(databaseConnection, stoi(splitValues.at(1))); resultJsonTemplate = Database::convertResultRowToJSON(resultTemplate); } crow::mustache::context ctx(resultJsonTemplate); if (cookieLoginStateValid) { string templateOperation = splitValues.at(0); if (templateOperation == MUSTACHE_FREELANCER_TEMPLATE_OPERATION_VIEW) { ctx[MUSTACHE_FREELANCER_TEMPLATE_OPERATION_FULFILMENT_VIEW] = true; } else if (templateOperation == MUSTACHE_FREELANCER_TEMPLATE_OPERATION_EDIT) { ctx[MUSTACHE_FREELANCER_TEMPLATE_OPERATION_FULFILMENT_EDIT] = true; } else if (templateOperation == MUSTACHE_FREELANCER_TEMPLATE_OPERATION_DELETE) { ctx[MUSTACHE_FREELANCER_TEMPLATE_OPERATION_FULFILMENT_DELETE] = true; } else { ctx[MUSTACHE_FREELANCER_TEMPLATE_OPERATION_ERROR_NO_TEMPLATE] = true; } ctx[MUSTACHE_COOKIE_LOGGED_IN] = true; } auto page = crow::mustache::load(TEMPLATE_FREELANCER_TEMPLATE_MANAGEMENT_FULFILMENT_STAGE_CONFIRMATION); return page.render(ctx); }); /* * Execute Template Operation */ CROW_ROUTE(app, "/freelancer/templateManagement/fulfilment/").methods("POST"_method) ([&, configuration](const crow::request& postRequest, string operation ) { auto& cookieCtx = app.get_context(postRequest); crow::mustache::context ctx; bool operationEdit = false; bool operationDelete = false; bool error = false; if (operation == MUSTACHE_FREELANCER_TEMPLATE_OPERATION_EDIT) { operationEdit = true; ctx[MUSTACHE_FREELANCER_TEMPLATE_OPERATION_FULFILMENT_EDIT] = true; } else if (operation == MUSTACHE_FREELANCER_TEMPLATE_OPERATION_DELETE) { operationDelete = true; ctx[MUSTACHE_FREELANCER_TEMPLATE_OPERATION_FULFILMENT_DELETE] = true; } else { error = true; ctx[MUSTACHE_FREELANCER_TEMPLATE_OPERATION_FULFILMENT_ERROR] = true; } if (!error) { if (Utilities::checkCookieLoginState(configuration, cookieCtx)) { ctx[MUSTACHE_COOKIE_LOGGED_IN] = true; string freelancerEmail = cookieCtx.get_cookie(COOKIE_FREELANCER_EMAIL); string postRequestBody = postRequest.body; Utilities::decodeString(postRequestBody); int templateid; vector splitPostRequestBody = Utilities::splitStringIntoVector(postRequestBody, '&'); for (const string& item : splitPostRequestBody) { vector splitItem = Utilities::splitStringIntoVector(item, '='); if (splitItem[0] == "templateid") { templateid = stoi(splitItem[1]); break; } } pqxx::connection databaseConnection(configuration.databaseConnectionString); if (operationEdit) { Utilities::templateItem toEditTemplate; Database::prepareStatement(databaseConnection, ID_UPDATE_EDIT_FREELANCER_TEMPLATE); toEditTemplate.parseRequestBodyIntoItem(postRequestBody); bool updateSuccess = Database::executePreparedStatement_UPDATE_EDIT_FREELANCER_TEMPLATE( databaseConnection, toEditTemplate.name, toEditTemplate.content, toEditTemplate.contactdata, toEditTemplate.contactinformation, toEditTemplate.currencypreference, toEditTemplate.priceupfront, toEditTemplate.priceondeliver, templateid, freelancerEmail); if (updateSuccess) { ctx[MUSTACHE_FREELANCER_TEMPLATE_OPERATION_COMPLETE] = true; ctx[MUSTACHE_FREELANCER_TEMPLATE_OPERATION_FULFILMENT_EDIT] = true; } else { ctx[MUSTACHE_FREELANCER_TEMPLATE_OPERATION_FULFILMENT_ERROR] = true; } } else if (operationDelete) { Database::prepareStatement(databaseConnection, ID_DELETE_FREELANCER_TEMPLATE); Database::executePreparedStatement_DELETE_FREELANCER_TEMPLATE(databaseConnection, templateid, freelancerEmail); ctx[MUSTACHE_FREELANCER_TEMPLATE_OPERATION_COMPLETE] = true; ctx[MUSTACHE_FREELANCER_TEMPLATE_OPERATION_FULFILMENT_DELETE] = true; } } } auto page = crow::mustache::load(TEMPLATE_FREELANCER_TEMPLATE_MANAGEMENT_FULFILMENT_STAGE_EXECUTION); return page.render(ctx); }); /* * Page for freelancer to create/delete Alias */ CROW_ROUTE(app, "/freelancer/aliasManagement") ([&, configuration](const crow::request& getRequest ) { auto& cookieCtx = app.get_context(getRequest); crow::mustache::context ctx; if (Utilities::checkCookieLoginState(configuration, cookieCtx)) { ctx = Utilities::getFreelancerAlias(configuration, cookieCtx.get_cookie(COOKIE_FREELANCER_EMAIL)); ctx["freelanceremail"] = cookieCtx.get_cookie(COOKIE_FREELANCER_EMAIL); ctx[MUSTACHE_COOKIE_LOGGED_IN] = true; } auto page = crow::mustache::load(TEMPLATE_FREELANCER_ALIAS_MANAGEMENT); return page.render(ctx); }); /* * Execute Alias deletion */ CROW_ROUTE(app, "/freelancer/aliasManagement/delete").methods("POST"_method) ([&, configuration](const crow::request& postRequest) { auto& cookieCtx = app.get_context(postRequest); crow::mustache::context ctx; if (Utilities::checkCookieLoginState(configuration, cookieCtx)) { string postRequestBody = postRequest.body; Utilities::decodeString(postRequestBody); string aliasName; vector splitPostRequestBody = Utilities::splitStringIntoVector(postRequestBody, '&'); for (const string& item : splitPostRequestBody) { vector splitItem = Utilities::splitStringIntoVector(item, '='); if (splitItem.at(0) == "alias") aliasName = splitItem.at(1); } Utilities::deleteFreelancerAlias(configuration, aliasName, cookieCtx.get_cookie(COOKIE_FREELANCER_EMAIL)); ctx["freelanceremail"] = cookieCtx.get_cookie(COOKIE_FREELANCER_EMAIL); ctx[MUSTACHE_COOKIE_LOGGED_IN] = true; } auto page = crow::mustache::load(TEMPLATE_FREELANCER_ALIAS_MANAGEMENT_DELETE); return page.render(ctx); }); /* * Page for freelancer to create alias */ CROW_ROUTE(app, "/freelancer/aliasManagement/new") ([&, configuration](const crow::request& getRequest ) { auto& cookieCtx = app.get_context(getRequest); crow::mustache::context ctx; if (Utilities::checkCookieLoginState(configuration, cookieCtx)) { ctx = Utilities::getFreelancerTemplates(configuration, cookieCtx.get_cookie(COOKIE_FREELANCER_EMAIL)); ctx["freelanceremail"] = cookieCtx.get_cookie(COOKIE_FREELANCER_EMAIL); ctx[MUSTACHE_COOKIE_LOGGED_IN] = true; } auto page = crow::mustache::load(TEMPLATE_FREELANCER_ALIAS_MANAGEMENT_NEW); return page.render(ctx); }); /* * Execute alias creation */ CROW_ROUTE(app, "/freelancer/aliasManagement/new/fulfilment").methods("POST"_method) ([&, configuration](const crow::request& postRequest) { auto& cookieCtx = app.get_context(postRequest); crow::mustache::context ctx; if (Utilities::checkCookieLoginState(configuration, cookieCtx)) { bool error = false; ctx[MUSTACHE_COOKIE_LOGGED_IN] = true; string postRequestBody = postRequest.body; Utilities::decodeString(postRequestBody); vector splitPostRequestBody = Utilities::splitStringIntoVector(postRequestBody, '&'); string aliasname, templateNameID, templateName; for (const string& item : splitPostRequestBody) { vector splitItem = Utilities::splitStringIntoVector(item, '='); if (splitItem.at(0) == "freelanceralias") aliasname = splitItem.at(1); if (splitItem.at(0) == "aliasroute") templateNameID = splitItem.at(1); } pqxx::connection databaseConnection(configuration.databaseConnectionString); Database::prepareStatements(databaseConnection, { ID_SELECT_CHECK_FREELANCER_ALIAS, ID_INSERT_FREELANCER_ALIAS, ID_SELECT_FREELANCER_ID, ID_SELECT_FREELANCER_NAME }); bool aliasAlreadyUsed = false; if (aliasname.empty()) { error = true; ctx[MUSTACHE_FREELANCER_ALIAS_CREATION_ERROR] = true; ctx[MUSTACHE_FREELANCER_ALIAS_CREATION_ERROR_UNNAMED] = true; } else { ctx["aliasname"] = aliasname; aliasAlreadyUsed = Database::executePreparedStatement_SELECT_CHECK_FREELANCER_ALIAS(databaseConnection, aliasname); } if (aliasAlreadyUsed) { error = true; ctx[MUSTACHE_FREELANCER_ALIAS_CREATION_ERROR] = true; ctx[MUSTACHE_FREELANCER_ALIAS_CREATION_ERROR_DUPLICATE] = true; } if (!error) { string route, routeparameter, routevalue; pqxx::result freelancerNameResult = Database::executePreparedStatement_SELECT_FREELANCER_NAME(databaseConnection, cookieCtx.get_cookie(COOKIE_FREELANCER_EMAIL)); if (templateNameID.empty()) { routeparameter = "freelancerID"; pqxx::result freelancerIDResult = Database::executePreparedStatement_SELECT_FREELANCER_ID(databaseConnection, cookieCtx.get_cookie(COOKIE_FREELANCER_EMAIL)); routevalue = freelancerIDResult.at(0).at(0).c_str(); route = Utilities::generateAliasRouteFreelancer(freelancerNameResult.at(0).at(0).c_str()); } else { routeparameter = "templateID"; vector splitValues = Utilities::splitStringIntoVector(templateNameID, '|'); routevalue = splitValues.at(1); route = Utilities::generateAliasRouteFreelancerTemplate(freelancerNameResult.at(0).at(0).c_str(), splitValues.at(0)); } int errorLevel = Database::executePreparedStatement_INSERT_FREELANCER_ALIAS(databaseConnection, aliasname, cookieCtx.get_cookie(COOKIE_FREELANCER_EMAIL), route, routeparameter, routevalue); ctx["aliasname"] = aliasname; if (errorLevel != 0) ctx[MUSTACHE_FREELANCER_ALIAS_CREATION_ERROR] = true; } } auto page = crow::mustache::load(TEMPLATE_FREELANCER_ALIAS_MANAGEMENT_NEW_FULFILMENT); return page.render(ctx); }); /* * Page for freelancer to manage submissions for commissions */ CROW_ROUTE(app, "/freelancer/submissionManagement") ([&, configuration](const crow::request& getRequest ) { auto& cookieCtx = app.get_context(getRequest); crow::mustache::context ctx; if (Utilities::checkCookieLoginState(configuration, cookieCtx)) { ctx[MUSTACHE_COOKIE_LOGGED_IN] = true; } auto page = crow::mustache::load(TEMPLATE_FREELANCER_SUBMISSION_MANAGEMENT); return page.render(ctx); }); /* * Page for freelancer to add a new submissions for a commission */ CROW_ROUTE(app, "/freelancer/submissionManagement/add") ([&, configuration](const crow::request& getRequest ) { auto& cookieCtx = app.get_context(getRequest); crow::mustache::context ctx; if (Utilities::checkCookieLoginState(configuration, cookieCtx)) { ctx[MUSTACHE_COOKIE_LOGGED_IN] = true; } auto page = crow::mustache::load(TEMPLATE_FREELANCER_SUBMISSION_MANAGEMENT_ADD); return page.render(ctx); }); /* * Page for freelancer to view existing submissions */ CROW_ROUTE(app, "/freelancer/submissionManagement/view") ([&, configuration](const crow::request& getRequest ) { auto& cookieCtx = app.get_context(getRequest); crow::mustache::context ctx; if (Utilities::checkCookieLoginState(configuration, cookieCtx)) { ctx[MUSTACHE_COOKIE_LOGGED_IN] = true; } auto page = crow::mustache::load(TEMPLATE_FREELANCER_SUBMISSION_MANAGEMENT_VIEW); return page.render(ctx); }); /* * Execute adding of submission */ CROW_ROUTE(app, "/freelancer/submissionManagement/add/fulfillment") ([&, configuration](const crow::request& getRequest ) { auto& cookieCtx = app.get_context(getRequest); crow::mustache::context ctx; if (Utilities::checkCookieLoginState(configuration, cookieCtx)) { ctx[MUSTACHE_COOKIE_LOGGED_IN] = true; } auto page = crow::mustache::load(TEMPLATE_FREELANCER_SUBMISSION_MANAGEMENT_ADD); return page.render(ctx); }); /* * Page for freelancer to generate link to a particular submission */ CROW_ROUTE(app, "/freelancer/submissionManagement/view/generateLink") ([&, configuration](const crow::request& getRequest ) { auto& cookieCtx = app.get_context(getRequest); crow::mustache::context ctx; if (Utilities::checkCookieLoginState(configuration, cookieCtx)) { ctx[MUSTACHE_COOKIE_LOGGED_IN] = true; } auto page = crow::mustache::load(TEMPLATE_FREELANCER_SUBMISSION_MANAGEMENT_VIEW_GENERATE_LINK); return page.render(ctx); }); CROW_ROUTE(app, "/fileuploadtest") ([&, configuration](const crow::request& getRequest ) { auto& cookieCtx = app.get_context(getRequest); crow::mustache::context ctx; if (Utilities::checkCookieLoginState(configuration, cookieCtx)) { ctx[MUSTACHE_COOKIE_LOGGED_IN] = true; } auto page = crow::mustache::load("TEST_UPLOAD.html"); return page.render(ctx); }); CROW_ROUTE(app, "/fileuploadtestExecution") .methods(crow::HTTPMethod::Post)([](const crow::request& postRequest) { crow::multipart::message multipartMessage(postRequest); for (const auto& part : multipartMessage.part_map) { const auto& mustacheSubmissionNameValue = part.first; const auto& fileSubmission = part.second; if (MUSTACHE_FREELANCER_SUBMISSION_NAME == mustacheSubmissionNameValue) { auto contentDispositionIt = fileSubmission.headers.find("Content-Disposition"); if (contentDispositionIt == fileSubmission.headers.end()) { //ERROR no content disposition found return crow::response(400); } auto filenameIt = contentDispositionIt->second.params.find("filename"); if (filenameIt == contentDispositionIt->second.params.end()) { //ERROR no filename found return crow::response(400); } const std::string outputFilename = filenameIt->second; std::ofstream outputFileStream(outputFilename); if (!outputFileStream) { //ERROR Write to file failed } outputFileStream << fileSubmission.body; outputFileStream.close(); } else { //ERROR Mustache Submission Name can not be found } } return crow::response(200); }); /*CROW_ROUTE(app, "/getEntry/") ([databaseURI](int entry) { pqxx::connection databaseConnection(databaseURI); pqxx::result result = Database::executePreparedStatement_SELECT_ITEM_BY_ID(databaseConnection, entry); auto page = crow::mustache::load("test.html"); crow::json::wvalue resultJson = Database::convertResultRowToJSON(result); crow::mustache::context ctx(resultJson); return page.render(ctx); }); CROW_ROUTE(app, "/testRequest") ([databaseURI]() { Database::requestsItem item; item.customerEmailAddress = "test@testmail.com"; item.freelancer = "michael"; item.templateName = "coding"; item.currencyPreference = "XMR"; item.priceUpFront = "0.36"; item.priceOnDeliver = "0.36"; item.requestDescription = "This is a test"; item.accepted = stoi("0"); item.upFrontInvoiceID = "12424242424"; item.onDeliverInvoiceID = "329532532532"; item.upFrontPaid = stoi("0"); item.onDeliverPaid = stoi("0"); pqxx::connection databaseConnection(databaseURI); Database::executePreparedStatement_INSERT_ITEM_IN_REQUESTS(databaseConnection, item); return crow::response(200); }); CROW_ROUTE(app, "/newRequest") .methods("POST"_method) ([databaseURI](const crow::request &req) { auto jsonBody = crow::json::load(req.body); if (!jsonBody) return crow::response(crow::status::BAD_REQUEST); * Example CURL to insert into DB: * Price must be delivered as a string to avoid unnecessary conversion to double which would lead to the addition to a database of the value 0.359999999999999999999999999 instead of 0.36. * curl -H "Content-Type: application/json" -H "Accept: application/json" -d '{"customerEmailAddress":"2@testmail.com","freelancer":"nick","templateName":"coding","currencyPreference":"XMR","priceUpFront":"0.36","priceOnDeliver":"0.36","requestDescription":"This is a test","accepted":false,"upFrontInvoiceID":"12424242424","onDeliverInvoiceID":"329532532532","upFrontPaid":false,"onDeliverPaid":false}' http://0.0.0.0:18080/newRequest Database::requestsItem item; item.insertJsonIntoItem(jsonBody); pqxx::connection databaseConnection(databaseURI); Database::executePreparedStatement_INSERT_ITEM_IN_REQUESTS(databaseConnection, item); return crow::response(200); }); */ //set the port, set the app to run on multiple threads, and run the app #ifdef CROW_ENABLE_SSL cout << "SSL Enabled" << endl; app.ssl_file(configuration.sslCrtPath, configuration.sslKeyPath); #endif app.port(18080).multithreaded().run(); }