rough framework
[lemu] / command.c
1 #include <strings.h>
2
3 #include <event2/event.h>
4 #include <event2/bufferevent.h>
5 #include <event2/bufferevent_struct.h>
6
7 #include <stdio.h>
8 #include <string.h>
9 #include <errno.h>
10 #include <pthread.h>
11
12 #include <lua.h>
13 #include <lualib.h>
14 #include <lauxlib.h>
15
16 #include "command.h"
17 #include "common.h"
18 #include "connections.h"
19 #include "db.h"
20 #include "lua_interface.h"
21 #include "notify.h"
22 #include "server.h"
23
24 /**
25 * Return a new populated command.
26 */
27 struct command *
28 command_new(unsigned char *command_line, size_t command_len, command_flags_t flags)
29 {
30 struct command *command;
31
32 command = calloc(1, sizeof *command);
33 if (command == NULL) {
34 NOTIFY_ERROR("%s:%s", "calloc", strerror(errno));
35 return NULL;
36 }
37
38 command->command = command_line;
39 command->command_len = command_len;
40 command->flags = flags;
41
42 return command;
43 }
44
45
46 /**
47 * Release a command and its data.
48 */
49 void
50 command_free(struct command *command)
51 {
52 command->command_len = 0;
53 command->flags = 0;
54 if (command->command) {
55 free(command->command);
56 command->command = NULL;
57 }
58 free(command);
59 }
60
61
62 /**
63 * This is invoked when a connection has successfully joined the server.
64 */
65 void
66 command_connected(struct connection *c)
67 {
68 connection_printf(c, "Welcome!\n");
69 connection_printf_broadcast(c, true, "[%s has connected]\n", c->name);
70 }
71
72
73 /**
74 * This is invoked when a connection has been disconnected from the server,
75 * before the connection is destroyed.
76 */
77 void
78 command_disconnected(struct connection *c)
79 {
80 connection_printf(c, "Goodbye!\n");
81 connection_printf_broadcast(c, true, "[%s has disconnected]\n", c->name);
82 NOTIFY_DEBUG("%s disconnected", c->name);
83 }
84
85
86 /**
87 * process some simple commands, until we get fancier
88 */
89 void
90 command_parse(struct connection *c, struct server_worker_context *ctx, size_t thread_id, struct command *command)
91 {
92 NOTIFY_DEBUG("parsing '%s' from '%s' (%p) [%zu]", command->command, c->name, ctx, thread_id);
93
94 if (command->flags & COMMAND_FLAG_EVENT_CONNECT) {
95 command_connected(c);
96 }
97 if (command->flags & COMMAND_FLAG_EVENT_DISCONNECT) {
98 command_disconnected(c);
99 }
100
101 if (!command->command) {
102 return;
103 }
104
105 if (strncasecmp((char *)command->command, "auth", 4) == 0) {
106 char *cmd, *user, *secret, *state;
107 int uid = 0;
108 int r = 0;
109
110 cmd = strtok_r((char *)command->command, " \t\n\r", &state);
111 user = strtok_r(NULL, " \t\n\r", &state);
112 secret = strtok_r(NULL, " \t\n\r", &state);
113
114 if (!cmd || !user || !secret) {
115 connection_printf(c, "Failed.\n");
116 return;
117 }
118
119 NOTIFY_DEBUG("authentication request for '%s'", user);
120
121 r = db_uid_from_name(&c->server->db, (unsigned char *)user, &uid);
122 NOTIFY_DEBUG("r:%d uid: %d", r, uid);
123
124 connection_printf(c, ">> r:%d uid: %d\n", r, uid);
125
126 return;
127 }
128
129 if (memcmp(command->command, "QUIT", command->command_len) == 0) {
130 bufferevent_disable(c->bev, EV_READ);
131 connection_printf(c, "Goodbye! You sent me a total of %zu bytes! (user_time:%ld.%.6ld sys_time:%ld.%.6ld)\n",
132 c->total_read,
133 c->utime.tv_sec, (long int) c->utime.tv_usec,
134 c->stime.tv_sec, (long int) c->utime.tv_usec);
135 c->state = CONNECTION_STATE_WANT_CLOSE;
136 return;
137 }
138
139 if (memcmp(command->command, "@SHUTDOWN", command->command_len) == 0) {
140 struct timeval _now = { 0, 0 };
141 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> */
142
143 connection_printf(c, "Killing server.\n");
144
145 event_base_loopexit(base, &_now);
146 return;
147 }
148
149 if (memcmp(command->command, "INFO", command->command_len) == 0) {
150 char *uuid_buf = NULL;
151 size_t uuid_len;
152 uuid_rc_t rc;
153
154 rc = uuid_export(c->uuid, UUID_FMT_STR, &uuid_buf, &uuid_len);
155 if (rc != UUID_RC_OK) {
156 NOTIFY_ERROR("%s:%s", "uuid_export", uuid_error(rc));
157 }
158
159 connection_lock_output(c);
160 connection_printf(c, "You are connected from: %s\n", c->client_address);
161 connection_printf(c, "Your connection is %sencrypted.\n", c->flags & CONN_TYPE_SSL ? "" : "not ");
162 connection_printf(c, "Your UUID is: %s\n", uuid_buf);
163 connection_unlock_output(c);
164
165 free(uuid_buf);
166
167 return;
168 }
169
170 if (memcmp(command->command, "WHO", command->command_len) == 0) {
171 struct connection **clist;
172 struct connection **clist_iter;
173
174 clist_iter = clist = connections_all_as_array(&c->server->connections, NULL);
175
176 connection_lock_output(c);
177 while (*clist_iter) {
178 connection_printf(c, "%s from %s%s%s\n",
179 (*clist_iter)->name,
180 (*clist_iter)->client_address,
181 (*clist_iter)->flags & CONN_TYPE_SSL ? " (via ssl)" : "",
182 (*clist_iter == c) ? " <- you" : "");
183 connection_free(*clist_iter);
184 clist_iter++;
185 }
186 connection_unlock_output(c);
187
188 free(clist);
189
190 return;
191 }
192
193 #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__)))
194 if (*command->command == '!') {
195 lua_State *L = ctx->L;
196 const char *echo = (const char *) command->command + 1;
197
198
199 // lemu_cosnnection_push(L, c); // c
200
201 // int t = lua_gettop(L);
202 // lua_pushnil(L); /* first key */
203 // while (lua_next(L, t) != 0) {
204 // /* uses 'key' (at index -2) and 'value' (at index -1) */
205 // printf("k:%s v:%s\n",
206 // lua_typename(L, lua_type(L, -2)),
207 // lua_typename(L, lua_type(L, -1)));
208 // /* removes 'value'; keeps 'key' for next iteration */
209 // switch (lua_type(L, -2)) {
210 // case LUA_TSTRING:
211 // printf("\tk:%s\n", lua_tostring(L, -2));
212 // break;
213 // case LUA_TNUMBER:
214 // printf("\tk:%f\n", lua_tonumber(L, -2));
215 // break;
216 // default:
217 // printf("\tk:?\n");
218 // }
219 // switch (lua_type(L, -1)) {
220 // case LUA_TSTRING:
221 // printf("\tv:%s\n", lua_tostring(L, -1));
222 // break;
223 // case LUA_TNUMBER:
224 // printf("\tv:%f\n", lua_tonumber(L, -1));
225 // break;
226 // default:
227 // printf("\tv:?\n");
228 // }
229 // lua_pop(L, 1);
230 // }
231
232 NOTIFY_DEBUG("lua echo on '%s'", echo);
233 TOSD(0);
234 lemu_connection_push(L, c); // c
235 TOSD(0);
236 lua_getfield(L, 1, "send"); // c sendfn
237 TOSD(0);
238 lua_rotate(L, 1, 1); // sendfn c
239 TOSD(0);
240 lua_pushfstring(L, "(lua echo) %s", echo); // sendfn c str
241 TOSD(0);
242 lua_call(L, 2, 0);
243 }
244
245 /* default to broadcasting message */
246 connection_printf_broadcast(c, true, "%s said: %s\n", c->name, command->command);
247 connection_printf(c, "%s said: %s\n", "You", command->command);
248 }