/* Monolith chatbot API. * - by Richard W.M. Jones * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Library General Public License for more details. * * You should have received a copy of the GNU Library General Public * License along with this library; if not, write to the Free * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. * * $Id: chatbot.c,v 1.5 2003/02/22 15:34:27 rich Exp $ */ #include "config.h" #include #include #ifdef HAVE_STRING_H #include #endif #include #include #include #include #include #include #include #include "chatroom.h" /* Define some RFC-compliant dates to represent past and future. */ #define DISTANT_PAST "Thu, 01 Dec 1994 16:00:00 GMT" #define DISTANT_FUTURE "Sun, 01 Dec 2030 16:00:00 GMT" /* Headers which are sent to defeat caches. */ #define NO_CACHE_HEADERS "Cache-Control", "must-revalidate", "Expires", DISTANT_PAST, "Pragma", "no-cache" #define CRLF "\r\n" static int bad_request_error (pool pool, http_request http_request, io_handle io, const char *text); static vector parse_rooms_param (pool pool, const char *rooms); static int do_rooms (pool pool, http_request http_request, io_handle io, cgi cgi); static int do_wait (pool pool, http_request http_request, io_handle io, cgi cgi); static int do_post (pool pool, http_request http_request, io_handle io, cgi cgi); static const char *conninfo = 0; /* Please see the file README.chatbot-api for the full API. */ int handle_request (rws_request rq) { pool pool = pth_get_pool (current_pth); http_request http_request = rws_request_http_request (rq); io_handle io = rws_request_io (rq); cgi cgi; const char *fn; int close; /* Get the conninfo string. */ if (!conninfo) conninfo = rws_request_cfg_get_string (rq, "chatbot database", ""); /* Parse CGI parameters. */ cgi = new_cgi (pool, http_request, io); /* What function was selected? */ fn = cgi_param (cgi, "fn"); if (!fn) return bad_request_error (pool, http_request, io, "fn parameter is missing"); if (strcasecmp (fn, "rooms") == 0) close = do_rooms (pool, http_request, io, cgi); else if (strcasecmp (fn, "wait") == 0) close = do_wait (pool, http_request, io, cgi); else if (strcasecmp (fn, "post") == 0) close = do_post (pool, http_request, io, cgi); else close = bad_request_error (pool, http_request, io, "unknown fn parameter"); return close; } static int do_rooms (pool pool, http_request http_request, io_handle io, cgi cgi) { db_handle dbh; st_handle sth; int resid; const char *name; http_response http_response; int close; /* XXX Future: access control. */ dbh = get_db_handle (conninfo, DBI_THROW_ERRORS); sth = st_prepare_cached (dbh, "select c.resid, r.name from ml_chat_rooms c, ml_resources r " " where c.resid = r.resid " " order by 1"); st_execute (sth); st_bind (sth, 0, resid, DBI_INT); st_bind (sth, 1, name, DBI_STRING); http_response = new_http_response (pool, http_request, io, 200, "OK"); http_response_send_headers (http_response, /* Content type. */ "Content-Type", "text/plain", NO_CACHE_HEADERS, /* End of headers. */ NULL); close = http_response_end_headers (http_response); if (http_request_is_HEAD (http_request)) return close; while (st_fetch (sth)) io_fprintf (io, "%d 0 %s" CRLF, resid, name); /* XXX msgid */ return close; } static int do_wait (pool pool, http_request http_request, io_handle io, cgi cgi) { const char *rooms, *msgids; vector chatrooms; hash next_msgids; message msg; chatroom room; http_response http_response; int i, resid, next_msgid, close; const char *response; rooms = cgi_param (cgi, "rooms"); if (!rooms) return bad_request_error (pool, http_request, io, "rooms parameter is missing"); chatrooms = parse_rooms_param (pool, rooms); if (!chatrooms) return bad_request_error (pool, http_request, io, "rooms parameter empty or could not be parsed"); msgids = cgi_param (cgi, "msgids"); if (!msgids) return bad_request_error (pool, http_request, io, "msgids parameter is missing"); next_msgids = parse_msgids_param (pool, msgids); if (!next_msgids) return bad_request_error (pool, http_request, io, "msgids parameter could not be parsed"); /* Verify that there is a next_msgid for each room. */ for (i = 0; i < vector_size (chatrooms); ++i) { vector_get (chatrooms, i, resid); if (!hash_exists (next_msgids, resid)) return bad_request_error (pool, http_request, io, "msgids parameter missing room"); } /* Wait for the next message in all chatrooms. */ if (chatroom_get_message_multiple (chatrooms, next_msgids, &msg, &room) == 0) { /* Message found in room. */ hash_get (next_msgids, resid, next_msgid); resid = chatroom_get_resid (room); response = psprintf (pool, "%d %d ", resid, next_msgid); } else { /* Message gone in room. */ hash_get (next_msgids, resid, next_msgid); resid = chatroom_get_resid (room); response = psprintf (pool, "%d %d gone", resid, next_msgid); } } static int do_post (pool pool, http_request http_request, io_handle io, cgi cgi) { abort (); } /* Parse the rooms parameter (a list of resids) and return a vector * of chatroom objects. If the parsing fails for some reason, return * NULL. */ static vector parse_rooms_param (pool pool, const char *rooms) { vector chatrooms; vector rooms_v; db_handle dbh; st_handle sth; int i; /* Split up the list of rooms. */ rooms_v = pstrcsplit (pool, rooms, ','); if (vector_size (rooms_v) == 0) return bad_request_error (pool, http_request, io, "rooms parameter is empty"); dbh = get_db_handle (conninfo, DBI_THROW_ERRORS); sth = st_prepare_cached (dbh, "select 1 from ml_chat_rooms where resid = ?", DBI_INT); chatrooms = new_vector (pool, chatroom); /* Resolve each chatroom object. * XXX Future: access control for each room. */ for (i = 0; i < vector_size (rooms_v); ++i) { const char *room; int resid; chatroom cr; vector_get (rooms_v, i, room); if (sscanf (room, "%d", &resid) != 1) return 0; st_execute (sth, resid); if (!st_fetch (sth)) return 0; /* Room does not exist. */ st_finish (sth); cr = get_chatroom (dbh, resid); vector_push_back (chatrooms, cr); } return chatrooms; } static int bad_request_error (pool pool, http_request http_request, io_handle io, const char *text) { http_response http_response; int close; http_response = new_http_response (pool, http_request, io, 500, "Internal server error"); http_response_send_headers (http_response, /* Content type. */ "Content-Type", "text/plain", NO_CACHE_HEADERS, /* End of headers. */ NULL); close = http_response_end_headers (http_response); if (http_request_is_HEAD (http_request)) return close; /* XXX Escaping. */ io_fputs (text, io); return close; }