#include #include #include #include #include #include #include #include #include #include #include #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 */ 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); }