From d07c27135d0ce31ca88ca05caf93881aed92e820 Mon Sep 17 00:00:00 2001 From: Neale Pickett Date: Thu, 12 Jun 2014 21:56:33 -0600 Subject: [PATCH] Add a slack.com CGI front-end --- Makefile | 4 +- src/cgi.c | 189 ++++++++++++++++++++++++++++++++++++++++++++++++ src/cgi.h | 10 +++ src/slack.cgi.c | 95 ++++++++++++++++++++++++ 4 files changed, 297 insertions(+), 1 deletion(-) create mode 100644 src/cgi.c create mode 100644 src/cgi.h create mode 100644 src/slack.cgi.c diff --git a/Makefile b/Makefile index c235a5b..2a30103 100644 --- a/Makefile +++ b/Makefile @@ -1,5 +1,5 @@ CFLAGS = -Wall -Werror -TARGETS = bot factoids +TARGETS = bot factoids slack.cgi all: $(TARGETS) @@ -9,6 +9,8 @@ all: $(TARGETS) src/bot: src/factoids: src/factoids.o src/cdb.o src/cdbmake.o +src/slack.cgi: src/slack.cgi.o src/cgi.o + .PHONY: clean clean: rm -f $(TARGETS) $(addprefix src/, $(TARGETS)) src/*.o diff --git a/src/cgi.c b/src/cgi.c new file mode 100644 index 0000000..34fe2c2 --- /dev/null +++ b/src/cgi.c @@ -0,0 +1,189 @@ +/* + * This software has been authored by an employee or employees of Los + * Alamos National Security, LLC, operator of the Los Alamos National + * Laboratory (LANL) under Contract No. DE-AC52-06NA25396 with the U.S. + * Department of Energy. The U.S. Government has rights to use, + * reproduce, and distribute this software. The public may copy, + * distribute, prepare derivative works and publicly display this + * software without charge, provided that this Notice and any statement + * of authorship are reproduced on all copies. Neither the Government + * nor LANS makes any warranty, express or implied, or assumes any + * liability or responsibility for the use of this software. If + * software is modified to produce derivative works, such modified + * software should be clearly marked, so as not to confuse it with the + * version available from LANL. + */ + +#include +#include +#include + +#include "cgi.h" + +#define POST_MAX 4000 + +static int is_cgi = 0; +static char **argv = NULL; + +static int +read_char_argv() +{ + static int arg = 0; + static char *p; + + if (NULL == argv) { + return EOF; + } + + if (0 == arg) { + arg = 1; + p = argv[1]; + } + + if (! p) { + return EOF; + } else if (! *p) { + arg += 1; + p = argv[arg]; + return '&'; + } + + return *(p++); +} + +static int +read_char_stdin() +{ + static long inlen = -1; + + if (-1 == inlen) { + char *p = getenv("CONTENT_LENGTH"); + if (p) { + inlen = atoi(p); + if (inlen > POST_MAX) { + inlen = POST_MAX; + } + if (inlen < 0) { + inlen = 0; + } + } else { + inlen = 0; + } + } + + if (inlen) { + inlen -= 1; + return getchar(); + } + return EOF; +} + +static int +read_char_query_string() +{ + static char *p = (char *)-1; + + if ((char *)-1 == p) { + p = getenv("QUERY_STRING"); + } + + if (! p) { + return EOF; + } else if (! *p) { + return EOF; + } else { + return *(p++); + } +} + +static int (* read_char)() = read_char_argv; + +int +cgi_init(char *global_argv[]) +{ + char *rm = getenv("REQUEST_METHOD"); + + if (! rm) { + read_char = read_char_argv; + argv = global_argv; + } else if (0 == strcmp(rm, "POST")) { + read_char = read_char_stdin; + is_cgi = 1; + } else if (0 == strcmp(rm, "GET")) { + read_char = read_char_query_string; + is_cgi = 1; + } else { + printf(("405 Method not allowed\r\n" + "Allow: GET, POST\r\n" + "Content-type: text/plain\r\n" + "\r\n" + "%s is not allowed.\n"), + rm); + return -1; + } + + return 0; +} + +static char +tonum(int c) +{ + if ((c >= '0') && (c <= '9')) { + return c - '0'; + } + if ((c >= 'a') && (c <= 'f')) { + return 10 + c - 'a'; + } + if ((c >= 'A') && (c <= 'F')) { + return 10 + c - 'A'; + } + return 0; +} + +static char +read_hex() +{ + int a = read_char(); + int b = read_char(); + + return tonum(a)*16 + tonum(b); +} + +/* Read a key or a value. Since & and = aren't supposed to appear + outside of boundaries, we can use the same function for both. +*/ +size_t +cgi_item(char *str, size_t maxlen) +{ + int c; + size_t pos = 0; + + while (1) { + c = read_char(); + switch (c) { + case EOF: + case '=': + case '&': + str[pos] = '\0'; + return pos; + case '%': + c = read_hex(); + break; + case '+': + c = ' '; + break; + } + if (pos < maxlen - 1) { + str[pos] = c; + pos += 1; + } + } +} + +void +cgi_header(char *content_type) +{ + if (is_cgi) { + printf("Content-type: %s\r\n\r\n", content_type); + } +} diff --git a/src/cgi.h b/src/cgi.h new file mode 100644 index 0000000..d0a5496 --- /dev/null +++ b/src/cgi.h @@ -0,0 +1,10 @@ +#ifndef __CGI_H_ +#define __CGI_H_ + +#include + +int cgi_init(char *global_argv[]); +size_t cgi_item(char *str, size_t maxlen); +void cgi_header(char *content_type); + +#endif diff --git a/src/slack.cgi.c b/src/slack.cgi.c new file mode 100644 index 0000000..5eb5083 --- /dev/null +++ b/src/slack.cgi.c @@ -0,0 +1,95 @@ +#include +#include +#include +#include // only for chdir +#include + +#include "cgi.h" + +void +jputchar(char c) +{ + if (c == '\n') { + printf("\\n"); + } else if (c < 0x20) { + printf("\%04x", c); + } else if ((c == '\\') || (c == '"')) { + putchar('\\'); + putchar(c); + } else { + putchar(c); + } +} + +int +main(int argc, char *argv[]) +{ + char key[80]; + char val[2000]; + + bool its_me = false; + + cgi_init(argv); + + for (;;) { + size_t len; + + len = cgi_item(key, sizeof(key)); + len = cgi_item(val, sizeof(val)); + + if (0 == len) { + break; + } + + if (0 == strcmp(key, "user_id")) { + if (0 == strcmp(val, "USLACKBOT")) { + its_me = true; + } + } else if (0 == strcmp(key, "channel_name")) { + char chan[40]; + + snprintf(chan, sizeof(chan), "#%s", val); + setenv("forum", chan, true); + } else if (0 == strcmp(key, "user_name")) { + setenv("sender", val, true); + } else if (0 == strcmp(key, "text")) { + setenv("text", val, true); + } + } + + cgi_header("text/json"); + + if (its_me) { + printf("{}"); + return 0; + } + + setenv("command", "PRIVMSG", true); + + chdir("/home/neale/bot/zinc"); + { + FILE *p = popen("./handler", "r"); + int newlines = 0; + + printf("{\"text\": \""); + for (;;) { + int c = fgetc(p); + + if (EOF == c) { + break; + } else if ('\n' == c) { + newlines += 1; + } else { + for (; newlines > 0; newlines -= 1) { + jputchar('\n'); + } + jputchar(c); + } + } + printf("\"}\n"); + + pclose(p); + } + + return 0; +}