summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJoel Klinghed <the_jk@yahoo.com>2015-06-04 00:22:57 +0200
committerJoel Klinghed <the_jk@yahoo.com>2015-06-04 00:22:57 +0200
commita6dfc269d93cdf557f6dac62b03b886d694faecd (patch)
tree8f9c04a4200a99fc4e986f1b0b23808040580011
parent052a162715449252bb6126c41dd1700b1440c394 (diff)
Add config and change all commands to one
-rw-r--r--src/Makefile.am5
-rw-r--r--src/config.cc55
-rw-r--r--src/config.hh29
-rw-r--r--src/event_main.cc129
-rw-r--r--src/strutils.cc14
-rw-r--r--src/strutils.hh2
6 files changed, 191 insertions, 43 deletions
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 <fstream>
+#include <unordered_map>
+
+#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<std::string, std::string> 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<std::string, std::string> data_;
+};
+
+} // namespace
+
+std::unique_ptr<Config> Config::create() {
+ return std::unique_ptr<Config>(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 <memory>
+#include <string>
+
+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<Config> 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<Config> g_cfg;
std::shared_ptr<DB> open(const std::string& channel) {
std::string tmp = channel;
@@ -45,11 +46,13 @@ std::shared_ptr<DB> 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<std::string, std::string>& data) {
- std::vector<std::string> args;
- if (!parse(data["text"], &args)) return true;
+ std::map<std::string, std::string>& data,
+ std::vector<std::string>& 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<std::string, std::string>& data) {
- std::vector<std::string> args;
- if (!parse(data["text"], &args)) return true;
+ std::map<std::string, std::string>& data,
+ std::vector<std::string>& args) {
std::vector<unsigned long> 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<std::string, std::string>& data) {
+ std::map<std::string, std::string>& data,
+ std::vector<std::string>& args) {
std::set<std::string> update;
update.insert("name");
update.insert("start");
update.insert("text");
- std::vector<std::string> 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<std::string, std::string>& data) {
- std::vector<std::string> args;
- if (!parse(data["text"], &args)) return true;
+ std::map<std::string, std::string>& data,
+ std::vector<std::string>& args) {
std::vector<unsigned long> 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<std::string, std::string>& data,
+ std::vector<std::string>& args,
bool going) {
- std::vector<std::string> 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> event;
std::vector<unsigned long> indexes;
std::string note;
@@ -487,6 +485,46 @@ bool going(const std::string& channel,
return true;
}
+bool help(std::vector<std::string>& 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<std::string, std::string> 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<std::string> 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 */