#include #include #include #include #include #include #include #include #include #include #include "crow.h" #include "cpp/opportunisticsecuresmtpclient.hpp" #include "cpp/htmlmessage.hpp" #include "emailTemplateCollection.cpp" using namespace jed_utils::cpp; /* * Utility Manager */ namespace Utilities { const static std::map HTML_URL_CODES = {{"%20", " "}, {"%21", "!"}, {"%22", "\""}, {"%23", "#"}, {"%24", "$"}, {"%25", "%"}, {"%26", "&"}, {"%27", "'"}, {"%28", "("}, {"%29", ")"}, {"%2A", "*"}, {"%2B", "+"}, {"%2C", ","}, {"%2D", "-"}, {"%2E", "."}, {"%2F", "/"}, {"%30", "0"}, {"%31", "1"}, {"%32", "2"}, {"%33", "3"}, {"%34", "4"}, {"%35", "5"}, {"%36", "6"}, {"%37", "7"}, {"%38", "8"}, {"%39", "9"}, {"%3A", ":"}, {"%3B", ";"}, {"%3C", "<"}, {"%3D", "="}, {"%3E", ">"}, {"%3F", "?"}, {"%40", "@"}, {"%5B", "["}, {"%5C", "\\"}, {"%5D", "]"}, {"%5E", "^"}, {"%5F", "_"}, {"%60", "`"}, {"%7B", "{"}, {"%7C", "|"}, {"%7D", "}"}, {"%7E", "~"}, {"%7F", " "}, {"%80", "€"}, {"%82", "‚"}, {"%83", "ƒ"}, {"%84", "„"}, {"%85", "…"}, {"%86", "†"}, {"%87", "‡"}, {"%88", "ˆ"}, {"%89", "‰"}, {"%8A", "Š"}, {"%8B", "‹"}, {"%8C", "Œ"}, {"%8E", "Ž"}, {"%91", "‘"}, {"%92", "’"}, {"%93", "“"}, {"%94", "”"}, {"%95", "•"}, {"%96", "–"}, {"%97", "—"}, {"%98", "˜"}, {"%99", "™"}, {"%9A", "š"}, {"%9B", "›"}, {"%9C", "œ"}, {"%9E", "ž"}, {"%9F", "Ÿ"}, {"%A1", "¡"}, {"%A2", "¢"}, {"%A3", "£"}, {"%A4", "¤"}, {"%A5", "¥"}, {"%A6", "¦"}, {"%A7", "§"}, {"%A8", "¨"}, {"%A9", "©"}, {"%AA", "ª"}, {"%AB", "«"}, {"%AC", "¬"}, {"%AE", "®"}, {"%AF", "¯"}, {"%B0", "°"}, {"%B1", "±"}, {"%B2", "²"}, {"%B3", "³"}, {"%B4", "´"}, {"%B5", "µ"}, {"%B6", "¶"}, {"%B7", "·"}, {"%B8", "¸"}, {"%B9", "¹"}, {"%BA", "º"}, {"%BB", "»"}, {"%BC", "¼"}, {"%BD", "½"}, {"%BE", "¾"}, {"%BF", "¿"}, {"%C0", "À"}, {"%C1", "Á"}, {"%C2", "Â"}, {"%C3", "Ã"}, {"%C4", "Ä"}, {"%C5", "Å"}, {"%C6", "Æ"}, {"%C7", "Ç"}, {"%C8", "È"}, {"%C9", "É"}, {"%CA", "Ê"}, {"%CB", "Ë"}, {"%CC", "Ì"}, {"%CD", "Í"}, {"%CE", "Î"}, {"%CF", "Ï"}, {"%D0", "Ð"}, {"%D1", "Ñ"}, {"%D2", "Ò"}, {"%D3", "Ó"}, {"%D4", "Ô"}, {"%D5", "Õ"}, {"%D6", "Ö"}, {"%D7", "×"}, {"%D8", "Ø"}, {"%D9", "Ù"}, {"%DA", "Ú"}, {"%DB", "Û"}, {"%DC", "Ü"}, {"%DD", "Ý"}, {"%DE", "Þ"}, {"%DF", "ß"}, {"%E0", "à"}, {"%E1", "á"}, {"%E2", "â"}, {"%E3", "ã"}, {"%E4", "ä"}, {"%E5", "å"}, {"%E6", "æ"}, {"%E7", "ç"}, {"%E8", "è"}, {"%E9", "é"}, {"%EA", "ê"}, {"%EB", "ë"}, {"%EC", "ì"}, {"%ED", "í"}, {"%EE", "î"}, {"%EF", "ï"}, {"%F0", "ð"}, {"%F1", "ñ"}, {"%F2", "ò"}, {"%F3", "ó"}, {"%F4", "ô"}, {"%F5", "õ"}, {"%F6", "ö"}, {"%F7", "÷"}, {"%F8", "ø"}, {"%F9", "ù"}, {"%FA", "ú"}, {"%FB", "û"}, {"%FC", "ü"}, {"%FD", "ý"}, {"%FE", "þ"}, {"%FF", "ÿ"}}; const static int SALT_SIZE = 32; const std::string SALT_CHAR_SET = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"; /* * Takes string to split it into a vector based on a given delimiter */ std::vector splitStringIntoVector(const std::string& stringToSplit, char delimiter) { std::vector splitVector; std::istringstream stringStream(stringToSplit); while (!stringStream.eof()) { std::string splitResult; std::getline( stringStream, splitResult, delimiter); splitVector.push_back(splitResult); } return splitVector; } std::string trimFromDelimiter(std::string stringToTrim, char delimiter) { std::size_t position = stringToTrim.find(delimiter); if (position != std::string::npos) return stringToTrim.substr(0, position); return stringToTrim; } /* * Struct representing the configuration file */ struct config { std::string configPath = "cavecomm.conf"; std::string databaseConnectionString; std::string emailAddress; std::string emailPassword; std::string emailServerAddress; int emailServerPort = 587; std::string emailAddressDisplay = "Cavecomm Automated Management System"; std::string sslCrtPath; std::string sslKeyPath; std::string domain; /* * validates existence of mandatory variables in config * returns 0 if successful else 1 */ int checkMandatoryVariables() const { if ( emailAddress.empty() || emailPassword.empty() || emailServerAddress.empty() || databaseConnectionString.empty() || sslCrtPath.empty() || sslKeyPath.empty() || domain.empty() ) return 1; return 0; } /* * Parses the config file and fills the struct. * returns 0 if successful else 1 */ int readConfigFile(){ int errorLevel = 0; std::ifstream configFile(configPath); std::cout << "Loading Configuration: " << configPath << std::endl; if (configFile.good()) { bool sanityCheckStart = true; std::string line; while (std::getline(configFile, line)) { if (sanityCheckStart) { if (line.find("#configstart#") != std::string::npos) { sanityCheckStart = false; continue; } else { errorLevel = 1; std::cout << "ERROR: Config file is invalid" << std::endl; break; } } else { if (line.size() <= 1) continue; } if (line.find("#configend#") != std::string::npos) break; if (line.at(0) == '#') { std::cout << "COMMENT: "; std::cout << line << std::endl; } else { std::cout << "CONFIG: "; std::vector lineVector = Utilities::splitStringIntoVector(line, '='); if (lineVector.size() == 2) { std::cout << lineVector.at(0) << " - " << lineVector.at(1) << std::endl; if (lineVector.at(0) == "emailAddress") { emailAddress = trimFromDelimiter(lineVector.at(1), ';'); continue; } if (lineVector.at(0) == "emailPassword") { emailPassword = trimFromDelimiter(lineVector.at(1), ';'); continue; } if (lineVector.at(0) == "emailServerAddress") { emailServerAddress = trimFromDelimiter(lineVector.at(1), ';'); continue; } if (lineVector.at(0) == "emailServerPort") { emailServerPort = std::stoi(trimFromDelimiter(lineVector.at(1), ';')); continue; } if (lineVector.at(0) == "emailAddressDisplay") { emailAddressDisplay = trimFromDelimiter(lineVector.at(1), ';'); continue; } if (lineVector.at(0) == "databaseConnectionString") { databaseConnectionString = trimFromDelimiter(lineVector.at(1), ';'); continue; } if (lineVector.at(0) == "sslCrtPath") { sslCrtPath = trimFromDelimiter(lineVector.at(1), ';'); continue; } if (lineVector.at(0) == "sslKeyPath") { sslKeyPath = trimFromDelimiter(lineVector.at(1), ';'); continue; } if (lineVector.at(0) == "domain") { domain = trimFromDelimiter(lineVector.at(1), ';'); continue; } } } } } else { errorLevel = 1; } if (errorLevel == 0) errorLevel = checkMandatoryVariables(); return errorLevel; } }; /* * Takes String and cuts from the start-up to the length of valueName */ std::string extractSingleValueFromRequestBody(const std::string& responseBody, const std::string& valueName) { return responseBody.substr(valueName.length() + 1, responseBody.length()); } /* * replaces a string with another string within a string */ void replaceString(std::string &stringToProcess, const std::string& from, const std::string& to) { std::size_t stringPosition = stringToProcess.find(from); while (stringPosition != std::string::npos) { stringToProcess.replace(stringToProcess.find(from), std::string(from).size(), to); stringPosition = stringToProcess.find(from); } } /* * decodes html coded chars */ void decodeString(std::string &stringToDecode) { replaceString(stringToDecode, "+", " "); for(const auto & it : HTML_URL_CODES) { replaceString(stringToDecode, it.first, it.second); } } /* * Sends an HTML Email using CPP-SMTPClient-library * return 0 at success, 1 at client fail, 2 at critical fail. */ int sendEmail(const config& configuration, const std::string& destinationEmail, std::string emailSubject, std::string htmlBody){ OpportunisticSecureSMTPClient client(configuration.emailServerAddress, configuration.emailServerPort); client.setCredentials(Credential(configuration.emailAddress, configuration.emailPassword)); try { const MessageAddress from(configuration.emailAddress, configuration.emailAddressDisplay); const auto to = { MessageAddress(destinationEmail) }; const auto subject = std::move(emailSubject); const auto body = std::move(htmlBody); const std::vector cc = {}; const std::vector bcc = {}; const std::vector attachments = {}; HTMLMessage msg(from, to, subject, body, cc, bcc, attachments); int err_no = client.sendMail(msg); if (err_no != 0) { std::cerr << client.getCommunicationLog() << '\n'; std::string errorMessage = OpportunisticSecureSMTPClient::getErrorMessage(err_no); std::cerr << "An error occurred: " << errorMessage << " (error no: " << err_no << ")" << '\n'; return 1; } std::cout << client.getCommunicationLog() << '\n'; std::cout << "Operation completed!" << std::endl; } catch (std::invalid_argument &err) { std::cerr << err.what() << std::endl; return 2; } return 0; } std::string createHashSha512(const std::string& str){ unsigned char hash[SHA512_DIGEST_LENGTH]; SHA512_CTX sha512; SHA512_Init(&sha512); SHA512_Update(&sha512, str.c_str(), str.size()); SHA512_Final(hash, &sha512); std::stringstream ss; for(int i = 0; i < SHA512_DIGEST_LENGTH; i++){ ss << std::hex << std::setw(2) << std::setfill('0') << static_cast( hash[i] ); } return ss.str(); } std::string createHashSha256(const std::string& str){ unsigned char hash[SHA256_DIGEST_LENGTH]; SHA512_CTX sha256; SHA512_Init(&sha256); SHA512_Update(&sha256, str.c_str(), str.size()); SHA512_Final(hash, &sha256); std::stringstream ss; for(int i = 0; i < SHA256_DIGEST_LENGTH; i++){ ss << std::hex << std::setw(2) << std::setfill('0') << static_cast( hash[i] ); } return ss.str(); } /* * Generates a salt using Mersenne Twister */ std::string generateSalt() { std::random_device randomDevice; std::mt19937 saltGenerator(randomDevice()); std::uniform_int_distribution<> distribution(0, SALT_CHAR_SET.size() - 1); std::string salt; for (int i = 0; i < SALT_SIZE; i++) { salt += SALT_CHAR_SET[distribution(saltGenerator)]; } return salt; } /* * Hashes a given password with a given salt */ std::string hashPassword(const std::string& pwsalt, const std::string& password) { return createHashSha512(pwsalt + password); } /* * Gets The Alias * takes configuration * returns crow::json::wvalue with the Alias */ crow::json::wvalue getAlias(const Utilities::config& configuration, const std::string& alias) { pqxx::connection databaseConnection(configuration.databaseConnectionString); pqxx::result resultAlias = Database::executePreparedStatement_SELECT_ALIAS(databaseConnection, alias); crow::json::wvalue resultJsonAlias; if (!resultAlias.empty()) resultJsonAlias = Database::convertResultRowToJSON(resultAlias); else resultJsonAlias["route"] = "/"; //If the alias does not exist redirect back to the index. return resultJsonAlias; } /* * Gets The freelancer listing * takes configuration * returns crow::json::wvalue with the Freelancer profile listing */ crow::json::wvalue getFreelancerListing(const Utilities::config& configuration) { pqxx::connection databaseConnection(configuration.databaseConnectionString); pqxx::result result = Database::executeStatement_SELECT_FREELANCERS_WITHCOMMISSIONSSTATE(databaseConnection); return Database::convertResultToJSON(result, "freelancerProfiles"); } /* * Checks if freelancer is logged in * Takes config, the loginkey and freelancerid taken from the secure cookie * returns true if logged in */ bool checkFreelancerLoginState(const Utilities::config& configuration, const std::string& loginKey, const std::string& freelancerEmail) { bool loggedIn = false; //check if freelancer exists pqxx::connection databaseConnection(configuration.databaseConnectionString); pqxx::result checkLoginKey = Database::executePreparedStatement_SELECT_CHECK_FREELANCER_LOGIN_STATE(databaseConnection, freelancerEmail, loginKey); int checkLoginKeyExtracted = std::stoi(checkLoginKey.at(0).at(0).c_str()); if (checkLoginKeyExtracted == 1) loggedIn = true; return loggedIn; } /* * Generates a hash based on a combination of 2 random salts */ std::string generateRandomHashValueSHA512() { return createHashSha512(generateSalt() + generateSalt()); } /* * Generates a hash based on a combination of 2 random salts */ std::string generateRandomHashValueSHA256() { return createHashSha256(generateSalt() + generateSalt()); } /* * Generates a secure cookie */ std::string generateSecureCookie(const std::string& value, bool stayLoggedIn) { std:: string secureCookieValue = value; secureCookieValue += "; HttpOnly"; // avoid JS from Accessing cookie secureCookieValue += "; Secure"; // set cookie to be secure secureCookieValue += "; Path=/"; if (stayLoggedIn) secureCookieValue += "; Expires=Fri, 31 Dec 9999 23:59:59 GMT"; //max value since cookies can't be set to never expire else it will simply be a session cookie return secureCookieValue; } /* * Generates the value to create a secure cookie with the value representing a logged-in session */ std::string generateSecureCookieLoginKeyValue(const std::string& loginKeyValue, bool stayLoggedIn) { return generateSecureCookie(loginKeyValue, stayLoggedIn); } /* * generates the value to create a secure cookie with the value representing a logged-in FreelancerID */ std::string generateSecureCookieFreelancerEmailValue(const std::string& freelancerEmailValue, bool stayLoggedIn) { return generateSecureCookie(freelancerEmailValue, stayLoggedIn); } /* * Generates a cookie that is expired contains no relevant information. */ std::string generateExpiredCookie() { return "EXPIRED; HttpOnly; Secure; Path=/; Max-Age=0"; } }