summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--configure.ac5
-rw-r--r--src/Makefile.am3
-rw-r--r--src/sender.cc147
3 files changed, 133 insertions, 22 deletions
diff --git a/configure.ac b/configure.ac
index abdf761..5b49ee5 100644
--- a/configure.ac
+++ b/configure.ac
@@ -72,6 +72,11 @@ AC_SUBST(FASTCGI_CFLAGS)
AC_SUBST(FASTCGI_LIBS)
AC_DEFINE_UNQUOTED([HAVE_FASTCGI],[$have_fastcgi],[define to 1 if FastCGI is available])
+# Neon
+
+# 7.25 ???
+PKG_CHECK_MODULES([CURL],[libcurl >= 7.25.0])
+
# Finish up
AC_CONFIG_HEADERS([src/config.h])
diff --git a/src/Makefile.am b/src/Makefile.am
index 8ab164c..65c5858 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -11,7 +11,8 @@ event_SOURCES = event.cc event.hh event_main.cc common.hh cgi.hh db.hh \
event_LDADD = libdb.la libcgi.la
sender_SOURCES = common.hh sender.cc json.hh json.cc
-sender_LDADD = libutil.la
+sender_CPPFLAGS = $(AM_CPPFLAGS) @CURL_CFLAGS@
+sender_LDADD = libutil.la @CURL_LIBS@
libcgi_la_SOURCES = cgi.hh common.hh cgi.cc \
query_parser.hh query_parser.cc \
diff --git a/src/sender.cc b/src/sender.cc
index b9b6a36..e9b4a94 100644
--- a/src/sender.cc
+++ b/src/sender.cc
@@ -14,6 +14,8 @@
#include <unistd.h>
#include <vector>
+#include <curl/curl.h>
+
#include "config.hh"
#include "json.hh"
@@ -142,6 +144,48 @@ private:
std::string message_;
};
+size_t ignore_all(char* ptr, size_t size, size_t nmemb, void* userdata) {
+ return size * nmemb;
+}
+
+class Request {
+public:
+ Request(CURLM* multi, const std::string& url, const std::string& json)
+ : multi_(multi), headers_(nullptr) {
+ curl_ = curl_easy_init();
+ curl_easy_setopt(curl_, CURLOPT_URL, url.c_str());
+ curl_easy_setopt(curl_, CURLOPT_MAXREDIRS, 10L);
+ curl_easy_setopt(curl_, CURLOPT_FOLLOWLOCATION, 1L);
+ curl_easy_setopt(curl_, CURLOPT_POST, 1);
+ headers_ = curl_slist_append(headers_,
+ "Content-Type: application/json");
+ curl_easy_setopt(curl_, CURLOPT_HTTPHEADER, headers_);
+ curl_easy_setopt(curl_, CURLOPT_POSTFIELDSIZE, json.size());
+ curl_easy_setopt(curl_, CURLOPT_COPYPOSTFIELDS, json.data());
+
+ curl_easy_setopt(curl_, CURLOPT_WRITEFUNCTION, ignore_all);
+
+ curl_multi_add_handle(multi_, curl_);
+ }
+
+ ~Request() {
+ curl_multi_remove_handle(multi_, curl_);
+ curl_easy_cleanup(curl_);
+ curl_slist_free_all(headers_);
+ }
+
+ bool done() {
+ long status;
+ curl_easy_getinfo(curl_, CURLINFO_RESPONSE_CODE, &status);
+ return status != 0;
+ }
+
+private:
+ CURL* curl_;
+ CURLM* multi_;
+ struct curl_slist* headers_;
+};
+
bool g_quit;
void quit(int sig) {
@@ -154,6 +198,12 @@ struct Info {
std::string username;
std::string icon_url;
std::string icon_emoji;
+
+ std::string url;
+
+ CURLM* multi;
+
+ std::vector<Request> requests;
};
Info g_info;
@@ -169,7 +219,7 @@ void queue_message(const std::string& channel, const std::string& message) {
obj->put("channel", "#" + channel);
obj->put("text", message);
-
+ g_info.requests.emplace_back(g_info.multi, g_info.url, obj->str());
}
} // namespace
@@ -180,6 +230,11 @@ int main() {
cfg->load(SYSCONFDIR "/sender.config");
}
g_info.username = cfg->get("username", "stuff-sender-bot");
+ g_info.url = cfg->get("url", "");
+ if (g_info.url.empty()) {
+ std::cerr << "No url configured" << std::endl;
+ return EXIT_FAILURE;
+ }
g_info.icon_url = cfg->get("icon_url", "");
g_info.icon_emoji = cfg->get("icon_emoji", "");
auto const& listener = cfg->get("listener", "");
@@ -188,6 +243,19 @@ int main() {
return EXIT_FAILURE;
}
+ if (curl_global_init(CURL_GLOBAL_ALL)) {
+ std::cerr << "CURL failed to initialize" << std::endl;
+ return EXIT_FAILURE;
+ }
+ g_info.multi = curl_multi_init();
+ if (!g_info.multi) {
+ std::cerr << "CURL didn't initialize" << std::endl;
+ return EXIT_FAILURE;
+ }
+
+ std::vector<Client> clients;
+ int still_running;
+ int exitvalue;
int sock_ = -1;
size_t pos = listener.find(':');
if (pos != std::string::npos) {
@@ -201,7 +269,7 @@ int main() {
if (getaddrinfo(pos == 0 ? nullptr : listener.substr(0, pos).c_str(),
listener.substr(pos + 1).c_str(), &hints, &res)) {
std::cerr << "Invalid host or port in: " << listener << std::endl;
- return EXIT_FAILURE;
+ goto error;
}
for (auto ptr = res; ptr; ptr = ptr->ai_next) {
sock_ = socket(ptr->ai_family, ptr->ai_socktype,
@@ -217,7 +285,7 @@ int main() {
freeaddrinfo(res);
if (sock_ == -1) {
std::cerr << "Unable to bind: " << listener << std::endl;
- return EXIT_FAILURE;
+ goto error;
}
} else {
// socket
@@ -225,7 +293,7 @@ int main() {
if (sock_ == -1) {
std::cerr << "Unable to create local socket: " << strerror(errno)
<< std::endl;
- return EXIT_FAILURE;
+ goto error;
}
struct sockaddr_un name;
name.sun_family = AF_LOCAL;
@@ -234,47 +302,74 @@ int main() {
if (bind(sock_, reinterpret_cast<struct sockaddr*>(&name),
SUN_LEN(&name))) {
std::cerr << "Bind failed: " << strerror(errno) << std::endl;
- close(sock_);
- sock_ = 1;
- return EXIT_FAILURE;
+ goto error;
}
}
if (listen(sock_, 10)) {
std::cerr << "Listen failed: " << strerror(errno) << std::endl;
- close(sock_);
- return EXIT_FAILURE;
+ goto error;
}
- int value = 1;
- setsockopt(sock_, SOL_SOCKET, SO_REUSEPORT, &value, sizeof(value));
+ {
+ int value = 1;
+ setsockopt(sock_, SOL_SOCKET, SO_REUSEPORT, &value, sizeof(value));
+ }
signal(SIGPIPE, SIG_IGN);
signal(SIGINT, quit);
signal(SIGTERM, quit);
- std::vector<Client> clients;
-
while (!g_quit) {
+ curl_multi_perform(g_info.multi, &still_running);
+
+ for (auto it = g_info.requests.begin(); it != g_info.requests.end();) {
+ if (it->done()) {
+ it = g_info.requests.erase(it);
+ } else {
+ ++it;
+ }
+ }
+
fd_set read_set;
- auto max = sock_ + 1;
+ fd_set write_set;
+ fd_set err_set;
+ int max = -1;
FD_ZERO(&read_set);
+ FD_ZERO(&write_set);
+ FD_ZERO(&err_set);
+
+ long timeout = -1;
+ struct timeval *to = nullptr;
+ struct timeval _to;
+ curl_multi_timeout(g_info.multi, &timeout);
+ if (timeout >= 0) {
+ _to.tv_sec = timeout / 1000;
+ _to.tv_usec = (timeout % 1000) * 1000;
+ to = &_to;
+ }
+ if (curl_multi_fdset(g_info.multi, &read_set, &write_set, &err_set,
+ &max) != CURLM_OK) {
+ std::cerr << "curl_multi_fdset failed" << std::endl;
+ goto error;
+ }
+
+ max = std::max(max, sock_);
FD_SET(sock_, &read_set);
for (auto it = clients.begin(); it != clients.end();) {
if (it->sock() == -1) {
it = clients.erase(it);
} else {
- max = std::max(max, it->sock() + 1);
+ max = std::max(max, it->sock());
FD_SET(it->sock(), &read_set);
++it;
}
}
- auto ret = select(max, &read_set, nullptr, nullptr, nullptr);
+ auto ret = select(max + 1, &read_set, &write_set, &err_set, to);
if (ret < 0) {
if (errno == EINTR) continue;
std::cerr << "Select failed: " << strerror(errno);
- close(sock_);
- return EXIT_FAILURE;
+ goto error;
}
for (auto it = clients.begin(); ret > 0 && it != clients.end();) {
if (FD_ISSET(it->sock(), &read_set)) {
@@ -295,8 +390,7 @@ int main() {
if (errno == EINTR) continue;
if (errno == EWOULDBLOCK || errno == EAGAIN) continue;
std::cerr << "Accept failed: " << strerror(errno);
- close(sock_);
- return EXIT_FAILURE;
+ goto error;
}
if (clients.size() == MAX_CLIENTS) {
// Remove oldest
@@ -305,6 +399,17 @@ int main() {
clients.emplace_back(sock);
}
}
+
+ exitvalue = EXIT_SUCCESS;
+ goto end;
+ error:
+ exitvalue = EXIT_FAILURE;
+
+ end:
+ clients.clear();
+ g_info.requests.clear();
+ curl_multi_cleanup(g_info.multi);
+ curl_global_cleanup();
close(sock_);
- return EXIT_SUCCESS;
+ return exitvalue;
}