summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorJoel Klinghed <the_jk@yahoo.com>2015-06-25 02:13:15 +0200
committerJoel Klinghed <the_jk@yahoo.com>2015-06-25 02:13:15 +0200
commit0c55606145b6c5d9a303b19b3dba4996ec3ed3a9 (patch)
treeff7804d99c83a0c22e4074cd11624c93365057ff /src
parent021dbcb976f9534750e9dc42812f1a66f47a6392 (diff)
Split out common event code for future use
Diffstat (limited to 'src')
-rw-r--r--src/.gitignore2
-rw-r--r--src/Makefile.am10
-rw-r--r--src/event_main.cc182
-rw-r--r--src/event_utils.cc199
-rw-r--r--src/event_utils.hh58
5 files changed, 309 insertions, 142 deletions
diff --git a/src/.gitignore b/src/.gitignore
index 13d1e3c..bda1427 100644
--- a/src/.gitignore
+++ b/src/.gitignore
@@ -4,9 +4,11 @@
/stamp-h1
/libcgi.la
/libdb.la
+/libevent.la
/libjson.la
/libutil.la
/libsender_client.la
/event
+/page
/sender
/test-sender
diff --git a/src/Makefile.am b/src/Makefile.am
index 1f8b819..8a6ee24 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -6,10 +6,10 @@ AM_CPPFLAGS = @DEFINES@ -DLOCALSTATEDIR='"@localstatedir@/stuff"' \
bin_PROGRAMS = event sender
noinst_PROGRAMS = test-sender
noinst_LTLIBRARIES = libdb.la libcgi.la libutil.la libsender_client.la \
- libjson.la
+ libjson.la libevent.la
-event_SOURCES = event.cc event.hh event_main.cc common.hh cgi.hh db.hh
-event_LDADD = libdb.la libcgi.la libsender_client.la
+event_SOURCES = event_main.cc common.hh cgi.hh
+event_LDADD = libcgi.la libevent.la
sender_SOURCES = common.hh sender.cc
sender_CPPFLAGS = $(AM_CPPFLAGS) @CURL_CFLAGS@
@@ -18,6 +18,10 @@ sender_LDADD = libjson.la libutil.la @CURL_LIBS@
test_sender_SOURCES = common.hh test_sender.cc sender_client.hh
test_sender_LDADD = libsender_client.la
+libevent_la_SOURCES = event.cc event.hh event_utils.cc event_utils.hh \
+ common.hh db.hh sender_client.hh
+libevent_la_LIBADD = libdb.la libsender_client.la
+
libjson_la_SOURCES = json.hh json.cc common.hh
libcgi_la_SOURCES = cgi.hh common.hh cgi.cc \
diff --git a/src/event_main.cc b/src/event_main.cc
index 093ed76..c7c867e 100644
--- a/src/event_main.cc
+++ b/src/event_main.cc
@@ -1,7 +1,6 @@
#include "common.hh"
#include <algorithm>
-#include <functional>
#include <iostream>
#include <memory>
#include <set>
@@ -14,10 +13,9 @@
#include "config.hh"
#include "db.hh"
#include "event.hh"
-#include "fsutils.hh"
+#include "event_utils.hh"
#include "http.hh"
#include "sender_client.hh"
-#include "sqlite3_db.hh"
/*
token=gIkuvaNzQIHg97ATvDxqgjtO
@@ -38,82 +36,12 @@ namespace {
std::unique_ptr<Config> g_cfg;
std::unique_ptr<SenderClient> g_sender;
-std::shared_ptr<DB> open(const std::string& channel) {
- std::string tmp = channel;
- for (auto it = tmp.begin(); it != tmp.end(); ++it) {
- if (!((*it >= 'a' && *it <= 'z') ||
- (*it >= 'A' && *it <= 'Z') ||
- (*it >= '0' && *it <= '9') ||
- *it == '-' || *it == '_' || *it == '.')) {
- *it = '.';
- }
- }
- 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(path + "/" + tmp + ".db");
- if (!db || db->bad()) {
- Http::response(200, "Unable to open database");
- db.reset();
- } else if (!Event::setup(db.get())) {
- Http::response(200, "Unable to setup database");
- db.reset();
- }
- return std::move(db);
-}
-
bool parse(const std::string& text, std::vector<std::string>* args) {
if (Args::parse(text, args)) return true;
Http::response(200, "Invalid parameter format");
return false;
}
-const double ONE_DAY_IN_SEC = 24.0 * 60.0 * 60.0;
-const double ONE_WEEK_IN_SEC = ONE_DAY_IN_SEC * 7.0;
-// It's OK that we ignore leap years here
-const double ONE_YEAR_IN_SEC = 365 * ONE_DAY_IN_SEC;
-
-std::string format_date(time_t date) {
- time_t now = time(NULL);
- struct tm _t;
- struct tm* t = localtime_r(&date, &_t);
- double diff = difftime(date, now);
- char tmp[100];
- if (diff <= ONE_DAY_IN_SEC) {
- // Same day, just show time
- strftime(tmp, sizeof(tmp), "%H:%M", t);
- } else if (diff <= ONE_WEEK_IN_SEC) {
- // Inside a week, show day and time
- strftime(tmp, sizeof(tmp), "%A %H:%M", t);
- } else if (diff <= ONE_YEAR_IN_SEC / 2.0) {
- // Inside a year, show date, day and time
- strftime(tmp, sizeof(tmp), "%A %d/%m %H:%M", t);
- } else {
- strftime(tmp, sizeof(tmp), "%Y-%m-%d %H:%M", t);
- }
- return tmp;
-}
-
-void signal_channel(const std::string& channel, const std::string& str) {
- if (!g_sender) return;
- g_sender->send(channel, str);
-}
-
-void signal_event(const std::string& channel,
- const std::unique_ptr<Event>& event) {
- std::ostringstream ss;
- ss << event->name() << " @ " << format_date(event->start()) << std::endl;
- if (!event->text().empty()) {
- ss << event->text() << std::endl;
- }
- ss << std::endl;
- ss << "Use /event going to join the event" << std::endl;
- signal_channel(channel, ss.str());
-}
-
bool parse_time(const std::string& value, time_t* date) {
struct tm _t, _tmp;
time_t now = time(NULL);
@@ -129,7 +57,7 @@ bool parse_time(const std::string& value, time_t* date) {
if (days <= 0) {
days += 7;
}
- time_t tmp = mktime(&_tmp) + days * ONE_DAY_IN_SEC;
+ time_t tmp = mktime(&_tmp) + days * EventUtils::ONE_DAY_IN_SEC;
localtime_r(&tmp, &_tmp);
goto done;
}
@@ -148,7 +76,7 @@ bool parse_time(const std::string& value, time_t* date) {
return true;
}
-bool create(const std::string& channel,
+bool create(EventUtils* utils,
std::map<std::string, std::string>& data,
std::vector<std::string>& args) {
if (args.size() < 2) {
@@ -171,9 +99,8 @@ bool create(const std::string& channel,
text.append(args.front());
args.erase(args.begin());
}
- auto db = open(channel);
- if (!db) return true;
- auto event = Event::create(db, name, start);
+ auto event = utils->create(name, start);
+ if (!utils->good()) return true;
if (!event) {
Http::response(200, "Unable to create event");
return true;
@@ -192,10 +119,7 @@ bool create(const std::string& channel,
return true;
}
Http::response(200, "Event created");
- auto next_event = Event::next(db);
- if (next_event->id() == event->id()) {
- signal_event(channel, next_event);
- }
+ utils->created(event.get());
return true;
}
@@ -215,7 +139,7 @@ bool append_indexes(Iterator begin, Iterator end,
return true;
}
-bool cancel(const std::string& channel,
+bool cancel(EventUtils* utils,
std::map<std::string, std::string>& data,
std::vector<std::string>& args) {
std::vector<unsigned long> indexes;
@@ -229,9 +153,8 @@ bool cancel(const std::string& channel,
std::greater<unsigned long>());
std::unique(indexes.begin(), indexes.end());
}
- auto db = open(channel);
- if (!db) return true;
- auto events = Event::all(db);
+ auto events = utils->all();
+ if (!utils->good()) return true;
if (indexes.front() >= events.size()) {
if (events.empty()) {
Http::response(200, "There are no events");
@@ -244,28 +167,18 @@ bool cancel(const std::string& channel,
}
return true;
}
- std::string signal;
for (const auto& index : indexes) {
- if (index == 0) {
- std::ostringstream ss;
- ss << "Event canceled: " << events[index]->name() << " @ "
- << format_date(events[index]->start());
- signal = ss.str();
- }
- events[index]->remove();
+ utils->cancel(events[index].get(), index);
}
if (indexes.size() > 1) {
Http::response(200, "Events removed");
} else {
Http::response(200, "Event removed");
}
- if (!signal.empty()) {
- signal_channel(channel, signal);
- }
return true;
}
-bool update(const std::string& channel,
+bool update(EventUtils* utils,
std::map<std::string, std::string>& data,
std::vector<std::string>& args) {
std::set<std::string> update;
@@ -276,32 +189,34 @@ bool update(const std::string& channel,
Http::response(200, "Usage: update [INDEX] [name NAME] [start START] [text TEXT]");
return true;
}
- auto db = open(channel);
- if (!db) return true;
std::unique_ptr<Event> event;
+ int64_t first_event;
auto it = args.begin();
if (update.count(*it) == 0) {
std::vector<unsigned long> indexes;
if (!append_indexes(args.begin(), ++it, &indexes)) {
return true;
}
- auto events = Event::all(db);
+ auto events = utils->all();
+ if (!utils->good()) return true;
if (indexes.front() >= events.size()) {
std::ostringstream ss;
ss << "No such event: " << indexes.front() << std::endl;
Http::response(200, ss.str());
return true;
}
+ first_event = events.front()->id();
event.swap(events[indexes.front()]);
++it;
} else {
- event = Event::next(db);
+ event = utils->next();
+ if (!utils->good()) return true;
if (!event) {
Http::response(200, "No event to update");
return true;
}
+ first_event = event->id();
}
- auto first_event = Event::next(db)->id();
while (it != args.end()) {
if (*it == "name") {
if (++it == args.end()) {
@@ -344,18 +259,14 @@ bool update(const std::string& channel,
}
if (event->store()) {
Http::response(200, "Event updated");
- auto next_event = Event::next(db);
- if (next_event->id() != first_event ||
- next_event->id() == event->id()) {
- signal_event(channel, next_event);
- }
+ utils->updated(event.get(), first_event);
} else {
Http::response(200, "Update failed");
}
return true;
}
-bool show(const std::string& channel,
+bool show(EventUtils* utils,
std::map<std::string, std::string>& data,
std::vector<std::string>& args) {
std::vector<unsigned long> indexes;
@@ -366,9 +277,8 @@ bool show(const std::string& channel,
return true;
}
}
- auto db = open(channel);
- if (!db) return true;
- auto events = Event::all(db);
+ auto events = utils->all();
+ if (!utils->good()) return true;
std::ostringstream ss;
for (const auto& index : indexes) {
if (indexes.size() > 1) {
@@ -382,7 +292,7 @@ bool show(const std::string& channel,
}
} else {
ss << events[index]->name() << " @ "
- << format_date(events[index]->start()) << std::endl;
+ << EventUtils::format_date(events[index]->start()) << std::endl;
const auto& text = events[index]->text();
if (!text.empty()) {
ss << text << std::endl;
@@ -412,7 +322,7 @@ bool show(const std::string& channel,
return true;
}
-bool going(const std::string& channel,
+bool going(EventUtils* utils,
std::map<std::string, std::string>& data,
std::vector<std::string>& args,
bool going) {
@@ -424,8 +334,6 @@ bool going(const std::string& channel,
std::unique_ptr<Event> event;
std::vector<unsigned long> indexes;
std::string note, user = user_name;
- auto db = open(channel);
- if (!db) return true;
if (!args.empty() && args.front() == "user") {
if (args.size() == 1) {
Http::response(200, "Expected username after 'user'");
@@ -435,7 +343,8 @@ bool going(const std::string& channel,
args.erase(args.begin(), args.begin() + 2);
}
if (args.empty()) {
- event = Event::next(db);
+ event = utils->next();
+ if (!utils->good()) return true;
} else {
if (args.size() == 1) {
char* end = nullptr;
@@ -446,7 +355,8 @@ bool going(const std::string& channel,
}
if (indexes.empty()) {
- event = Event::next(db);
+ event = utils->next();
+ if (!utils->good()) return true;
note = args.front();
}
} else {
@@ -462,7 +372,8 @@ bool going(const std::string& channel,
}
}
if (!event) {
- auto events = Event::all(db);
+ auto events = utils->all();
+ if (!utils->good()) return true;
if (events.empty()) {
Http::response(200, "There are no events to attend");
return true;
@@ -478,20 +389,7 @@ bool going(const std::string& channel,
event->update_going(user, going, note);
if (event->store()) {
Http::response(200, "Your wish have been recorded, if not granted");
- auto next_event = Event::next(db);
- if (next_event->id() == event->id()) {
- std::string extra;
- if (user != user_name) {
- extra = " says " + user_name;
- }
- if (going) {
- signal_channel(channel, user + " will be attending " +
- event->name() + extra);
- } else {
- signal_channel(channel, user + " will not be attending " +
- event->name() + extra);
- }
- }
+ utils->going(event.get(), going, user, user_name);
} else {
Http::response(200, "Event store failed");
}
@@ -541,6 +439,10 @@ bool help(std::vector<std::string>& args) {
return true;
}
+void error_response(const std::string& message) {
+ Http::response(200, message);
+}
+
bool handle_request(CGI* cgi) {
switch (cgi->request_type()) {
case CGI::GET:
@@ -575,23 +477,25 @@ bool handle_request(CGI* cgi) {
}
auto command = args.front();
args.erase(args.begin());
+ auto utils = EventUtils::create(channel, error_response, g_cfg.get(),
+ g_sender.get());
if (command == "create") {
- return create(channel, data, args);
+ return create(utils.get(), data, args);
}
if (command == "cancel") {
- return cancel(channel, data, args);
+ return cancel(utils.get(), data, args);
}
if (command == "update") {
- return update(channel, data, args);
+ return update(utils.get(), data, args);
}
if (command == "show") {
- return show(channel, data, args);
+ return show(utils.get(), data, args);
}
if (command == "going") {
- return going(channel, data, args, true);
+ return going(utils.get(), data, args, true);
}
if (command == "!going") {
- return going(channel, data, args, false);
+ return going(utils.get(), data, args, false);
}
if (command == "help") {
return help(args);
diff --git a/src/event_utils.cc b/src/event_utils.cc
new file mode 100644
index 0000000..403d10f
--- /dev/null
+++ b/src/event_utils.cc
@@ -0,0 +1,199 @@
+#include "common.hh"
+
+#include <sstream>
+
+#include "config.hh"
+#include "event.hh"
+#include "event_utils.hh"
+#include "fsutils.hh"
+#include "sender_client.hh"
+#include "sqlite3_db.hh"
+
+namespace stuff {
+
+namespace {
+
+class EventUtilsImpl : public EventUtils {
+public:
+ EventUtilsImpl(const std::string& channel,
+ std::function<void(const std::string&)> error_cb,
+ Config* config, SenderClient* sender)
+ : channel_(channel), error_cb_(error_cb), cfg_(config),
+ sender_(sender) {
+ }
+
+ std::unique_ptr<Event> create(
+ const std::string& name, time_t start) override {
+ if (!db_ && !open()) return nullptr;
+ return Event::create(db_, name, start);
+ }
+
+ void created(Event* event) override {
+ if (!event) return;
+ auto next_event = Event::next(db_);
+ if (next_event->id() == event->id()) {
+ signal_event(next_event);
+ }
+ }
+
+ std::vector<std::unique_ptr<Event>> all() override {
+ if (!db_ && !open()) return std::vector<std::unique_ptr<Event>>();
+ return Event::all(db_);
+ }
+
+ std::unique_ptr<Event> next() override {
+ if (!db_ && !open()) return nullptr;
+ return Event::next(db_);
+ }
+
+ bool good() const override {
+ return db_.get() != nullptr;
+ }
+
+ void cancel(Event* event, size_t index) override {
+ if (!event) return;
+ event->remove();
+ if (index == 0) {
+ std::ostringstream ss;
+ ss << "Event canceled: " << event->name() << " @ "
+ << format_date(event->start());
+ signal_channel(ss.str());
+ }
+ }
+
+ void updated(Event* event, int64_t was_first) override {
+ auto next_event = Event::next(db_);
+ if (next_event->id() != was_first ||
+ next_event->id() == event->id()) {
+ signal_event(next_event);
+ }
+ }
+
+ void going(Event* event, bool going, const std::string& user,
+ const std::string& owner) override {
+ auto next_event = Event::next(db_);
+ if (next_event->id() == event->id()) {
+ std::string extra;
+ if (user != owner) {
+ extra = " says " + owner;
+ }
+ if (going) {
+ signal_channel(user + " will be attending " +
+ event->name() + extra);
+ } else {
+ signal_channel(user + " will not be attending " +
+ event->name() + extra);
+ }
+ }
+ }
+
+ const std::string& channel() const override {
+ return channel_;
+ }
+
+private:
+ void signal_event(const std::unique_ptr<Event>& event) {
+ std::ostringstream ss;
+ ss << event->name() << " @ " << format_date(event->start()) << std::endl;
+ if (!event->text().empty()) {
+ ss << event->text() << std::endl;
+ }
+ ss << std::endl;
+ ss << "Use /event going to join the event" << std::endl;
+ signal_channel(ss.str());
+ }
+
+ void signal_channel(const std::string& str) {
+ if (!sender_) return;
+ sender_->send(channel_, str);
+ }
+
+ void error(const std::string& message) {
+ if (error_cb_) {
+ error_cb_(message);
+ }
+ }
+
+ bool open() {
+ std::string tmp = channel_;
+ for (auto it = tmp.begin(); it != tmp.end(); ++it) {
+ if (!((*it >= 'a' && *it <= 'z') ||
+ (*it >= 'A' && *it <= 'Z') ||
+ (*it >= '0' && *it <= '9') ||
+ *it == '-' || *it == '_' || *it == '.')) {
+ *it = '.';
+ }
+ }
+ std::string path;
+ if (cfg_) path = cfg_->get("db_path", LOCALSTATEDIR);
+ if (path.empty()) path = ".";
+ if (!mkdir_p(path)) {
+ error("Unable to create database directory");
+ return false;
+ }
+ auto db = SQLite3::open(path + "/" + tmp + ".db");
+ if (!db || db->bad()) {
+ error("Unable to open database");
+ return false;
+ } else if (!Event::setup(db.get())) {
+ error("Unable to setup database");
+ return false;
+ }
+ db_ = std::move(db);
+ return true;
+ }
+
+ std::string channel_;
+ std::function<void(const std::string&)> error_cb_;
+ std::shared_ptr<DB> db_;
+ Config* cfg_;
+ SenderClient* sender_;
+};
+
+} // namespace
+
+EventUtils::EventUtils() {
+}
+
+EventUtils::~EventUtils() {
+}
+
+std::unique_ptr<EventUtils> EventUtils::create(
+ const std::string& channel,
+ std::function<void(const std::string&)> error_cb,
+ Config* config,
+ SenderClient* sender) {
+ std::unique_ptr<EventUtils> utils(
+ new EventUtilsImpl(channel, error_cb, config, sender));
+ return utils;
+}
+
+const double EventUtils::ONE_DAY_IN_SEC = 24.0 * 60.0 * 60.0;
+const double EventUtils::ONE_WEEK_IN_SEC = ONE_DAY_IN_SEC * 7.0;
+// It's OK that we ignore leap years here
+const double EventUtils::ONE_YEAR_IN_SEC = 365 * ONE_DAY_IN_SEC;
+
+std::string EventUtils::format_date(time_t date) {
+ time_t now = time(NULL);
+ struct tm _t;
+ struct tm* t = localtime_r(&date, &_t);
+ double diff = difftime(date, now);
+ char tmp[100];
+ if (diff <= ONE_DAY_IN_SEC) {
+ // Same day, just show time
+ strftime(tmp, sizeof(tmp), "%H:%M", t);
+ } else if (diff <= ONE_WEEK_IN_SEC) {
+ // Inside a week, show day and time
+ strftime(tmp, sizeof(tmp), "%A %H:%M", t);
+ } else if (diff <= ONE_YEAR_IN_SEC / 2.0) {
+ // Inside a year, show date, day and time
+ strftime(tmp, sizeof(tmp), "%A %d/%m %H:%M", t);
+ } else {
+ strftime(tmp, sizeof(tmp), "%Y-%m-%d %H:%M", t);
+ }
+ return tmp;
+}
+
+} // namespace stuff
+
+
diff --git a/src/event_utils.hh b/src/event_utils.hh
new file mode 100644
index 0000000..8151d64
--- /dev/null
+++ b/src/event_utils.hh
@@ -0,0 +1,58 @@
+#ifndef EVENT_UTILS_HH
+#define EVENT_UTILS_HH
+
+#include <functional>
+#include <memory>
+#include <string>
+
+namespace stuff {
+
+class Config;
+class Event;
+class SenderClient;
+
+class EventUtils {
+public:
+ virtual ~EventUtils();
+
+ virtual std::unique_ptr<Event> create(
+ const std::string& name, time_t start) = 0;
+ virtual void created(Event* event) = 0;
+
+ virtual std::vector<std::unique_ptr<Event>> all() = 0;
+ virtual std::unique_ptr<Event> next() = 0;
+
+ virtual bool good() const = 0;
+
+ virtual void cancel(Event* event, size_t index) = 0;
+
+ virtual void updated(Event* event, int64_t was_first) = 0;
+
+ virtual void going(Event* event, bool going, const std::string& user,
+ const std::string& owner) = 0;
+
+ virtual const std::string& channel() const = 0;
+
+ static std::unique_ptr<EventUtils> create(
+ const std::string& channel,
+ std::function<void(const std::string&)> error_cb,
+ Config* config,
+ SenderClient* sender);
+
+ static const double ONE_DAY_IN_SEC;
+ static const double ONE_WEEK_IN_SEC;
+ static const double ONE_YEAR_IN_SEC;
+
+ static std::string format_date(time_t date);
+
+protected:
+ EventUtils();
+
+private:
+ EventUtils(const EventUtils&) = delete;
+ EventUtils& operator=(const EventUtils&) = delete;
+};
+
+} // namespace stuff
+
+#endif /* EVENT_UTILS_HH */