#include #include #include "crow.h" extern "C" { #include "ternarytrie.h" } static const std::string index_page = R"(

r8r.be

This is the URL shortener and pastebin accompanying my site, The Rusty Bever.

)"; #define ENV(var, env_var) \ const char *_##var = getenv(env_var); \ if (_##var == NULL) { \ printf("Missing environment variable %s.\n", env_var); \ return 1; \ } \ const std::string var = std::string(_##var); #define AUTH() \ std::string provided_api_key = req.get_header_value("X-Api-Key"); \ if (api_key.compare(provided_api_key) != 0) { \ return crow::response(crow::status::UNAUTHORIZED); \ } crow::response add_redirect(std::string base_url, TernaryTrie *trie, const char *url, bool secure) { Entry *new_entry = entry_new(Redirect, url); char *key = ternarytrie_add_random(trie, new_entry, secure); if (key == NULL) { return crow::response(crow::status::INTERNAL_SERVER_ERROR); } std::string res = base_url + key; free(key); return crow::response(res); } bool store_paste(const char *key, const char *body) { // Write paste contents to file std::fstream file; file.open(std::string("pastes/") + key, std::ios_base::out); if (!file.is_open()) { return false; } file << body; file.close(); return true; } crow::response add_paste(std::string base_url, TernaryTrie *trie, const char *body, bool secure) { Entry *new_entry = entry_new(Paste, ""); char *key = ternarytrie_add_random(trie, new_entry, secure); if (key == NULL) { return crow::response(crow::status::INTERNAL_SERVER_ERROR); } if (!store_paste(key, body)) { return crow::response(crow::status::INTERNAL_SERVER_ERROR); } std::string res = base_url + key; free(key); return crow::response(res); } int main() { // Initialize random seed for generating URLs srand(time(NULL)); ENV(api_key, "LANDER_API_KEY"); ENV(base_url, "LANDER_BASE_URL"); // Initialize trie and populate from data file TernaryTrie *trie = ternarytrie_init(); std::string file_path = "lander.data"; std::cout << "Populating trie from file '" << file_path << "'..." << std::endl; int count = ternarytrie_populate(trie, file_path.c_str()); if (count == -1) { std::cout << "An error occured while populating the trie." << std::endl; exit(1); } else { std::cout << "Added " << count << " entries to trie." << std::endl; } // Create pastes directory if not present // TODO don't just ignore errors here mkdir("pastes", 0700); crow::SimpleApp app; app.loglevel(crow::LogLevel::Info); CROW_ROUTE(app, "/").methods(crow::HTTPMethod::Get)( []() { return crow::response("html", index_page); }); // Serve an entry CROW_ROUTE(app, "/") .methods(crow::HTTPMethod::Get)( [trie](crow::response &res, std::string key) { Entry *entry = ternarytrie_search(trie, key.c_str()); if (entry != NULL) { if (entry->type == Redirect) { res.redirect(entry->string); } else if (entry->type == Paste) { res.set_static_file_info("pastes/" + key); } } else { res.code = 404; } res.end(); }); // Add a new Redirect with a short randomly generated key CROW_ROUTE(app, "/s/") .methods(crow::HTTPMethod::Post)( [api_key, base_url, trie](const crow::request req) { AUTH(); return add_redirect(base_url, trie, req.body.c_str(), false); }); // Add a new Redirect with a long randomly generated key CROW_ROUTE(app, "/sl/") .methods(crow::HTTPMethod::Post)( [api_key, base_url, trie](const crow::request req) { AUTH(); return add_redirect(base_url, trie, req.body.c_str(), true); }); // Add a new Redirect with a given key CROW_ROUTE(app, "/s/") .methods(crow::HTTPMethod::Post)( [api_key, base_url, trie](const crow::request &req, std::string key) { AUTH(); Entry *new_entry = entry_new(Redirect, req.body.c_str()); bool added = ternarytrie_add(trie, key.c_str(), new_entry); if (!added) { return crow::response(crow::status::CONFLICT); } return crow::response(base_url + key); }); // Add a new Paste with a short randomly generated key CROW_ROUTE(app, "/p/") .methods(crow::HTTPMethod::Post)( [api_key, base_url, trie](const crow::request &req) { AUTH(); return add_paste(base_url, trie, req.body.c_str(), false); }); // Add a new Paste with a long randomly generated key CROW_ROUTE(app, "/pl/") .methods(crow::HTTPMethod::Post)( [api_key, base_url, trie](const crow::request &req) { AUTH(); return add_paste(base_url, trie, req.body.c_str(), true); }); // Add a paste with a given key CROW_ROUTE(app, "/p/") .methods(crow::HTTPMethod::Post)( [api_key, base_url, trie](const crow::request &req, std::string key) { AUTH(); Entry *new_entry = entry_new(Paste, ""); bool added = ternarytrie_add(trie, key.c_str(), new_entry); if (!added) { return crow::response(crow::status::CONFLICT); } if (!store_paste(key.c_str(), req.body.c_str())) { return crow::response(crow::status::INTERNAL_SERVER_ERROR); } return crow::response(base_url + key); }); app.port(18080).multithreaded().run(); }