rough framework
[lemu] / command.c
diff --git a/command.c b/command.c
new file mode 100644 (file)
index 0000000..20ca0fc
--- /dev/null
+++ b/command.c
@@ -0,0 +1,248 @@
+#include <strings.h>
+
+#include <event2/event.h>
+#include <event2/bufferevent.h>
+#include <event2/bufferevent_struct.h>
+
+#include <stdio.h>
+#include <string.h>
+#include <errno.h>
+#include <pthread.h>
+
+#include <lua.h>
+#include <lualib.h>
+#include <lauxlib.h>
+
+#include "command.h"
+#include "common.h"
+#include "connections.h"
+#include "db.h"
+#include "lua_interface.h"
+#include "notify.h"
+#include "server.h"
+
+/**
+ * Return a new populated command.
+ */
+struct command *
+command_new(unsigned char *command_line, size_t command_len, command_flags_t flags)
+{
+       struct command *command;
+
+       command = calloc(1, sizeof *command);
+       if (command == NULL) {
+               NOTIFY_ERROR("%s:%s", "calloc", strerror(errno));
+               return NULL;
+       }
+
+       command->command = command_line;
+       command->command_len = command_len;
+       command->flags = flags;
+
+       return command;
+}
+
+
+/**
+ * Release a command and its data.
+ */
+void
+command_free(struct command *command)
+{
+       command->command_len = 0;
+       command->flags = 0;
+       if (command->command) {
+               free(command->command);
+               command->command = NULL;
+       }
+       free(command);
+}
+
+
+/**
+ * This is invoked when a connection has successfully joined the server.
+ */
+void
+command_connected(struct connection *c)
+{
+       connection_printf(c, "Welcome!\n");
+       connection_printf_broadcast(c, true, "[%s has connected]\n", c->name);
+}
+
+
+/**
+ * This is invoked when a connection has been disconnected from the server,
+ * before the connection is destroyed.
+ */
+void
+command_disconnected(struct connection *c)
+{
+       connection_printf(c, "Goodbye!\n");
+       connection_printf_broadcast(c, true, "[%s has disconnected]\n", c->name);
+       NOTIFY_DEBUG("%s disconnected", c->name);
+}
+
+
+/**
+ * process some simple commands, until we get fancier
+ */
+void
+command_parse(struct connection *c, struct server_worker_context *ctx, size_t thread_id, struct command *command)
+{
+       NOTIFY_DEBUG("parsing '%s' from '%s' (%p) [%zu]", command->command, c->name, ctx, thread_id);
+
+       if (command->flags & COMMAND_FLAG_EVENT_CONNECT) {
+               command_connected(c);
+       }
+       if (command->flags & COMMAND_FLAG_EVENT_DISCONNECT) {
+               command_disconnected(c);
+       }
+
+       if (!command->command) {
+               return;
+       }
+
+       if (strncasecmp((char *)command->command, "auth", 4) == 0) {
+               char *cmd, *user, *secret, *state;
+               int uid = 0;
+               int r = 0;
+
+               cmd = strtok_r((char *)command->command, " \t\n\r", &state);
+               user = strtok_r(NULL, " \t\n\r", &state);
+               secret = strtok_r(NULL, " \t\n\r", &state);
+
+               if (!cmd || !user || !secret) {
+                       connection_printf(c, "Failed.\n");
+                       return;
+               }
+
+               NOTIFY_DEBUG("authentication request for '%s'", user);
+
+               r = db_uid_from_name(&c->server->db, (unsigned char *)user, &uid);
+               NOTIFY_DEBUG("r:%d uid: %d", r, uid);
+
+               connection_printf(c, ">> r:%d uid: %d\n", r, uid);
+
+               return;
+       }
+
+       if (memcmp(command->command, "QUIT", command->command_len) == 0) {
+               bufferevent_disable(c->bev, EV_READ);
+               connection_printf(c, "Goodbye! You sent me a total of %zu bytes! (user_time:%ld.%.6ld sys_time:%ld.%.6ld)\n",
+                                 c->total_read,
+                                 c->utime.tv_sec, (long int) c->utime.tv_usec,
+                                 c->stime.tv_sec, (long int) c->utime.tv_usec);
+               c->state = CONNECTION_STATE_WANT_CLOSE;
+               return;
+       }
+
+       if (memcmp(command->command, "@SHUTDOWN", command->command_len) == 0) {
+               struct timeval _now = { 0, 0 };
+               struct event_base *base = c->bev->ev_base; /* no accessor like bufferevent_get_base() so need to directly grab this with <event2/bufferevent_struct.h> */
+
+               connection_printf(c, "Killing server.\n");
+
+               event_base_loopexit(base, &_now);
+               return;
+       }
+
+       if (memcmp(command->command, "INFO", command->command_len) == 0) {
+               char *uuid_buf = NULL;
+               size_t uuid_len;
+               uuid_rc_t rc;
+
+               rc = uuid_export(c->uuid, UUID_FMT_STR, &uuid_buf, &uuid_len);
+               if (rc != UUID_RC_OK) {
+                       NOTIFY_ERROR("%s:%s", "uuid_export", uuid_error(rc));
+               }
+
+               connection_lock_output(c);
+               connection_printf(c, "You are connected from: %s\n", c->client_address);
+               connection_printf(c, "Your connection is %sencrypted.\n", c->flags & CONN_TYPE_SSL ? "" : "not ");
+               connection_printf(c, "Your UUID is: %s\n", uuid_buf);
+               connection_unlock_output(c);
+
+               free(uuid_buf);
+
+               return;
+       }
+
+       if (memcmp(command->command, "WHO", command->command_len) == 0) {
+               struct connection **clist;
+               struct connection **clist_iter;
+
+               clist_iter = clist = connections_all_as_array(&c->server->connections, NULL);
+
+               connection_lock_output(c);
+               while (*clist_iter) {
+                       connection_printf(c, "%s from %s%s%s\n",
+                                         (*clist_iter)->name,
+                                         (*clist_iter)->client_address,
+                                         (*clist_iter)->flags & CONN_TYPE_SSL ? " (via ssl)" : "",
+                                         (*clist_iter == c) ? " <- you" : "");
+                       connection_free(*clist_iter);
+                       clist_iter++;
+               }
+               connection_unlock_output(c);
+
+               free(clist);
+
+               return;
+       }
+
+#define TOSD(__off__) NOTIFY_DEBUG("TOS[%d]: %s '%s'", lua_gettop(L) - (__off__), lua_typename(L, lua_type(L, -1 - (__off__))), lua_tostring(L, -1 - (__off__)))
+       if (*command->command == '!') {
+               lua_State *L = ctx->L;
+               const char *echo = (const char *) command->command + 1;
+
+
+               // lemu_cosnnection_push(L, c); // c
+
+               // int t = lua_gettop(L);
+               // lua_pushnil(L);  /* first key */
+               // while (lua_next(L, t) != 0) {
+               //      /* uses 'key' (at index -2) and 'value' (at index -1) */
+               //      printf("k:%s v:%s\n",
+               //             lua_typename(L, lua_type(L, -2)),
+               //             lua_typename(L, lua_type(L, -1)));
+               //      /* removes 'value'; keeps 'key' for next iteration */
+               //      switch (lua_type(L, -2)) {
+               //              case LUA_TSTRING:
+               //                      printf("\tk:%s\n", lua_tostring(L, -2));
+               //                      break;
+               //              case LUA_TNUMBER:
+               //                      printf("\tk:%f\n", lua_tonumber(L, -2));
+               //                      break;
+               //              default:
+               //                      printf("\tk:?\n");
+               //      }
+               //      switch (lua_type(L, -1)) {
+               //              case LUA_TSTRING:
+               //                      printf("\tv:%s\n", lua_tostring(L, -1));
+               //                      break;
+               //              case LUA_TNUMBER:
+               //                      printf("\tv:%f\n", lua_tonumber(L, -1));
+               //                      break;
+               //              default:
+               //                      printf("\tv:?\n");
+               //      }
+               //      lua_pop(L, 1);
+               // }
+
+               NOTIFY_DEBUG("lua echo on '%s'", echo);
+               TOSD(0);
+               lemu_connection_push(L, c); // c
+               TOSD(0);
+               lua_getfield(L, 1, "send"); // c sendfn
+               TOSD(0);
+               lua_rotate(L, 1, 1); // sendfn c
+               TOSD(0);
+               lua_pushfstring(L, "(lua echo) %s", echo); // sendfn c str
+               TOSD(0);
+               lua_call(L, 2, 0);
+       }
+
+       /* default to broadcasting message */
+       connection_printf_broadcast(c, true, "%s said: %s\n", c->name, command->command);
+       connection_printf(c, "%s said: %s\n", "You", command->command);
+}