Migrated struct wmudClient to the WmudClient GObject
This commit is contained in:
parent
50a487b7e2
commit
1489be34bf
@ -1,5 +1,5 @@
|
||||
bin_PROGRAMS = wmud
|
||||
AM_CPPFLAGS = -DWMUD_STATEDIR=\""$(localstatedir)"\" -DWMUD_CONFDIR=\""$(sysconfdir)"\" $(MEMCACHED_CFLAGS) $(GLIB_CFLAGS) $(GIO_CFLAGS) $(GTHREAD_CFLAGS) $(SQLITE3_CFLAGS) $(CURL_CFLAGS)
|
||||
|
||||
wmud_SOURCES = main.c game-networking.c interpreter.c db.c players.c maintenance.c game.c configuration.c world.c menu.c texts.c
|
||||
wmud_SOURCES = main.c game-networking.c interpreter.c db.c players.c maintenance.c game.c configuration.c world.c menu.c texts.c wmudclient.c
|
||||
wmud_LDADD = $(MEMCACHED_LIBS) $(GLIB_LIBS) $(GIO_LIBS) $(GTHREAD_LIBS) $(SQLITE3_LIBS) $(CURL_LIBS)
|
||||
|
@ -33,6 +33,7 @@
|
||||
#include "configuration.h"
|
||||
#include "menu.h"
|
||||
#include "texts.h"
|
||||
#include "wmudclient.h"
|
||||
|
||||
/**
|
||||
* SECTION:game-networking
|
||||
@ -49,55 +50,33 @@ struct AcceptData {
|
||||
|
||||
/**
|
||||
* clients:
|
||||
* The full #GSList of the currently connected #wmudClient structs.
|
||||
* The full #GSList of the currently connected #WmudClient objects.
|
||||
*/
|
||||
GSList *clients = NULL;
|
||||
|
||||
static GRegex *email_regex = NULL;
|
||||
|
||||
void wmud_client_interpret_newplayer_email(wmudClient *client);
|
||||
void wmud_client_interpret_newplayer_mailconfirm(wmudClient *client);
|
||||
static void state_fresh(wmudClient *client);
|
||||
static void state_passwait(wmudClient *client);
|
||||
static void state_menu(wmudClient *client);
|
||||
static void state_yesno(wmudClient *client);
|
||||
static void state_registering(wmudClient *client);
|
||||
static void state_regemail_confirm(wmudClient *client);
|
||||
void wmud_client_interpret_newplayer_email(WmudClient *client);
|
||||
void wmud_client_interpret_newplayer_mailconfirm(WmudClient *client);
|
||||
static void state_fresh(WmudClient *client);
|
||||
static void state_passwait(WmudClient *client);
|
||||
static void state_menu(WmudClient *client);
|
||||
static void state_yesno(WmudClient *client);
|
||||
static void state_registering(WmudClient *client);
|
||||
static void state_regemail_confirm(WmudClient *client);
|
||||
|
||||
/**
|
||||
* wmud_client_close:
|
||||
* @client: the client whose connection should be dropped
|
||||
* @send_goodbye: if set to %TRUE, we will send a nice good-bye message to the
|
||||
* client before dropping the connection
|
||||
*
|
||||
* Closes a client connection. If send_goodbye is set to %TRUE, a good-bye
|
||||
* message will be sent to the client.
|
||||
*/
|
||||
void
|
||||
wmud_client_close(wmudClient *client, gboolean send_goodbye)
|
||||
static void
|
||||
remove_client(WmudClient *client, gboolean send_quitmessage)
|
||||
{
|
||||
if (send_goodbye)
|
||||
wmud_client_send(client, "\r\nHave a nice real-world day!\r\n\r\n");
|
||||
|
||||
g_log(G_LOG_DOMAIN, G_LOG_LEVEL_INFO, "Connection closed.");
|
||||
g_socket_shutdown(client->socket, TRUE, TRUE, NULL);
|
||||
clients = g_slist_remove(clients, client);
|
||||
|
||||
if (client->player && !client->player->registered)
|
||||
wmud_player_free(&(client->player));
|
||||
|
||||
if (client->buffer)
|
||||
g_string_free(client->buffer, TRUE);
|
||||
|
||||
g_source_destroy(client->socket_source);
|
||||
g_free(client);
|
||||
wmud_client_close(client, send_quitmessage);
|
||||
}
|
||||
|
||||
/**
|
||||
* wmud_client_callback:
|
||||
* @client: the socket of the client on which the data arrived
|
||||
* @condition: the condition available on the client socket
|
||||
* @client: the wmudClient structure of the client
|
||||
* @client: the WmudClient structure of the client
|
||||
*
|
||||
* Processes incoming client data, and client hangup
|
||||
*
|
||||
@ -105,12 +84,12 @@ wmud_client_close(wmudClient *client, gboolean send_goodbye)
|
||||
* otherwise
|
||||
*/
|
||||
static gboolean
|
||||
wmud_client_callback(GSocket *client_socket, GIOCondition condition, wmudClient *client)
|
||||
wmud_client_callback(GSocket *client_socket, GIOCondition condition, WmudClient *client)
|
||||
{
|
||||
GError *err = NULL;
|
||||
|
||||
if (condition & G_IO_HUP) {
|
||||
wmud_client_close(client, FALSE);
|
||||
remove_client(client, FALSE);
|
||||
|
||||
return FALSE;
|
||||
} else if ((condition & G_IO_IN) || (condition & G_IO_PRI)) {
|
||||
@ -120,7 +99,7 @@ wmud_client_callback(GSocket *client_socket, GIOCondition condition, wmudClient
|
||||
|
||||
if ((len = g_socket_receive(client_socket, buf, MAX_RECV_LEN, NULL, &err)) == 0) {
|
||||
g_free(buf);
|
||||
wmud_client_close(client, FALSE);
|
||||
remove_client(client, FALSE);
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
@ -135,38 +114,38 @@ wmud_client_callback(GSocket *client_socket, GIOCondition condition, wmudClient
|
||||
sloc = -1;
|
||||
|
||||
if ((r < n) && r) {
|
||||
if (client->buffer->len > 0)
|
||||
g_string_append_len(client->buffer, buf2, (r - buf2));
|
||||
if (wmud_client_get_buffer_length(client) > 0)
|
||||
g_string_append_len(wmud_client_get_buffer(client), buf2, (r - buf2));
|
||||
else
|
||||
g_string_overwrite_len(client->buffer, 0, buf2, (r - buf2));
|
||||
g_string_overwrite_len(wmud_client_get_buffer(client), 0, buf2, (r - buf2));
|
||||
buf2 = r;
|
||||
} else if (n) {
|
||||
if (client->buffer->len > 0)
|
||||
g_string_append_len(client->buffer, buf2, (n - buf2));
|
||||
if (wmud_client_get_buffer_length(client) > 0)
|
||||
g_string_append_len(wmud_client_get_buffer(client), buf2, (n - buf2));
|
||||
else
|
||||
g_string_overwrite_len(client->buffer, 0, buf2, (n - buf2));
|
||||
g_string_overwrite_len(wmud_client_get_buffer(client), 0, buf2, (n - buf2));
|
||||
buf2 = n;
|
||||
}
|
||||
|
||||
/* Remove telnet codes from the string */
|
||||
for (i = 0; i < client->buffer->len; i++) {
|
||||
guchar c = (client->buffer->str)[i];
|
||||
for (i = 0; i < wmud_client_get_buffer_length(client); i++) {
|
||||
guchar c = (wmud_client_get_buffer(client)->str)[i];
|
||||
|
||||
if ((c >= 240) || (c == 1)) {
|
||||
if (sloc == -1)
|
||||
sloc = i;
|
||||
} else {
|
||||
if (sloc != -1) {
|
||||
g_string_erase(client->buffer, sloc, i - sloc);
|
||||
g_string_erase(wmud_client_get_buffer(client), sloc, i - sloc);
|
||||
sloc = -1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (sloc != -1)
|
||||
g_string_erase(client->buffer, sloc, -1);
|
||||
g_string_erase(wmud_client_get_buffer(client), sloc, -1);
|
||||
|
||||
switch (client->state)
|
||||
switch (wmud_client_get_state(client))
|
||||
{
|
||||
case WMUD_CLIENT_STATE_FRESH:
|
||||
state_fresh(client);
|
||||
@ -190,17 +169,17 @@ wmud_client_callback(GSocket *client_socket, GIOCondition condition, wmudClient
|
||||
state_regemail_confirm(client);
|
||||
break;
|
||||
}
|
||||
g_string_erase(client->buffer, 0, -1);
|
||||
g_string_erase(wmud_client_get_buffer(client), 0, -1);
|
||||
|
||||
for (; ((*buf2 == '\r') || (*buf2 == '\n')) && *buf2; buf2++);
|
||||
|
||||
if (!*buf2)
|
||||
break;
|
||||
} else {
|
||||
if (client->buffer->len > 0)
|
||||
g_string_append(client->buffer, buf2);
|
||||
if (wmud_client_get_buffer_length(client) > 0)
|
||||
g_string_append(wmud_client_get_buffer(client), buf2);
|
||||
else
|
||||
g_string_overwrite(client->buffer, 0, buf2);
|
||||
g_string_overwrite(wmud_client_get_buffer(client), 0, buf2);
|
||||
|
||||
break;
|
||||
}
|
||||
@ -229,21 +208,19 @@ game_source_callback(GSocket *socket, GIOCondition condition, struct AcceptData
|
||||
GSocket *client_socket;
|
||||
GSource *client_source;
|
||||
GError *err = NULL;
|
||||
wmudClient *client;
|
||||
GSocketAddress *remote_addr;
|
||||
WmudClient *client;
|
||||
|
||||
/* This function should never return an error. If so, it is a huge bug,
|
||||
* and will trigger a higher level error. */
|
||||
client_socket = g_socket_listener_accept_socket(accept_data->listener, NULL, NULL, &err);
|
||||
|
||||
client = g_new0(wmudClient, 1);
|
||||
client->socket = client_socket;
|
||||
client->buffer = g_string_new("");
|
||||
client->state = WMUD_CLIENT_STATE_FRESH;
|
||||
client = wmud_client_new();
|
||||
wmud_client_set_socket(WMUD_CLIENT(client), client_socket);
|
||||
client_source = wmud_client_get_socket_source(client);
|
||||
|
||||
clients = g_slist_prepend(clients, client);
|
||||
|
||||
client_source = g_socket_create_source(client_socket, G_IO_IN | G_IO_OUT | G_IO_PRI | G_IO_ERR | G_IO_HUP | G_IO_NVAL, NULL);
|
||||
client->socket_source = client_source;
|
||||
g_source_set_callback(client_source, (GSourceFunc)wmud_client_callback, client, NULL);
|
||||
g_source_attach(client_source, accept_data->context);
|
||||
|
||||
@ -267,7 +244,7 @@ game_source_callback(GSocket *socket, GIOCondition condition, struct AcceptData
|
||||
}
|
||||
|
||||
g_clear_error(&err);
|
||||
wmud_client_send(client, "By what name shall we call you? ");
|
||||
wmud_client_send(WMUD_CLIENT(client), "By what name shall we call you? ");
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
@ -394,99 +371,80 @@ wmud_networking_init(guint port_number, GMainContext *game_context, GSList *menu
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
/**
|
||||
* wmud_client_send:
|
||||
* @client: the client to which the message will be sent
|
||||
* @fmt: the printf() style format string of the message
|
||||
* @...: optional parameters to the format string
|
||||
*
|
||||
* Sends a formatted message to a game client
|
||||
*/
|
||||
void
|
||||
wmud_client_send(wmudClient *client, const gchar *fmt, ...)
|
||||
{
|
||||
va_list ap;
|
||||
GString *buf = g_string_new("");
|
||||
|
||||
va_start(ap, fmt);
|
||||
g_string_vprintf(buf, fmt, ap);
|
||||
va_end(ap);
|
||||
|
||||
/* TODO: error checking */
|
||||
g_socket_send(client->socket, buf->str, buf->len, NULL, NULL);
|
||||
g_string_free(buf, TRUE);
|
||||
}
|
||||
|
||||
void
|
||||
wmud_client_quitanswer(wmudClient *client, gboolean answer)
|
||||
wmud_client_quitanswer(WmudClient *client, gboolean answer)
|
||||
{
|
||||
if (answer) {
|
||||
wmud_client_close(client, TRUE);
|
||||
remove_client(client, TRUE);
|
||||
} else {
|
||||
wmud_client_send(client, "Good boy!\r\n");
|
||||
client->state = WMUD_CLIENT_STATE_MENU;
|
||||
wmud_client_set_state(client, WMUD_CLIENT_STATE_MENU);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
wmud_client_newchar_answer(wmudClient *client, gboolean answer)
|
||||
wmud_client_newchar_answer(WmudClient *client, gboolean answer)
|
||||
{
|
||||
if (answer) {
|
||||
g_log(G_LOG_DOMAIN, G_LOG_LEVEL_DEBUG, "Creating new player\n");
|
||||
wmud_client_send(client, "Welcome to this MUD!\r\nPlease enter your e-mail address: ");
|
||||
client->state = WMUD_CLIENT_STATE_REGISTERING;
|
||||
wmud_client_set_state(client, WMUD_CLIENT_STATE_REGISTERING);
|
||||
} else {
|
||||
wmud_client_send(client, "What is your player-name, then? ");
|
||||
client->state = WMUD_CLIENT_STATE_FRESH;
|
||||
wmud_client_set_state(client, WMUD_CLIENT_STATE_FRESH);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
state_fresh(wmudClient *client)
|
||||
state_fresh(WmudClient *client)
|
||||
{
|
||||
if (*(client->buffer->str)) {
|
||||
if (*(wmud_client_get_buffer(client)->str)) {
|
||||
wmudPlayer *player;
|
||||
|
||||
if ((player = wmud_player_exists(client->buffer->str)) != NULL) {
|
||||
if ((player = wmud_player_exists(wmud_client_get_buffer(client)->str)) != NULL) {
|
||||
g_log(G_LOG_DOMAIN, G_LOG_LEVEL_DEBUG, "Trying to"
|
||||
" login with playername '%s'",
|
||||
client->buffer->str);
|
||||
wmud_client_get_buffer(client)->str);
|
||||
|
||||
if (player->cpassword == NULL) {
|
||||
wmud_client_send(client, "Your registration is"
|
||||
" not finished yet.\r\n");
|
||||
wmud_client_close(client, TRUE);
|
||||
remove_client(client, TRUE);
|
||||
} else {
|
||||
client->state = WMUD_CLIENT_STATE_PASSWAIT;
|
||||
client->player = player;
|
||||
client->player->registered = TRUE;
|
||||
wmud_client_set_state(client, WMUD_CLIENT_STATE_PASSWAIT);
|
||||
player->registered = TRUE;
|
||||
wmud_client_set_player(client, player);
|
||||
wmud_client_send(client, "Please provide us your password: %c%c%c", TELNET_IAC, TELNET_WILL, TELNET_ECHO);
|
||||
}
|
||||
} else {
|
||||
client->player = g_new0(wmudPlayer, 1);
|
||||
client->player->player_name = g_strdup(client->buffer->str);
|
||||
client->state = WMUD_CLIENT_STATE_YESNO;
|
||||
client->yesNoCallback = wmud_client_newchar_answer;
|
||||
wmud_client_send(client, "Is %s new to this game? [Y/N] ", client->buffer->str);
|
||||
wmudPlayer *player = g_new0(wmudPlayer, 1);
|
||||
player->player_name = g_strdup(wmud_client_get_buffer(client)->str);
|
||||
wmud_client_set_player(client, player);
|
||||
wmud_client_set_state(client, WMUD_CLIENT_STATE_YESNO);
|
||||
wmud_client_set_yesno_callback(client, wmud_client_newchar_answer);
|
||||
wmud_client_send(client, "Is %s new to this game? [Y/N] ", wmud_client_get_buffer(client)->str);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
state_passwait(wmudClient *client)
|
||||
state_passwait(WmudClient *client)
|
||||
{
|
||||
if (*(client->buffer->str)) {
|
||||
if (*(wmud_client_get_buffer(client)->str)) {
|
||||
if (wmud_player_auth(client)) {
|
||||
wmud_client_send(client, "%c%c%c\r\nLogin successful."
|
||||
"\r\n", TELNET_IAC, TELNET_WONT,
|
||||
TELNET_ECHO);
|
||||
client->authenticated = TRUE;
|
||||
wmud_client_set_authenticated(client, TRUE);
|
||||
|
||||
if (client->player->fail_count > 0)
|
||||
if (wmud_client_get_player(client)->fail_count > 0)
|
||||
wmud_client_send(client, "There %s %d failed"
|
||||
" login attempt%s with your"
|
||||
" account since your last"
|
||||
" visit\r\n", (client->player->fail_count == 1) ? "was" : "were", client->player->fail_count, (client->player->fail_count == 1) ? "" : "s");
|
||||
" visit\r\n",
|
||||
(wmud_client_get_player(client)->fail_count == 1) ? "was" : "were",
|
||||
wmud_client_get_player(client)->fail_count,
|
||||
(wmud_client_get_player(client)->fail_count == 1) ? "" : "s");
|
||||
|
||||
wmud_text_send_to_client("motd", client);
|
||||
wmud_menu_present(client);
|
||||
@ -496,22 +454,22 @@ state_passwait(wmudClient *client)
|
||||
" \r\nBy what name would you like to be"
|
||||
" be called? ", TELNET_IAC,
|
||||
TELNET_WONT, TELNET_ECHO);
|
||||
client->state = WMUD_CLIENT_STATE_FRESH;
|
||||
client->player->fail_count++;
|
||||
client->login_try_count++;
|
||||
if (client->login_try_count == 3) {
|
||||
wmud_client_set_state(client, WMUD_CLIENT_STATE_FRESH);
|
||||
wmud_client_get_player(client)->fail_count++;
|
||||
wmud_client_increase_login_fail_count(client);
|
||||
if (wmud_client_get_login_fail_count(client) == 3) {
|
||||
wmud_client_send(client, "You are trying "
|
||||
" these bad passwords for"
|
||||
" too many times. Please"
|
||||
" stop that!\r\n");
|
||||
wmud_client_close(client, TRUE);
|
||||
remove_client(client, TRUE);
|
||||
|
||||
/* TODO: Increase IP fail count, and ban IP if it's too high */
|
||||
}
|
||||
|
||||
/* TODO: Increase and save login fail count */
|
||||
|
||||
client->player = NULL;
|
||||
wmud_client_set_player(client, NULL);
|
||||
}
|
||||
} else {
|
||||
wmud_client_send(client, "\r\nEmpty passwords are"
|
||||
@ -520,37 +478,37 @@ state_passwait(wmudClient *client)
|
||||
}
|
||||
|
||||
static void
|
||||
state_menu(wmudClient *client)
|
||||
state_menu(WmudClient *client)
|
||||
{
|
||||
gchar *menu_command;
|
||||
|
||||
if ((menu_command = wmud_menu_get_command_by_menuchar(*(client->buffer->str), game_menu)) != NULL)
|
||||
if ((menu_command = wmud_menu_get_command_by_menuchar(*(wmud_client_get_buffer(client)->str), game_menu)) != NULL)
|
||||
wmud_menu_execute_command(client, menu_command);
|
||||
else
|
||||
wmud_client_send(client, "Unknown menu command.\r\n");
|
||||
}
|
||||
|
||||
static void
|
||||
state_yesno(wmudClient *client)
|
||||
state_yesno(WmudClient *client)
|
||||
{
|
||||
if (g_ascii_strcasecmp(client->buffer->str, "y") == 0)
|
||||
(client->yesNoCallback)(client, TRUE);
|
||||
else if (g_ascii_strcasecmp(client->buffer->str, "n") == 0)
|
||||
(client->yesNoCallback)(client, FALSE);
|
||||
if (g_ascii_strcasecmp(wmud_client_get_buffer(client)->str, "y") == 0)
|
||||
(wmud_client_get_yesno_callback(client))(client, TRUE);
|
||||
else if (g_ascii_strcasecmp(wmud_client_get_buffer(client)->str, "n") == 0)
|
||||
(wmud_client_get_yesno_callback(client))(client, FALSE);
|
||||
else
|
||||
wmud_client_send(client, "Please enter a 'Y' or 'N'"
|
||||
" character: ");
|
||||
}
|
||||
|
||||
static void
|
||||
state_registering(wmudClient *client)
|
||||
state_registering(WmudClient *client)
|
||||
{
|
||||
if (!*(client->buffer->str) && (client->bademail))
|
||||
wmud_client_close(client, TRUE);
|
||||
if (!*(wmud_client_get_buffer(client)->str) && (wmud_client_get_bademail(client) == TRUE))
|
||||
remove_client(client, TRUE);
|
||||
|
||||
if (g_regex_match(email_regex, client->buffer->str, 0, NULL)) {
|
||||
client->player->email = g_strdup(client->buffer->str);
|
||||
client->state = WMUD_CLIENT_STATE_REGEMAIL_CONFIRM;
|
||||
if (g_regex_match(email_regex, wmud_client_get_buffer(client)->str, 0, NULL)) {
|
||||
wmud_client_get_player(client)->email = g_strdup(wmud_client_get_buffer(client)->str);
|
||||
wmud_client_set_state(client, WMUD_CLIENT_STATE_REGEMAIL_CONFIRM);
|
||||
wmud_client_send(client, "It seems to be a valid"
|
||||
" address to me, but could you"
|
||||
" write it again? ");
|
||||
@ -567,32 +525,32 @@ state_registering(wmudClient *client)
|
||||
" it now: ",
|
||||
active_config->admin_email);
|
||||
|
||||
if (*(client->buffer->str))
|
||||
client->bademail = TRUE;
|
||||
if (*(wmud_client_get_buffer(client)->str))
|
||||
wmud_client_set_bademail(client, TRUE);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
state_regemail_confirm(wmudClient *client)
|
||||
state_regemail_confirm(WmudClient *client)
|
||||
{
|
||||
GError *err = NULL;
|
||||
|
||||
if (g_ascii_strcasecmp(client->player->email, client->buffer->str) == 0) {
|
||||
if (wmud_db_save_player(client->player, &err)) {
|
||||
if (g_ascii_strcasecmp(wmud_client_get_player(client)->email, wmud_client_get_buffer(client)->str) == 0) {
|
||||
if (wmud_db_save_player(wmud_client_get_player(client), &err)) {
|
||||
wmud_client_send(client, "Good. We will generate the password for this player name, and send it to you\r\nvia e-mail. Please come back to us, if you get that code, so you can log\r\nin.\r\n");
|
||||
players = g_slist_prepend(players, wmud_player_dup(client->player));
|
||||
players = g_slist_prepend(players, wmud_player_dup(wmud_client_get_player(client)));
|
||||
} else {
|
||||
g_critical("wmud_db_save_player() error: %s", err->message);
|
||||
wmud_client_send(client, "There was an error during the database update. Please try again later!\r\n");
|
||||
}
|
||||
|
||||
wmud_client_close(client, TRUE);
|
||||
remove_client(client, TRUE);
|
||||
} else {
|
||||
g_free(client->player->email);
|
||||
client->player->email = NULL;
|
||||
g_free(wmud_client_get_player(client)->email);
|
||||
wmud_client_get_player(client)->email = NULL;
|
||||
|
||||
wmud_client_send(client, "This is not the same as you entered before.\r\nLet's just try it again: ");
|
||||
client->state = WMUD_CLIENT_STATE_REGISTERING;
|
||||
wmud_client_set_state(client, WMUD_CLIENT_STATE_REGISTERING);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -21,6 +21,7 @@
|
||||
|
||||
#include <glib.h>
|
||||
|
||||
#include "wmudclient.h"
|
||||
#include "wmud-types.h"
|
||||
|
||||
/**
|
||||
@ -63,8 +64,7 @@
|
||||
extern GSList *clients;
|
||||
|
||||
gboolean wmud_networking_init(guint port_number, GMainContext *game_context, GSList *menu_items, GError **err);
|
||||
void wmud_client_send(wmudClient *client, const gchar *fmt, ...);
|
||||
void wmud_client_quitanswer(wmudClient *client, gboolean answer);
|
||||
void wmud_client_newchar_answer(wmudClient *client, gboolean answer);
|
||||
void wmud_client_quitanswer(WmudClient *client, gboolean answer);
|
||||
void wmud_client_newchar_answer(WmudClient *client, gboolean answer);
|
||||
|
||||
#endif
|
||||
|
@ -148,13 +148,13 @@ wmud_interpreter_check_directions(GSList *directions, GError **err)
|
||||
|
||||
/**
|
||||
* wmud_interpret_game_command:
|
||||
* @client: the wmudClient whose command should be processed
|
||||
* @client: the WmudClient whose command should be processed
|
||||
*
|
||||
* Processes a wmudClient's buffer, and executes the game command if there is
|
||||
* Processes a WmudClient's buffer, and executes the game command if there is
|
||||
* one
|
||||
*/
|
||||
void
|
||||
wmud_interpret_game_command(wmudClient *client)
|
||||
wmud_interpret_game_command(WmudClient *client)
|
||||
{
|
||||
GSList *command_parts = NULL;
|
||||
gchar *a,
|
||||
@ -166,14 +166,14 @@ wmud_interpret_game_command(wmudClient *client)
|
||||
match_count = 0;
|
||||
GSList *matches = NULL;
|
||||
|
||||
if (strchr(client->buffer->str, '\r') || strchr(client->buffer->str, '\n')) {
|
||||
if (strchr(wmud_client_get_buffer(client)->str, '\r') || strchr(wmud_client_get_buffer(client)->str, '\n')) {
|
||||
/* We should NEVER reach this point! */
|
||||
g_assert_not_reached();
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
a = client->buffer->str;
|
||||
a = wmud_client_get_buffer(client)->str;
|
||||
|
||||
GString *token;
|
||||
|
||||
@ -282,6 +282,6 @@ wmud_interpret_game_command(wmudClient *client)
|
||||
WMUD_COMMAND(quit)
|
||||
{
|
||||
wmud_client_send(client, "Are you sure you want to get back to that freaky other reality? [y/N] ");
|
||||
client->state = WMUD_CLIENT_STATE_YESNO;
|
||||
client->yesNoCallback = wmud_client_quitanswer;
|
||||
wmud_client_set_state(client, WMUD_CLIENT_STATE_YESNO);
|
||||
wmud_client_set_yesno_callback(client, wmud_client_quitanswer);
|
||||
}
|
||||
|
@ -29,14 +29,14 @@
|
||||
*
|
||||
* Command handler function type
|
||||
*/
|
||||
typedef void (*wmudCommandFunc)(wmudClient *client, gchar *command, GSList *token_list);
|
||||
typedef void (*wmudCommandFunc)(WmudClient *client, gchar *command, GSList *token_list);
|
||||
/**
|
||||
* WMUD_COMMAND:
|
||||
* @name: the name of the command. Should be in lowercase
|
||||
*
|
||||
* Shorthand to create the function header for command handlers
|
||||
*/
|
||||
#define WMUD_COMMAND(name) void gcmd_ ## name(wmudClient *client, gchar *command, GSList *token_list)
|
||||
#define WMUD_COMMAND(name) void gcmd_ ## name(WmudClient *client, gchar *command, GSList *token_list)
|
||||
|
||||
/**
|
||||
* wmudCommand:
|
||||
@ -59,7 +59,7 @@ typedef enum {
|
||||
} wmudInterpreterError;
|
||||
|
||||
gboolean wmud_interpreter_check_directions(GSList *directions, GError **err);
|
||||
void wmud_interpret_game_command(wmudClient *client);
|
||||
void wmud_interpret_game_command(WmudClient *client);
|
||||
|
||||
#endif /* __WMUD_INTERPRETER_H__ */
|
||||
|
||||
|
12
wmud/menu.c
12
wmud/menu.c
@ -189,9 +189,9 @@ WMUD_MENU_COMMAND(change_name)
|
||||
|
||||
WMUD_MENU_COMMAND(quit)
|
||||
{
|
||||
client->state = WMUD_CLIENT_STATE_YESNO;
|
||||
wmud_client_set_state(client, WMUD_CLIENT_STATE_YESNO);
|
||||
wmud_client_set_yesno_callback(client, wmud_client_quitanswer);
|
||||
wmud_client_send(client, "Are you sure you want to get back to the real world? [y/N] ");
|
||||
client->yesNoCallback = wmud_client_quitanswer;
|
||||
}
|
||||
|
||||
WMUD_MENU_COMMAND(redisplay_menu)
|
||||
@ -277,7 +277,7 @@ wmud_menu_get_command_by_menuchar(gchar menuchar, GSList *game_menu)
|
||||
}
|
||||
|
||||
void
|
||||
wmud_menu_execute_command(wmudClient *client, gchar *command)
|
||||
wmud_menu_execute_command(WmudClient *client, gchar *command)
|
||||
{
|
||||
wmudMenuCommandFunc func;
|
||||
|
||||
@ -288,17 +288,17 @@ wmud_menu_execute_command(wmudClient *client, gchar *command)
|
||||
}
|
||||
|
||||
void
|
||||
send_menu_item(wmudMenu *item, wmudClient *client)
|
||||
send_menu_item(wmudMenu *item, WmudClient *client)
|
||||
{
|
||||
/* TODO: Send ANSI menu item only to ANSI players! */
|
||||
wmud_client_send(client, "%s\r\n", item->display_text_ansi);
|
||||
}
|
||||
|
||||
void
|
||||
wmud_menu_present(wmudClient *client)
|
||||
wmud_menu_present(WmudClient *client)
|
||||
{
|
||||
g_slist_foreach(game_menu, (GFunc)send_menu_item, client);
|
||||
client->state = WMUD_CLIENT_STATE_MENU;
|
||||
wmud_client_set_state(client, WMUD_CLIENT_STATE_MENU);
|
||||
|
||||
/* TODO: send menu prologue */
|
||||
}
|
||||
|
@ -49,14 +49,14 @@ typedef struct _wmudMenu {
|
||||
gchar *func;
|
||||
} wmudMenu;
|
||||
|
||||
typedef void (*wmudMenuCommandFunc)(wmudClient *client);
|
||||
typedef void (*wmudMenuCommandFunc)(WmudClient *client);
|
||||
/**
|
||||
* WMUD_MENU_COMMAND:
|
||||
* @name: the name of the command. Should be in lowercase
|
||||
*
|
||||
* Shorthand to create the function header for menu command handlers
|
||||
*/
|
||||
#define WMUD_MENU_COMMAND(name) void wmud_mcmd_ ## name(wmudClient *client)
|
||||
#define WMUD_MENU_COMMAND(name) void wmud_mcmd_ ## name(WmudClient *client)
|
||||
|
||||
GSList *game_menu;
|
||||
|
||||
@ -66,8 +66,8 @@ gboolean wmud_menu_init(GSList **menu);
|
||||
gboolean wmud_menu_items_check(GSList *menu_items, GError **err);
|
||||
void wmud_menu_items_free(GSList **menu_items);
|
||||
gchar *wmud_menu_get_command_by_menuchar(gchar menuchar, GSList *game_menu);
|
||||
void wmud_menu_execute_command(wmudClient *client, gchar *command);
|
||||
void wmud_menu_present(wmudClient *client);
|
||||
void wmud_menu_execute_command(WmudClient *client, gchar *command);
|
||||
void wmud_menu_present(WmudClient *client);
|
||||
|
||||
#endif /* __WMUD_MENU_H__ */
|
||||
|
||||
|
@ -53,9 +53,9 @@ GSList *players = NULL;
|
||||
* Return value: %TRUE if the password is valid, %FALSE otherwise.
|
||||
*/
|
||||
gboolean
|
||||
wmud_player_auth(wmudClient *client)
|
||||
wmud_player_auth(WmudClient *client)
|
||||
{
|
||||
if (g_strcmp0(crypt(client->buffer->str, client->player->cpassword), client->player->cpassword) == 0)
|
||||
if (g_strcmp0(crypt(wmud_client_get_buffer(client)->str, wmud_client_get_player(client)->cpassword), wmud_client_get_player(client)->cpassword) == 0)
|
||||
return TRUE;
|
||||
|
||||
return FALSE;
|
||||
|
@ -22,10 +22,11 @@
|
||||
#include <glib.h>
|
||||
|
||||
#include "wmud-types.h"
|
||||
#include "wmudclient.h"
|
||||
|
||||
extern GSList *players;
|
||||
|
||||
gboolean wmud_player_auth(wmudClient *client);
|
||||
gboolean wmud_player_auth(WmudClient *client);
|
||||
wmudPlayer *wmud_player_exists(gchar *player_name);
|
||||
void wmud_player_free(wmudPlayer **player);
|
||||
wmudPlayer *wmud_player_dup(wmudPlayer *player);
|
||||
|
@ -78,7 +78,7 @@ wmud_texts_init(void)
|
||||
}
|
||||
|
||||
void
|
||||
wmud_text_send_to_client(gchar *text_name, wmudClient *client)
|
||||
wmud_text_send_to_client(gchar *text_name, WmudClient *client)
|
||||
{
|
||||
gchar *text = g_hash_table_lookup(text_table, text_name);
|
||||
wmud_client_send(client, "%s\r\n", text);
|
||||
|
@ -22,6 +22,6 @@
|
||||
#include "wmud-types.h"
|
||||
|
||||
void wmud_texts_init(void);
|
||||
void wmud_text_send_to_client(gchar *text, wmudClient *client);
|
||||
void wmud_text_send_to_client(gchar *text, WmudClient *client);
|
||||
|
||||
#endif /* __WMUD_TEXTS_H__ */
|
||||
|
@ -83,38 +83,5 @@ typedef struct _wmudPlayer {
|
||||
gboolean registered;
|
||||
} wmudPlayer;
|
||||
|
||||
typedef struct _wmudClient wmudClient;
|
||||
|
||||
typedef void (*wmudYesNoCallback)(wmudClient *client, gboolean answer);
|
||||
|
||||
/**
|
||||
* wmudClient:
|
||||
* @socket: The assigned GSocket object
|
||||
* @buffer: The client receive buffer. It may hold partial or multiple lines
|
||||
* until processed
|
||||
* @state: The state of the client
|
||||
* @authenticated: TRUE if the client is an authenticated game player
|
||||
* @player: The associatec player structure. It is also used during
|
||||
* registration, so it should be always checked if the player is a saved
|
||||
* database user
|
||||
* @bademail: indicates that the entered e-mail address is invalid
|
||||
* @socket_source: the #GSource associated with the client socket
|
||||
* @login_try_count: the failed login count of the client
|
||||
*
|
||||
* <structname>wmudClient</structname> contains all properties of a connected
|
||||
* game client.
|
||||
*/
|
||||
struct _wmudClient {
|
||||
GSocket *socket;
|
||||
GSource *socket_source;
|
||||
GString *buffer;
|
||||
wmudClientState state;
|
||||
gboolean authenticated;
|
||||
wmudPlayer *player;
|
||||
gboolean bademail;
|
||||
gint login_try_count;
|
||||
wmudYesNoCallback yesNoCallback;
|
||||
};
|
||||
|
||||
#endif /* __WMUD_TYPES_H__ */
|
||||
|
||||
|
237
wmud/wmudclient.c
Normal file
237
wmud/wmudclient.c
Normal file
@ -0,0 +1,237 @@
|
||||
/* wMUD - Yet another MUD codebase by W00d5t0ck
|
||||
* Copyright (C) 2012 - Gergely POLONKAI
|
||||
*
|
||||
* wmudclient.c: the WmudClient GObject type
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#include "wmudclient.h"
|
||||
|
||||
#include "players.h"
|
||||
|
||||
#define WMUD_CLIENT_GET_PRIVATE(obj) (G_TYPE_INSTANCE_GET_PRIVATE((obj), WMUD_TYPE_CLIENT, WmudClientPrivate))
|
||||
|
||||
struct _WmudClientPrivate
|
||||
{
|
||||
GSocket *socket;
|
||||
GSource *socket_source;
|
||||
GString *buffer;
|
||||
wmudClientState state;
|
||||
gboolean authenticated;
|
||||
wmudPlayer *player;
|
||||
gboolean bademail;
|
||||
gint login_try_count;
|
||||
WmudClientYesnoCallback yesno_callback;
|
||||
};
|
||||
|
||||
G_DEFINE_TYPE(WmudClient, wmud_client, G_TYPE_OBJECT);
|
||||
|
||||
static void
|
||||
wmud_client_dispose(GObject *gobject)
|
||||
{
|
||||
WmudClient *self = WMUD_CLIENT(gobject);
|
||||
|
||||
if (self->priv->socket) {
|
||||
g_object_unref(self->priv->socket);
|
||||
|
||||
self->priv->socket = NULL;
|
||||
}
|
||||
|
||||
G_OBJECT_CLASS(wmud_client_parent_class)->dispose(gobject);
|
||||
}
|
||||
|
||||
static void
|
||||
wmud_client_finalize(GObject *gobject)
|
||||
{
|
||||
WmudClient *self = WMUD_CLIENT(gobject);
|
||||
|
||||
g_string_free(self->priv->buffer, TRUE);
|
||||
g_source_destroy(self->priv->socket_source);
|
||||
|
||||
G_OBJECT_CLASS(wmud_client_parent_class)->finalize(gobject);
|
||||
}
|
||||
|
||||
static void
|
||||
wmud_client_class_init(WmudClientClass *klass)
|
||||
{
|
||||
GObjectClass *gobject_class = G_OBJECT_CLASS(klass);
|
||||
|
||||
gobject_class->dispose = wmud_client_dispose;
|
||||
gobject_class->finalize = wmud_client_finalize;
|
||||
|
||||
g_type_class_add_private(klass, sizeof(WmudClientPrivate));
|
||||
}
|
||||
|
||||
static void
|
||||
wmud_client_init(WmudClient *self)
|
||||
{
|
||||
self->priv = WMUD_CLIENT_GET_PRIVATE(self);
|
||||
self->priv->socket_source = NULL;
|
||||
self->priv->state = WMUD_CLIENT_STATE_FRESH;
|
||||
self->priv->buffer = g_string_new("");
|
||||
}
|
||||
|
||||
WmudClient *
|
||||
wmud_client_new(void)
|
||||
{
|
||||
return g_object_new(WMUD_TYPE_CLIENT, NULL, NULL);
|
||||
}
|
||||
|
||||
void
|
||||
wmud_client_set_socket(WmudClient *self, GSocket *socket)
|
||||
{
|
||||
/* TODO: Check if a socket is already set! */
|
||||
self->priv->socket = socket;
|
||||
self->priv->socket_source = g_socket_create_source(socket, G_IO_IN | G_IO_OUT | G_IO_PRI | G_IO_ERR | G_IO_HUP | G_IO_NVAL, NULL);
|
||||
}
|
||||
|
||||
GSocket *
|
||||
wmud_client_get_socket(WmudClient *self)
|
||||
{
|
||||
return self->priv->socket;
|
||||
}
|
||||
|
||||
GSource *
|
||||
wmud_client_get_socket_source(WmudClient *self)
|
||||
{
|
||||
return self->priv->socket_source;
|
||||
}
|
||||
|
||||
/**
|
||||
* wmud_client_send:
|
||||
* @client: the client to which the message will be sent
|
||||
* @fmt: the printf() style format string of the message
|
||||
* @...: optional parameters to the format string
|
||||
*
|
||||
* Sends a formatted message to a game client
|
||||
*/
|
||||
void
|
||||
wmud_client_send(WmudClient *self, const gchar *fmt, ...)
|
||||
{
|
||||
va_list ap;
|
||||
GString *buf = g_string_new("");
|
||||
|
||||
va_start(ap, fmt);
|
||||
g_string_vprintf(buf, fmt, ap);
|
||||
va_end(ap);
|
||||
|
||||
/* TODO: error checking */
|
||||
g_socket_send(self->priv->socket, buf->str, buf->len, NULL, NULL);
|
||||
g_string_free(buf, TRUE);
|
||||
}
|
||||
|
||||
/**
|
||||
* wmud_client_close:
|
||||
* @client: the client whose connection should be dropped
|
||||
* @send_goodbye: if set to %TRUE, we will send a nice good-bye message to the
|
||||
* client before dropping the connection
|
||||
*
|
||||
* Closes a client connection. If send_goodbye is set to %TRUE, a good-bye
|
||||
* message will be sent to the client.
|
||||
*/
|
||||
void
|
||||
wmud_client_close(WmudClient *self, gboolean send_goodbye)
|
||||
{
|
||||
if (send_goodbye)
|
||||
wmud_client_send(self, "\r\nHave a nice real-world day!\r\n\r\n");
|
||||
|
||||
g_socket_shutdown(self->priv->socket, TRUE, TRUE, NULL);
|
||||
g_log(G_LOG_DOMAIN, G_LOG_LEVEL_INFO, "Connection closed.");
|
||||
|
||||
/* TODO: player->registered should be a wmud_player_get_registered()
|
||||
* call after wmudPlayer is migrated to be a GObject */
|
||||
if (self->priv->player && !self->priv->player->registered)
|
||||
wmud_player_free(&(self->priv->player));
|
||||
|
||||
g_object_unref(self);
|
||||
}
|
||||
|
||||
gsize
|
||||
wmud_client_get_buffer_length(WmudClient *self)
|
||||
{
|
||||
return self->priv->buffer->len;
|
||||
}
|
||||
|
||||
GString *
|
||||
wmud_client_get_buffer(WmudClient *self)
|
||||
{
|
||||
return self->priv->buffer;
|
||||
}
|
||||
|
||||
wmudClientState
|
||||
wmud_client_get_state(WmudClient *self)
|
||||
{
|
||||
return self->priv->state;
|
||||
}
|
||||
|
||||
void
|
||||
wmud_client_set_state(WmudClient *self, wmudClientState state)
|
||||
{
|
||||
self->priv->state = state;
|
||||
}
|
||||
|
||||
void
|
||||
wmud_client_set_player(WmudClient *self, wmudPlayer *player)
|
||||
{
|
||||
self->priv->player = player;
|
||||
}
|
||||
|
||||
wmudPlayer *
|
||||
wmud_client_get_player(WmudClient *self)
|
||||
{
|
||||
return self->priv->player;
|
||||
}
|
||||
|
||||
void
|
||||
wmud_client_set_yesno_callback(WmudClient *self, WmudClientYesnoCallback yesno_callback)
|
||||
{
|
||||
self->priv->yesno_callback = yesno_callback;
|
||||
}
|
||||
|
||||
WmudClientYesnoCallback
|
||||
wmud_client_get_yesno_callback(WmudClient *self)
|
||||
{
|
||||
return self->priv->yesno_callback;
|
||||
}
|
||||
|
||||
void
|
||||
wmud_client_set_authenticated(WmudClient *self, gboolean authenticated)
|
||||
{
|
||||
self->priv->authenticated = authenticated;
|
||||
}
|
||||
|
||||
void
|
||||
wmud_client_increase_login_fail_count(WmudClient *self)
|
||||
{
|
||||
self->priv->login_try_count++;
|
||||
}
|
||||
|
||||
gint
|
||||
wmud_client_get_login_fail_count(WmudClient *self)
|
||||
{
|
||||
return self->priv->login_try_count;
|
||||
}
|
||||
|
||||
void
|
||||
wmud_client_set_bademail(WmudClient *self, gboolean bademail)
|
||||
{
|
||||
self->priv->bademail = bademail;
|
||||
}
|
||||
|
||||
gboolean
|
||||
wmud_client_get_bademail(WmudClient *self)
|
||||
{
|
||||
return self->priv->bademail;
|
||||
}
|
||||
|
74
wmud/wmudclient.h
Normal file
74
wmud/wmudclient.h
Normal file
@ -0,0 +1,74 @@
|
||||
/* wMUD - Yet another MUD codebase by W00d5t0ck
|
||||
* Copyright (C) 2012 - Gergely POLONKAI
|
||||
*
|
||||
* wmudclient.h: the WmudClient GObject type
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#ifndef __WMUD_WMUDCLIENT_H__
|
||||
#define __WMUD_WMUDCLIENT_H__
|
||||
|
||||
#include <glib-object.h>
|
||||
#include <glib.h>
|
||||
#include <gio/gio.h>
|
||||
#include "wmud-types.h"
|
||||
|
||||
#define WMUD_TYPE_CLIENT (wmud_client_get_type())
|
||||
#define WMUD_CLIENT(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), WMUD_TYPE_CLIENT, WmudClient))
|
||||
#define WMUD_IS_CLIENT(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), WMUD_TYPE_CLIENT))
|
||||
#define WMUD_TYPE_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass), WMUD_TYPE_CLIENT, WmudClientClass))
|
||||
#define WMUD_IS_CLIENT_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass), WMUD_TYPE_CLIENT))
|
||||
#define WMUD_CLIENT_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS((obj), WMUT_TYPE_CLIENT, WmudClientClass))
|
||||
|
||||
typedef struct _WmudClient WmudClient;
|
||||
typedef struct _WmudClientClass WmudClientClass;
|
||||
typedef struct _WmudClientPrivate WmudClientPrivate;
|
||||
typedef void (*WmudClientYesnoCallback)(WmudClient *client, gboolean answer);
|
||||
|
||||
struct _WmudClient
|
||||
{
|
||||
GObject parent_instance;
|
||||
|
||||
/*< private >*/
|
||||
WmudClientPrivate *priv;
|
||||
};
|
||||
|
||||
struct _WmudClientClass
|
||||
{
|
||||
GObjectClass parent_class;
|
||||
};
|
||||
|
||||
GType wmud_client_get_type(void);
|
||||
WmudClient *wmud_client_new(void);
|
||||
void wmud_client_set_socket(WmudClient *client, GSocket *socket);
|
||||
GSocket *wmud_client_get_socket(WmudClient *client);
|
||||
GSource *wmud_client_get_socket_source(WmudClient *client);
|
||||
void wmud_client_send(WmudClient *client, const gchar *fmt, ...);
|
||||
void wmud_client_close(WmudClient *self, gboolean send_goodbye);
|
||||
GString *wmud_client_get_buffer(WmudClient *client);
|
||||
gsize wmud_client_get_buffer_length(WmudClient *client);
|
||||
void wmud_client_set_state(WmudClient *client, wmudClientState state);
|
||||
wmudClientState wmud_client_get_state(WmudClient *client);
|
||||
void wmud_client_set_player(WmudClient *client, wmudPlayer *player);
|
||||
wmudPlayer *wmud_client_get_player(WmudClient *client);
|
||||
void wmud_client_set_yesno_callback(WmudClient *client, WmudClientYesnoCallback yesno_callback);
|
||||
WmudClientYesnoCallback wmud_client_get_yesno_callback(WmudClient *client);
|
||||
void wmud_client_set_authenticated(WmudClient *client, gboolean authenticated);
|
||||
void wmud_client_increase_login_fail_count(WmudClient *client);
|
||||
gint wmud_client_get_login_fail_count(WmudClient *client);
|
||||
void wmud_client_set_bademail(WmudClient *client, gboolean bademail);
|
||||
gboolean wmud_client_get_bademail(WmudClient *client);
|
||||
|
||||
#endif /* __WMUD_WMUDMENU_H__ */
|
||||
|
Loading…
Reference in New Issue
Block a user