aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/auth.c1
-rw-r--r--src/client.c500
-rw-r--r--src/client.h4
-rw-r--r--src/obj.c4
-rw-r--r--src/server.c19
-rw-r--r--src/server_reqs.c5
-rw-r--r--src/telnet.c13
-rw-r--r--src/telnet.h2
-rw-r--r--src/userdb.h3
9 files changed, 330 insertions, 221 deletions
diff --git a/src/auth.c b/src/auth.c
index 5fd8219..d6c72da 100644
--- a/src/auth.c
+++ b/src/auth.c
@@ -75,6 +75,7 @@ static void add_user_internal(const char *name, const char *pass, int authlevel)
/* doesn't need to be malloc'd */
struct userdata_t userdata;
+ memset(&userdata, 0, sizeof(userdata));
strncpy(userdata.username, name, sizeof(userdata.username));
memcpy(userdata.passhash, hexhash, sizeof(userdata.passhash));
diff --git a/src/client.c b/src/client.c
index 1de6818..62b9fbe 100644
--- a/src/client.c
+++ b/src/client.c
@@ -388,6 +388,7 @@ bool client_move(const char *dir)
{ "IN", DIR_IN },
{ "OUT", DIR_OT },
};
+
if(!dir_map)
{
dir_map = hash_init(ARRAYLEN(dirs), hash_djb, compare_strings);
@@ -446,6 +447,272 @@ void client_user_list(void)
#define WSPACE " \t\r\n"
+#define CMD_OK 0
+#define CMD_LOGOUT 1
+#define CMD_QUIT 2
+
+/*** callbacks ***/
+
+int user_cb(char **save)
+{
+ char *what = strtok_r(NULL, WSPACE, save);
+ if(!what)
+ return CMD_OK;
+
+ all_upper(what);
+
+ if(!strcmp(what, "DEL"))
+ {
+ char *user = strtok_r(NULL, WSPACE, save);
+ if(user)
+ {
+ if(strcmp(user, current_user) && auth_user_del(user))
+ out("Success.\n");
+ else
+ out("Failure.\n");
+ }
+ else
+ {
+ out("Usage: USER DEL <USERNAME>\n");
+ }
+ }
+ else if(!strcmp(what, "ADD") || !strcmp(what, "MODIFY"))
+ {
+ char *user = strtok_r(NULL, WSPACE, save);
+ if(user)
+ {
+ if(!strcmp(user, current_user))
+ {
+ out("Do not modify your own password using USER. User CHPASS instead.\n");
+ return CMD_OK;
+ }
+
+ out("Editing user '%s'\n", user);
+
+ out("New Password (_DO_NOT_USE_A_VALUABLE_PASSWORD_): ");
+
+ /* BAD BAD BAD BAD BAD BAD BAD CLEARTEXT PASSWORDS!!! */
+ char *pass = client_read_password();
+
+ out("Verify Password: ");
+ char *pass2 = client_read_password();
+
+ if(strcmp(pass, pass2))
+ {
+ memset(pass, 0, strlen(pass));
+ memset(pass2, 0, strlen(pass2));
+ free(pass);
+ free(pass2);
+ out("Failure.\n");
+ return CMD_OK;
+ }
+
+ out("Admin privileges [y/N]? ");
+ char *allow_admin = client_read();
+ int priv = PRIV_USER;
+ if(toupper(allow_admin[0]) == 'Y')
+ priv = PRIV_ADMIN;
+
+ free(allow_admin);
+
+ if(auth_user_add(user, pass, priv))
+ out("Success.\n");
+ else
+ out("Failure.\n");
+ memset(pass, 0, strlen(pass));
+ free(pass);
+ }
+ else
+ out("Usage: USER <ADD|MODIFY> <USERNAME>\n");
+ }
+ else if(!strcmp(what, "LIST"))
+ {
+ client_user_list();
+ }
+ else
+ {
+ out("Usage: USER <ADD|DEL|MODIFY|LIST> <ARGS>\n");
+ }
+
+ return CMD_OK;
+}
+
+int client_cb(char **save)
+{
+ char *what = strtok_r(NULL, WSPACE, save);
+ if(!what)
+ {
+ out("Usage: CLIENT <LIST|KICK> <PID>\n");
+ return CMD_OK;
+ }
+
+ all_upper(what);
+
+ if(!strcmp(what, "LIST"))
+ {
+ send_master(REQ_LISTCLIENTS, NULL, 0);
+ }
+ else if(!strcmp(what, "KICK"))
+ {
+ char *pid_s = strtok_r(NULL, WSPACE, save);
+ all_upper(pid_s);
+ if(pid_s)
+ {
+ if(!strcmp(pid_s, "ALL"))
+ {
+ const char *msg = "Kicking everyone...\n";
+ send_master(REQ_KICKALL, msg, strlen(msg));
+ return CMD_OK;
+ }
+ /* weird pointer voodoo */
+ /* TODO: simplify */
+ char pidbuf[MAX(sizeof(pid_t), MSG_MAX)];
+ char *end;
+ pid_t pid = strtol(pid_s, &end, 0);
+ if(pid == getpid())
+ {
+ out("You cannot kick yourself. Use EXIT instead.\n");
+ return CMD_OK;
+ }
+ else if(*end != '\0')
+ {
+ out("Expected a child PID after KICK.\n");
+ return CMD_OK;
+ }
+ memcpy(pidbuf, &pid, sizeof(pid));
+ int len = sizeof(pid_t) + snprintf(pidbuf + sizeof(pid_t),
+ sizeof(pidbuf) - sizeof(pid_t),
+ "You were kicked.\n");
+ send_master(REQ_KICK, pidbuf, len);
+ debugf("Success.\n");
+ }
+ else
+ out("Usage: CLIENT KICK <PID>\n");
+ }
+ return CMD_OK;
+}
+
+int quit_cb(char **save)
+{
+ (void) save;
+ return CMD_QUIT;
+}
+
+int say_cb(char **save)
+{
+ char buf[MSG_MAX];
+ char *what = strtok_r(NULL, "", save);
+ int len = snprintf(buf, sizeof(buf), "%s says %s\n", current_user, what);
+
+ send_master(REQ_BCASTMSG, buf, len);
+ return CMD_OK;
+}
+
+int date_cb(char **save)
+{
+ (void) save;
+ time_t t = time(NULL);
+ out("%s", ctime(&t));
+ return CMD_OK;
+}
+
+int logout_cb(char **save)
+{
+ (void) save;
+ out("Logged out.\n");
+ telnet_clear_screen();
+ return CMD_LOGOUT;
+}
+
+int look_cb(char **save)
+{
+ char *what = strtok_r(NULL, " ", save);
+ if(!what)
+ client_look();
+ else
+ {
+ client_look_at(what);
+ }
+ return CMD_OK;
+}
+
+int inventory_cb(char **save)
+{
+ (void) save;
+ client_inventory();
+ return CMD_OK;
+}
+
+int take_cb(char **save)
+{
+ char *what = strtok_r(NULL, " ", save);
+ client_take(what);
+ return CMD_OK;
+}
+
+int wait_cb(char **save)
+{
+ (void) save;
+ send_master(REQ_WAIT, NULL, 0);
+ return CMD_OK;
+}
+
+int go_cb(char **save)
+{
+ char *dir = strtok_r(NULL, WSPACE, save);
+ if(dir)
+ {
+ all_upper(dir);
+ if(client_move(dir))
+ client_look();
+ }
+ else
+ out("Expected direction after GO.\n");
+ return CMD_OK;
+}
+
+int drop_cb(char **save)
+{
+ char *what = strtok_r(NULL, " ", save);
+ client_drop(what);
+ return CMD_OK;
+}
+
+static const struct client_cmd {
+ const char *cmd;
+ int (*cb)(char **saveptr);
+ bool admin_only;
+} cmds[] = {
+ { "USER", user_cb, true },
+ { "CLIENT", client_cb, true },
+ { "EXIT", quit_cb, false },
+ { "QUIT", quit_cb, false },
+ { "SAY", say_cb, false },
+ { "DATE", date_cb, false },
+ { "LOGOUT", logout_cb, false },
+ { "LOOK", look_cb, false },
+ { "INVENTORY", inventory_cb, false },
+ { "TAKE", take_cb, false },
+ { "WAIT", wait_cb, false },
+ { "GO", go_cb, false },
+ { "DROP", drop_cb, false },
+};
+
+static void *cmd_map = NULL;
+
+void client_init(void)
+{
+ cmd_map = hash_init(ARRAYLEN(cmds), hash_djb, compare_strings);
+ hash_insert_pairs(cmd_map, (const struct hash_pair*)cmds, sizeof(cmds[0]), ARRAYLEN(cmds));
+}
+
+void client_shutdown(void)
+{
+ hash_free(cmd_map);
+ cmd_map = NULL;
+}
+
+
void client_main(int fd, struct sockaddr_in *addr, int total, int to, int from)
{
client_fd = fd;
@@ -539,232 +806,47 @@ auth:
while(1)
{
out(">> ");
- char *cmd = client_read();
- char *orig = strdup(cmd);
+ char *line = client_read();
+ char *orig = strdup(line);
char *save = NULL;
- char *tok = strtok_r(cmd, WSPACE, &save);
+ char *tok = strtok_r(line, WSPACE, &save);
if(!tok)
goto next_cmd;
- all_upper(tok);
-
- if(admin)
- {
- if(!strcmp(tok, "USER"))
- {
- char *what = strtok_r(NULL, WSPACE, &save);
- if(!what)
- goto next_cmd;
- all_upper(what);
-
- if(!strcmp(what, "DEL"))
- {
- char *user = strtok_r(NULL, WSPACE, &save);
- if(user)
- {
- if(strcmp(user, current_user) && auth_user_del(user))
- out("Success.\n");
- else
- out("Failure.\n");
- }
- else
- {
- out("Usage: USER DEL <USERNAME>\n");
- }
- }
- else if(!strcmp(what, "ADD") || !strcmp(what, "MODIFY"))
- {
- char *user = strtok_r(NULL, WSPACE, &save);
- if(user)
- {
- if(!strcmp(user, current_user))
- {
- out("Do not modify your own password using USER. User CHPASS instead.\n");
- goto next_cmd;
- }
-
- out("Editing user '%s'\n", user);
-
- out("New Password (_DO_NOT_USE_A_VALUABLE_PASSWORD_): ");
-
- /* BAD BAD BAD BAD BAD BAD BAD CLEARTEXT PASSWORDS!!! */
- char *pass = client_read_password();
-
- out("Verify Password: ");
- char *pass2 = client_read_password();
-
- if(strcmp(pass, pass2))
- {
- memset(pass, 0, strlen(pass));
- memset(pass2, 0, strlen(pass2));
- free(pass);
- free(pass2);
- out("Failure.\n");
- goto next_cmd;
- }
-
- out("Admin privileges [y/N]? ");
- char *allow_admin = client_read();
- int priv = PRIV_USER;
- if(toupper(allow_admin[0]) == 'Y')
- priv = PRIV_ADMIN;
-
- free(allow_admin);
-
- if(auth_user_add(user, pass, priv))
- out("Success.\n");
- else
- out("Failure.\n");
- memset(pass, 0, strlen(pass));
- free(pass);
- }
- else
- out("Usage: USER <ADD|MODIFY> <USERNAME>\n");
- }
- else if(!strcmp(what, "LIST"))
- {
- client_user_list();
- }
- else
- {
- out("Usage: USER <ADD|DEL|MODIFY|LIST> <ARGS>\n");
- }
- }
- else if(!strcmp(tok, "CLIENT"))
- {
- char *what = strtok_r(NULL, WSPACE, &save);
- if(!what)
- {
- out("Usage: CLIENT <LIST|KICK> <PID>\n");
- goto next_cmd;
- }
-
- all_upper(what);
-
- if(!strcmp(what, "LIST"))
- {
- send_master(REQ_LISTCLIENTS, NULL, 0);
- }
- else if(!strcmp(what, "KICK"))
- {
- char *pid_s = strtok_r(NULL, WSPACE, &save);
- all_upper(pid_s);
- if(pid_s)
- {
- if(!strcmp(pid_s, "ALL"))
- {
- const char *msg = "Kicking everyone...\n";
- send_master(REQ_KICKALL, msg, strlen(msg));
- goto next_cmd;
- }
- /* weird pointer voodoo */
- /* TODO: simplify */
- char pidbuf[MAX(sizeof(pid_t), MSG_MAX)];
- char *end;
- pid_t pid = strtol(pid_s, &end, 0);
- if(pid == getpid())
- {
- out("You cannot kick yourself. Use EXIT instead.\n");
- goto next_cmd;
- }
- else if(*end != '\0')
- {
- out("Expected a child PID after KICK.\n");
- goto next_cmd;
- }
- memcpy(pidbuf, &pid, sizeof(pid));
- int len = sizeof(pid_t) + snprintf(pidbuf + sizeof(pid_t),
- sizeof(pidbuf) - sizeof(pid_t),
- "You were kicked.\n");
- send_master(REQ_KICK, pidbuf, len);
- debugf("Success.\n");
- }
- else
- out("Usage: CLIENT KICK <PID>\n");
- }
- }
- //else if(!strcmp(tok, "HANG"))
- //{
- // send_master(REQ_HANG);
- //}
- }
-
- /* unprivileged commands */
- if(!strcmp(tok, "QUIT") || !strcmp(tok, "EXIT"))
- {
- free(cmd);
- goto done;
- }
- else if(!strcmp(tok, "SAY"))
- {
- char buf[MSG_MAX];
- char *what = strtok_r(NULL, "", &save);
- int len = snprintf(buf, sizeof(buf), "%s says %s\n", current_user, what);
+ all_upper(tok);
- send_master(REQ_BCASTMSG, buf, len);
- }
- else if(!strcmp(tok, "DATE"))
+ const struct client_cmd *cmd = hash_lookup(cmd_map, tok);
+ if(cmd && cmd->cb && (!cmd->admin_only || (cmd->admin_only && admin)))
{
- time_t t = time(NULL);
- out("%s", ctime(&t));
- }
- else if(!strcmp(tok, "LOGOUT"))
- {
- out("Logged out.\n");
- goto auth;
- }
- else if(!strcmp(tok, "LOOK"))
- {
- char *what = strtok_r(NULL, " ", &save);
- if(!what)
- client_look();
- else
- {
- client_look_at(what);
- }
- }
- else if(!strcmp(tok, "INVENTORY"))
- {
- client_inventory();
- }
- else if(!strcmp(tok, "TAKE"))
- {
- char *what = strtok_r(NULL, " ", &save);
- client_take(what);
- }
- else if(!strcmp(tok, "WAIT"))
- {
- send_master(REQ_WAIT, NULL, 0);
- }
- else if(!strcmp(tok, "GO"))
- {
- char *dir = strtok_r(NULL, WSPACE, &save);
- if(dir)
+ int ret = cmd->cb(&save);
+ switch(ret)
{
- all_upper(dir);
- if(client_move(dir))
- client_look();
+ case CMD_OK:
+ goto next_cmd;
+ case CMD_LOGOUT:
+ goto auth;
+ case CMD_QUIT:
+ free(line);
+ free(orig);
+ goto done;
+ default:
+ error("client: bad callback return value");
}
- else
- out("Expected direction after GO.\n");
}
- else if(!strcmp(tok, "DROP"))
+ else if(cmd && cmd->admin_only && !admin)
{
- char *what = strtok_r(NULL, " ", &save);
- client_drop(what);
+ out("You are not allowed to do that.\n");
+ goto next_cmd;
}
- else
- {
- /* we can't handle it, send it to the master */
- send_master(REQ_EXECVERB, orig, strlen(orig) + 1);
- }
+ /* if we can't handle it, let the master process try */
+ send_master(REQ_EXECVERB, orig, strlen(orig) + 1);
next_cmd:
- free(cmd);
+ free(line);
free(orig);
}
diff --git a/src/client.h b/src/client.h
index eeb515d..4ab7f9b 100644
--- a/src/client.h
+++ b/src/client.h
@@ -43,3 +43,7 @@ void out_raw(const void*, size_t);
/* called for every client */
void client_main(int sock, struct sockaddr_in *addr, int, int to_parent, int from_parent);
+
+/* can (and should) be called before forking the child */
+void client_init(void);
+void client_shutdown(void);
diff --git a/src/obj.c b/src/obj.c
index 20714f7..7905700 100644
--- a/src/obj.c
+++ b/src/obj.c
@@ -82,18 +82,20 @@ struct object_t *obj_read(int fd)
struct object_t *obj_dup(struct object_t *obj)
{
+ debugf("Adding an object reference.\n");
++obj->refcount;
return obj;
}
void obj_free(void *ptr)
{
+ debugf("Freeing an object reference.\n");
struct object_t *obj = ptr;
- debugf("Freeing obj %s\n", obj->name);
--obj->refcount;
if(!obj->refcount)
{
+ debugf("Freeing obj %s\n", obj->name);
if(obj->class->hook_destroy)
obj->class->hook_destroy(obj);
diff --git a/src/server.c b/src/server.c
index d65a3f1..0b1569d 100644
--- a/src/server.c
+++ b/src/server.c
@@ -118,6 +118,7 @@ static void __attribute__((noreturn)) serv_cleanup(void)
close(server_socket);
/* shut down modules */
+ client_shutdown();
obj_shutdown();
reqmap_free();
userdb_shutdown();
@@ -380,28 +381,32 @@ static void parse_args(int argc, char *argv[])
static SIMP_HASH(pid_t, pid_hash);
static SIMP_EQUAL(pid_t, pid_equal);
-int server_main(int argc, char *argv[])
+static void check_libs(void)
{
debugf("*** Starting NetCosm %s (libev %d.%d, %s) ***\n",
NETCOSM_VERSION, EV_VERSION_MAJOR, EV_VERSION_MINOR, OPENSSL_VERSION_TEXT);
-
assert(ev_version_major() == EV_VERSION_MAJOR &&
ev_version_minor() >= EV_VERSION_MINOR);
+}
- parse_args(argc, argv);
+int server_main(int argc, char *argv[])
+{
+ check_libs();
- srand(time(0));
+ parse_args(argc, argv);
server_socket = server_bind();
userdb_init(USERFILE);
check_userfile();
-
load_worldfile();
reqmap_init();
+ /* save some time after a fork() */
+ client_init();
+
/* this initial size very low to make iteration faster */
child_map = hash_init(16, pid_hash, pid_equal);
hash_setfreedata_cb(child_map, free_child_data);
@@ -409,7 +414,7 @@ int server_main(int argc, char *argv[])
debugf("Listening on port %d\n", port);
- struct ev_loop *loop = EV_DEFAULT;
+ struct ev_loop *loop = ev_default_loop(0);
/* we initialize signals after creating the default event loop
* because libev grabs SIGCHLD */
@@ -418,10 +423,12 @@ int server_main(int argc, char *argv[])
ev_io server_watcher;
ev_io_init(&server_watcher, new_connection_cb, server_socket, EV_READ);
ev_set_priority(&server_watcher, EV_MAXPRI);
+
ev_io_start(EV_A_ &server_watcher);
atexit(serv_cleanup);
+ /* everything's ready, hand it over to libev */
ev_loop(loop, 0);
/* should never get here */
diff --git a/src/server_reqs.c b/src/server_reqs.c
index fd1ef19..900ff87 100644
--- a/src/server_reqs.c
+++ b/src/server_reqs.c
@@ -374,9 +374,8 @@ static void req_listusers(unsigned char *data, size_t datalen, struct child_data
if(!user)
break;
- send_msg(sender, "%s: priv: %d room: %d last: %s", user->username,
+ send_msg(sender, "%s: priv: %d last: %s", user->username,
user->priv,
- user->room,
ctime(&user->last_login));
}
}
@@ -404,7 +403,7 @@ static void req_execverb(unsigned char *data, size_t datalen, struct child_data
if(verb)
goto exec_verb;
- send_msg(sender, "I don't know how to do that.\n");
+ send_msg(sender, "I don't know what that means.\n");
return;
char *args;
diff --git a/src/telnet.c b/src/telnet.c
index 743eb53..9932d84 100644
--- a/src/telnet.c
+++ b/src/telnet.c
@@ -16,8 +16,9 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
-#define TELCMDS
+#define TELCMDS /* see <arpa/telnet.h> */
#define TELOPTS
+
#include "globals.h"
#include "client.h"
@@ -132,3 +133,13 @@ void telnet_init(void)
out_raw(init_seq, ARRAYLEN(init_seq));
}
+
+void telnet_clear_screen(void)
+{
+ /* ESC ] 2 J */
+ unsigned char clear_seq[] = { '\033',
+ ']',
+ '2',
+ 'J' };
+ out_raw(clear_seq, ARRAYLEN(clear_seq));
+}
diff --git a/src/telnet.h b/src/telnet.h
index 8669e87..2f40c51 100644
--- a/src/telnet.h
+++ b/src/telnet.h
@@ -34,3 +34,5 @@ uint16_t telnet_get_height(void);
void telnet_echo_on(void);
void telnet_echo_off(void);
+
+void telnet_clear_screen(void);
diff --git a/src/userdb.h b/src/userdb.h
index 1d1d6e2..4804df9 100644
--- a/src/userdb.h
+++ b/src/userdb.h
@@ -34,7 +34,8 @@ struct userdata_t {
char passhash[AUTH_HASHLEN * 2 + 1];
priv_t priv;
- room_id room;
+
+ //room_id room;
time_t last_login;
void *objects; /* hash of object names -> objects */