From a6dfc269d93cdf557f6dac62b03b886d694faecd Mon Sep 17 00:00:00 2001 From: Joel Klinghed Date: Thu, 4 Jun 2015 00:22:57 +0200 Subject: Add config and change all commands to one --- src/Makefile.am | 5 ++- src/config.cc | 55 +++++++++++++++++++++++ src/config.hh | 29 ++++++++++++ src/event_main.cc | 129 +++++++++++++++++++++++++++++++++++++----------------- src/strutils.cc | 14 ++++++ src/strutils.hh | 2 + 6 files changed, 191 insertions(+), 43 deletions(-) create mode 100644 src/config.cc create mode 100644 src/config.hh diff --git a/src/Makefile.am b/src/Makefile.am index 7439c4b..6b6b797 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -1,12 +1,13 @@ MAINTAINERCLEANFILES = Makefile.in -AM_CPPFLAGS = @DEFINES@ -DLOCALSTATEDIR='"@localstatedir@/stuff"' +AM_CPPFLAGS = @DEFINES@ -DLOCALSTATEDIR='"@localstatedir@/stuff"' \ + -DSYSCONFDIR='"@sysconfdir@/stuff"' bin_PROGRAMS = event noinst_LTLIBRARIES = libdb.la libcgi.la event_SOURCES = event.cc event.hh event_main.cc common.hh cgi.hh db.hh \ - fsutils.cc fsutils.hh + fsutils.cc fsutils.hh config.cc config.hh event_LDADD = libdb.la libcgi.la libcgi_la_SOURCES = cgi.hh common.hh cgi.cc \ diff --git a/src/config.cc b/src/config.cc new file mode 100644 index 0000000..516ccde --- /dev/null +++ b/src/config.cc @@ -0,0 +1,55 @@ +#include "common.hh" + +#include +#include + +#include "config.hh" +#include "strutils.hh" + +namespace stuff { + +namespace { + +class ConfigImpl : public Config { +public: + ~ConfigImpl() override { + } + + std::string get(const std::string& name, + const std::string& fallback) const override { + auto it = data_.find(name); + if (it == data_.end()) return fallback; + return it->second; + } + + bool load(const std::string& path) override { + std::ifstream in(path); + std::unordered_map data; + while (in.good()) { + std::string line; + std::getline(in, line); + if (line.empty() || line.front() == '#') continue; + auto pos = line.find('='); + if (pos == std::string::npos) return false; + data.insert(std::make_pair(trim(line.substr(0, pos)), + trim(line.substr(pos + 1)))); + } + if (!in.eof()) return false; + data_.swap(data); + return true; + } + + ConfigImpl() { + } + +private: + std::unordered_map data_; +}; + +} // namespace + +std::unique_ptr Config::create() { + return std::unique_ptr(new ConfigImpl()); +} + +} // namespace stuff diff --git a/src/config.hh b/src/config.hh new file mode 100644 index 0000000..6ea3607 --- /dev/null +++ b/src/config.hh @@ -0,0 +1,29 @@ +#ifndef CONFIG_HH +#define CONFIG_HH + +#include +#include + +namespace stuff { + +class Config { +public: + virtual ~Config() {} + + virtual std::string get(const std::string& name, + const std::string& fallback) const = 0; + virtual bool load(const std::string& path) = 0; + + static std::unique_ptr create(); + +protected: + Config() {} + +private: + Config(const Config&) = delete; + Config& operator=(const Config&) = delete; +}; + +} // namespace stuff + +#endif /* CONFIG_HH */ diff --git a/src/event_main.cc b/src/event_main.cc index ce3800c..85c35eb 100644 --- a/src/event_main.cc +++ b/src/event_main.cc @@ -11,6 +11,7 @@ #include "args.hh" #include "cgi.hh" +#include "config.hh" #include "db.hh" #include "event.hh" #include "fsutils.hh" @@ -33,7 +34,7 @@ using namespace stuff; namespace { -std::string g_db_path; +std::unique_ptr g_cfg; std::shared_ptr open(const std::string& channel) { std::string tmp = channel; @@ -45,11 +46,13 @@ std::shared_ptr open(const std::string& channel) { *it = '.'; } } - if (!mkdir_p(g_db_path)) { + std::string path = g_cfg->get("db_path", LOCALSTATEDIR); + if (path.empty()) path = "."; + if (!mkdir_p(path)) { Http::response(200, "Unable to create database directory"); return nullptr; } - auto db = SQLite3::open(g_db_path + tmp + ".db"); + auto db = SQLite3::open(path + "/" + tmp + ".db"); if (!db || db->bad()) { Http::response(200, "Unable to open database"); db.reset(); @@ -142,11 +145,10 @@ bool parse_time(const std::string& value, time_t* date) { } bool create(const std::string& channel, - std::map& data) { - std::vector args; - if (!parse(data["text"], &args)) return true; + std::map& data, + std::vector& args) { if (args.size() < 2) { - Http::response(200, "Usage: /create NAME START [TEXT]"); + Http::response(200, "Usage: create NAME START [TEXT]"); return true; } std::string name, text; @@ -214,9 +216,8 @@ bool append_indexes(Iterator begin, Iterator end, } bool cancel(const std::string& channel, - std::map& data) { - std::vector args; - if (!parse(data["text"], &args)) return true; + std::map& data, + std::vector& args) { std::vector indexes; if (args.empty()) { indexes.push_back(0); @@ -265,15 +266,14 @@ bool cancel(const std::string& channel, } bool update(const std::string& channel, - std::map& data) { + std::map& data, + std::vector& args) { std::set update; update.insert("name"); update.insert("start"); update.insert("text"); - std::vector args; - if (!parse(data["text"], &args)) return true; if (args.empty()) { - Http::response(200, "Usage: /update [INDEX] [name NAME] [start START] [text TEXT]"); + Http::response(200, "Usage: update [INDEX] [name NAME] [start START] [text TEXT]"); return true; } auto db = open(channel); @@ -355,9 +355,8 @@ bool update(const std::string& channel, } bool show(const std::string& channel, - std::map& data) { - std::vector args; - if (!parse(data["text"], &args)) return true; + std::map& data, + std::vector& args) { std::vector indexes; if (args.empty()) { indexes.push_back(0); @@ -414,14 +413,13 @@ bool show(const std::string& channel, bool going(const std::string& channel, std::map& data, + std::vector& args, bool going) { - std::vector args; const auto& user_name = data["user_name"]; if (user_name.empty()) { Http::response(500, "No user_name"); return true; } - if (!parse(data["text"], &args)) return true; std::unique_ptr event; std::vector indexes; std::string note; @@ -487,6 +485,46 @@ bool going(const std::string& channel, return true; } +bool help(std::vector& args) { + std::ostringstream ss; + if (args.empty()) { + ss << "Usage: help COMMAND" << std::endl; + ss << "Known commands: create, update, cancel, show, going, !going"; + return true; + } else if (args.front() == "create") { + ss << "Usage: create NAME START [TEXT]" << std::endl; + ss << "Create a new event with the name NAME starting at START with" + << " an optional description TEXT." << std::endl; + ss << "START can be of the format: [DATE|DAY] HH:MM"; + } else if (args.front() == "update") { + ss << "Usage: update [INDEX] [name NAME] [start START] [text TEXT]" + << std::endl; + ss << "Update an event, specified by index (default is next event)" + << std::endl; + ss << "See help for create for description of NAME, START and TEXT."; + } else if (args.front() == "cancel") { + ss << "Usage: cancel [INDEX...]" << std::endl; + ss << "Cancel one or more events given by index" + << " (default is next event)"; + } else if (args.front() == "show") { + ss << "Usage: show [INDEX...]" << std::endl; + ss << "Show one or more events given by index" + << " (default is next event)"; + } else if (args.front() == "going") { + ss << "Usage: going [INDEX] [NOTE]" << std::endl; + ss << "Join an event specified by index (default is next event)" + << " and add an optional NOTE"; + } else if (args.front() == "!going") { + ss << "Usage: !going [INDEX] [NOTE]" << std::endl; + ss << "Un-join an event specified by index (default is next event)" + << " and add an optional NOTE"; + } else { + ss << "Unknown command: " << args.front(); + } + Http::response(200, ss.str()); + return true; +} + bool handle_request(CGI* cgi) { switch (cgi->request_type()) { case CGI::GET: @@ -499,50 +537,59 @@ bool handle_request(CGI* cgi) { std::map data; cgi->get_data(&data); + const auto& token = data["token"]; + if (token != g_cfg->get("token", "")) { + Http::response(500, "Bad token"); + return true; + } const auto& channel = data["channel"]; if (channel.empty()) { Http::response(500, "No channel"); return true; } - auto command = data["command"]; - if (command.front() == '/') command = command.substr(1); + std::vector args; + if (!parse(data["text"], &args)) return true; + if (args.empty()) { + std::ostringstream ss; + ss << "Usage: [COMMAND] [ARGUMENTS]" << std::endl + << "For more help about a command, use " << data["command"] + << " help COMMAND"; + Http::response(200, ss.str()); + return true; + } + auto command = args.front(); + args.erase(args.begin()); if (command == "create") { - return create(channel, data); + return create(channel, data, args); } if (command == "cancel") { - return cancel(channel, data); + return cancel(channel, data, args); } if (command == "update") { - return update(channel, data); + return update(channel, data, args); } if (command == "show") { - return show(channel, data); + return show(channel, data, args); } if (command == "going") { - return going(channel, data, true); + return going(channel, data, args, true); } if (command == "!going") { - return going(channel, data, false); + return going(channel, data, args, false); } - Http::response(500, "Unknown command: " + command); + if (command == "help") { + return help(args); + } + Http::response(200, "Unknown command: " + command); return true; } } // namespace -int main(int argc, char** argv) { - if (argc < 2) { - g_db_path = LOCALSTATEDIR "/"; - } else if (argc == 2) { - g_db_path = argv[1]; - if (g_db_path.empty()) { - g_db_path = "."; - } else if (g_db_path.back() != '/') { - g_db_path.push_back('/'); - } - } else { - std::cerr << "Too many arguments" << std::endl; - return EXIT_FAILURE; +int main() { + g_cfg = Config::create(); + if (!g_cfg->load("./event.config")) { + g_cfg->load(SYSCONFDIR "/event.config"); } return CGI::run(handle_request); } diff --git a/src/strutils.cc b/src/strutils.cc index e7b7c93..1687f53 100644 --- a/src/strutils.cc +++ b/src/strutils.cc @@ -26,4 +26,18 @@ std::string ascii_tolower(const std::string& str) { return str; } +namespace { +bool is_ws(char c) { + return c == ' ' || c == '\t' || c == '\n' || c == '\r'; +} +} // namespace + +std::string trim(const std::string& str) { + auto start = str.begin(); + while (start != str.end() && is_ws(*start)) start++; + auto end = str.end() - 1; + while (end >= start && is_ws(*end)) end--; + return std::string(start, end + 1); +} + } // namespace stuff diff --git a/src/strutils.hh b/src/strutils.hh index 76120a8..1d0a3a3 100644 --- a/src/strutils.hh +++ b/src/strutils.hh @@ -7,6 +7,8 @@ namespace stuff { std::string ascii_tolower(const std::string& str); +std::string trim(const std::string& str); + } // namespace stuff #endif /* STRUTILS_HH */ -- cgit v1.2.3-70-g09d2