--- /dev/null
+#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);
+}