diff options
author | Markus Uhlin <markus@nifty-networks.net> | 2023-12-07 21:31:49 +0100 |
---|---|---|
committer | Markus Uhlin <markus@nifty-networks.net> | 2023-12-07 21:31:49 +0100 |
commit | 79b59f9b30fb6a1fdf8c3efb446271f7cb00d434 (patch) | |
tree | f6ade4ccbc3af20d825edacfd12b5da8ded8d240 /FICS/command.c |
FICS 1.6.2
Diffstat (limited to 'FICS/command.c')
-rw-r--r-- | FICS/command.c | 1080 |
1 files changed, 1080 insertions, 0 deletions
diff --git a/FICS/command.c b/FICS/command.c new file mode 100644 index 0000000..a53f305 --- /dev/null +++ b/FICS/command.c @@ -0,0 +1,1080 @@ +/* command.c + * + */ + +/* + fics - An internet chess server. + Copyright (C) 1993 Richard V. Nash + + 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. +*/ + +/* Revision history: + name email yy/mm/dd Change + Richard Nash 93/10/22 Created +*/ + +#include "stdinclude.h" + +#include "common.h" +#include "rmalloc.h" +#include "command.h" +#include "command_list.h" +#include "movecheck.h" +#include "ficsmain.h" +#include "config.h" +#include "utils.h" +#include "playerdb.h" +#include "gamedb.h" +#include "gameproc.h" +#include "obsproc.h" +#include "ratings.h" +#include "vers.h" +#include "network.h" +#include "shutdown.h" +#include <sys/param.h> +#ifdef TIMESEAL +#include "timeseal.h" +#endif +/*#include "hostinfo.h" */ + +PUBLIC char *mess_dir = DEFAULT_MESS; +PUBLIC char *index_dir = DEFAULT_INDEX; +PUBLIC char *help_dir[NUM_LANGS] = {DEFAULT_HELP, HELP_SPANISH, HELP_FRENCH, HELP_DANISH}; +PUBLIC char *comhelp_dir = DEFAULT_COMHELP; +PUBLIC char *info_dir = DEFAULT_INFO; +PUBLIC char *adhelp_dir = DEFAULT_ADHELP; +PUBLIC char *uscf_dir = DEFAULT_USCF; +PUBLIC char *stats_dir = DEFAULT_STATS; +PUBLIC char *player_dir = DEFAULT_PLAYERS; +PUBLIC char *adj_dir = DEFAULT_ADJOURNED; +PUBLIC char *hist_dir = DEFAULT_HISTORY; +PUBLIC char *journal_dir = DEFAULT_JOURNAL; +PUBLIC char *board_dir = DEFAULT_BOARDS; +PUBLIC char *def_prompt = DEFAULT_PROMPT; +PUBLIC char *source_dir = DEFAULT_SOURCE; +PUBLIC char *lists_dir = DEFAULT_LISTS; +PUBLIC char *news_dir = DEFAULT_NEWS; +PUBLIC char *usage_dir[NUM_LANGS] = {DEFAULT_USAGE, USAGE_SPANISH, USAGE_FRENCH, USAGE_DANISH}; + +PUBLIC char *hadmin_handle = HADMINHANDLE; +char *hadmin_email = HADMINEMAIL; +char *reg_addr = REGMAIL; + +PUBLIC int startuptime; +PUBLIC char fics_hostname[81]; +PUBLIC int player_high; +PUBLIC int game_high; +PUBLIC int MailGameResult; + +PUBLIC int commanding_player = -1; /* The player whose command your in */ + +PRIVATE int lastCommandFound = -1; + +/* grimm */ +int player_notify_departure(int p); +/* added for warnings */ + +/* Copies command into comm, and returns pointer to parameters in + * parameters + */ +PRIVATE int parse_command(char *com_string, + char **comm, + char **parameters) +{ + *comm = com_string; + *parameters = eatword(com_string); + if (**parameters != '\0') { + **parameters = '\0'; + (*parameters)++; + *parameters = eatwhite(*parameters); + } + if (strlen(*comm) >= MAX_COM_LENGTH) { + return COM_BADCOMMAND; + } + return COM_OK; +} + +PUBLIC int alias_lookup(char *tmp, alias_type *alias_list, int numalias) +/* numalias is the maximum number to search through */ +{ + int i; + + for (i = 0; (alias_list[i].comm_name && i < numalias); i++) { + if (!strcmp(tmp, alias_list[i].comm_name)) + return i; + } + return -1; /* not found */ +} + +PUBLIC int alias_count(alias_type *alias_list) +{ + int i; + + for (i = 0; alias_list[i].comm_name; i++); + return i; +} + + +/* Puts alias substitution into alias_string */ +PRIVATE void alias_substitute(alias_type *alias_list, int num_alias, + char *com_str, char outalias[]) +{ + char *s = com_str; + char name[MAX_COM_LENGTH]; + char *t = name; + int i = 0; + char *atpos, *aliasval; + + /* Get first word of command, terminated by whitespace or by containing + punctuation */ + while (*s && !iswhitespace(*s)) { + if (i++ >= MAX_COM_LENGTH) { + strcpy(outalias, com_str); + return; + } + if (ispunct(*t++ = *s++)) + break; + } + *t = '\0'; + if (*s && iswhitespace(*s)) + s++; + + i = alias_lookup(name, alias_list, num_alias); + if (i < 0) { + strcpy(outalias, com_str); + return; + } + aliasval = alias_list[i].alias; + + /* See if alias contains an @ */ + atpos = strchr(aliasval, '@'); + if (atpos != NULL) { + strncpy(outalias, aliasval, atpos - aliasval); + outalias[atpos - aliasval] = '\0'; + strcat(outalias, s); + strcat(outalias, atpos + 1); + } else { + strcpy(outalias, aliasval); + if (*s) { + strcat(outalias, " "); + strcat(outalias, s); + } + } +} + +/* Returns pointer to command that matches */ +PRIVATE int match_command(char *comm, int p) +{ + int i = 0; + int gotIt = -1; + int len = strlen(comm); + + while (command_list[i].comm_name) { + if (!strncmp(command_list[i].comm_name, comm, len) + && parray[p].adminLevel >= command_list[i].adminLevel) { + if (gotIt >= 0) + return -COM_AMBIGUOUS; + gotIt = i; + } + i++; + } + if (in_list(p, L_REMOVEDCOM, command_list[gotIt].comm_name)) { + pprintf(p, "Due to a bug - this command has been temporarily removed.\n"); + return -COM_FAILED; + } + if (gotIt >= 0) { + lastCommandFound = gotIt; + return gotIt; + } + return -COM_FAILED; +} + +/* Gets the parameters for this command */ +PRIVATE int get_parameters(int command, char *parameters, param_list params) +{ + int i, parlen; + int paramLower; + char c; + static char punc[2]; + + punc[1] = '\0'; /* Holds punc parameters */ + for (i = 0; i < MAXNUMPARAMS; i++) + (params)[i].type = TYPE_NULL; /* Set all parameters to NULL */ + parlen = strlen(command_list[command].param_string); + for (i = 0; i < parlen; i++) { + c = command_list[command].param_string[i]; + if (isupper(c)) { + paramLower = 0; + c = tolower(c); + } else { + paramLower = 1; + } + switch (c) { + case 'w': + case 'o': /* word or optional word */ + parameters = eatwhite(parameters); + if (!*parameters) + return (c == 'o' ? COM_OK : COM_BADPARAMETERS); + (params)[i].val.word = parameters; + (params)[i].type = TYPE_WORD; + if (ispunct(*parameters)) { + punc[0] = *parameters; + (params)[i].val.word = punc; + parameters++; + if (*parameters && iswhitespace(*parameters)) + parameters++; + } else { + parameters = eatword(parameters); + if (*parameters != '\0') { + *parameters = '\0'; + parameters++; + } + } + if (paramLower) + stolower((params)[i].val.word); + break; + + case 'd': + case 'p': /* optional or required integer */ + parameters = eatwhite(parameters); + if (!*parameters) + return (c == 'p' ? COM_OK : COM_BADPARAMETERS); + if (sscanf(parameters, "%d", &(params)[i].val.integer) != 1) + return COM_BADPARAMETERS; + (params)[i].type = TYPE_INT; + parameters = eatword(parameters); + if (*parameters != '\0') { + *parameters = '\0'; + parameters++; + } + break; + + case 'i': + case 'n': /* optional or required word or integer */ + parameters = eatwhite(parameters); + if (!*parameters) + return (c == 'n' ? COM_OK : COM_BADPARAMETERS); + if (sscanf(parameters, "%d", &(params)[i].val.integer) != 1) { + (params)[i].val.word = parameters; + (params)[i].type = TYPE_WORD; + } else { + (params)[i].type = TYPE_INT; + } + if (ispunct(*parameters)) { + punc[0] = *parameters; + (params)[i].val.word = punc; + (params)[i].type = TYPE_WORD; + parameters++; + if (*parameters && iswhitespace(*parameters)) + parameters++; + } else { + parameters = eatword(parameters); + if (*parameters != '\0') { + *parameters = '\0'; + parameters++; + } + } + if ((params)[i].type == TYPE_WORD) + if (paramLower) + stolower((params)[i].val.word); + break; + + case 's': + case 't': /* optional or required string to end */ + if (!*parameters) + return (c == 't' ? COM_OK : COM_BADPARAMETERS); + (params)[i].val.string = parameters; + (params)[i].type = TYPE_STRING; + while (*parameters) + parameters = nextword(parameters); + if (paramLower) + stolower((params)[i].val.string); + break; + } + } + if (*parameters) + return COM_BADPARAMETERS; + else + return COM_OK; +} + +PRIVATE void printusage(int p, char *command_str) +{ + int i, parlen, UseLang = parray[p].language; + int command; + char c; + + char *filenames[1000]; /* enough for all usage names */ + + if ((command = match_command(command_str, p)) < 0) { + pprintf(p, " UNKNOWN COMMAND\n"); + return; + } + +/*Usage added by DAV 11/19/95 */ + /* First lets check if we have a text usage file for it */ + + i = search_directory(usage_dir[UseLang], command_str, filenames, 1000); + if (i == 0) { /* nope none in current Lang */ + if (UseLang != LANG_DEFAULT) { + i += search_directory(usage_dir[LANG_DEFAULT], command_str, filenames, 1000); + if (i > 0) { + pprintf(p, "No usage available in %s; using %s instead.\n", + Language(UseLang), Language(LANG_DEFAULT)); + UseLang = LANG_DEFAULT; + } + } + } + + if (i != 0) { + if ((i == 1) || (!strcmp(*filenames, command_str))) { /* found it? then send */ + if (psend_file(p, usage_dir[UseLang], *filenames)) { + /* we should never reach this unless the file was just deleted */ + pprintf(p, "Usage file %s could not be found! ", *filenames); + pprintf(p, "Please inform an admin of this. Thank you.\n"); + /* no need to print 'system' usage - should never happen */ + } + return; + } + } + + /* print the default 'system' usage files (which aren't much help!) */ + + pprintf(p, "Usage: %s", command_list[lastCommandFound].comm_name); + + parlen = strlen(command_list[command].param_string); + for (i = 0; i < parlen; i++) { + c = command_list[command].param_string[i]; + if (isupper(c)) + c = tolower(c); + switch (c) { + case 'w': /* word */ + pprintf(p, " word"); + break; + case 'o': /* optional word */ + pprintf(p, " [word]"); + break; + case 'd': /* integer */ + pprintf(p, " integer"); + break; + case 'p': /* optional integer */ + pprintf(p, " [integer]"); + break; + case 'i': /* word or integer */ + pprintf(p, " {word, integer}"); + break; + case 'n': /* optional word or integer */ + pprintf(p, " [{word, integer}]"); + break; + case 's': /* string to end */ + pprintf(p, " string"); + break; + case 't': /* optional string to end */ + pprintf(p, " [string]"); + break; + } + } + pprintf(p, "\nSee 'help %s' for a complete description.\n", command_list[lastCommandFound].comm_name); +} + +PUBLIC int process_command(int p, char *com_string, char **cmd) +{ + int which_command, retval; + param_list params; + static char alias_string1[MAX_STRING_LENGTH * 4], alias_string2[MAX_STRING_LENGTH * 4]; + char *comm, *parameters; + +#ifdef DEBUG + if (strcasecmp(parray[p].name, parray[p].login)) { + fprintf(stderr, "FICS: PROBLEM Name=%s, Login=%s\n", parray[p].name, parray[p].login); + } +#endif + if (!com_string) + return COM_FAILED; +#ifdef DEBUG + fprintf(stderr, "%s, %s, %d: >%s<\n", parray[p].name, parray[p].login, parray[p].socket, com_string); +#endif + alias_substitute(parray[p].alias_list, parray[p].numAlias, + com_string, alias_string1); + alias_substitute(g_alias_list, 999, + alias_string1, alias_string2); +#ifdef DEBUG + if (strcmp(com_string, alias_string2) != 0) + fprintf(stderr, "%s -alias-: >%s<\n", parray[p].name, alias_string2); +#endif + if ((retval = parse_command(alias_string2, &comm, ¶meters))) + return retval; + if (is_move(comm)) + return COM_ISMOVE; + stolower(comm); /* All commands are case-insensitive */ + *cmd = comm; + if ((which_command = match_command(comm, p)) < 0) + return -which_command; + if (parray[p].adminLevel < command_list[which_command].adminLevel) { + return COM_RIGHTS; + } + if ((retval = get_parameters(which_command, parameters, params))) + return retval; + return command_list[which_command].comm_func(p, params); +} + +PRIVATE int process_login(int p, char *loginname) +{ + int problem = 1; + + loginname = eatwhite(loginname); + + if (!*loginname) { /* do something in here? */ + } else { + char *loginnameii = strdup(loginname); + stolower(loginname); + if (!alphastring(loginname)) { + pprintf(p, "\nSorry, names can only consist of lower and upper case letters. Try again.\n"); + } else if (strlen(loginname) < 3) { + pprintf(p, "\nA name should be at least three characters long! Try again.\n"); + } else if (strlen(loginname) > 17) { + pprintf(p, "\nSorry, names may be at most 17 characters long. Try again.\n"); + } else if (in_list(p, L_BAN, loginnameii)) { + pprintf(p, "\nPlayer \"%s\" is banned.\n", loginname); + rfree(loginnameii); + return COM_LOGOUT; + } else if ((!in_list(p, L_ADMIN, loginnameii)) && (player_count(0) >= max_connections - 10)) { + psend_raw_file(p, mess_dir, MESS_FULL); + rfree(loginnameii); + return COM_LOGOUT; + } else { + problem = 0; + if (player_read(p, loginname)) { + strcpy(parray[p].name, loginnameii); + if (in_list(p, L_FILTER, dotQuad(parray[p].thisHost))) { + pprintf(p, "\nDue to abusive behavior, nobody from your site may login.\n"); + pprintf(p, "If you wish to use this server please email %s\n", reg_addr); + pprintf(p, "Include details of a nick-name to be called here, e-mail address and your real name.\n"); + pprintf(p, "We will send a password to you. Thanks.\n"); + rfree(loginnameii); + return COM_LOGOUT; + } + if ((player_count(0)) >= MAX(max_connections - 60, 200)) { + psend_raw_file(p, mess_dir, MESS_FULL_UNREG); + rfree(loginnameii); + return COM_LOGOUT; + } + pprintf_noformat(p, "\n\"%s\" is not a registered name. You may use this name to play unrated games.\n(After logging in, do \"help register\" for more info on how to register.)\n\nPress return to enter the FICS as \"%s\":", + parray[p].name, parray[p].name); + } else { + pprintf_noformat(p, "\n\"%s\" is a registered name. If it is yours, type the password.\nIf not, just hit return to try another name.\n\npassword: ", parray[p].name); + } + parray[p].status = PLAYER_PASSWORD; + turn_echo_off(parray[p].socket); + rfree(loginnameii); + if (strcasecmp(loginname, parray[p].name)) { + pprintf(p, "\nYou've got a bad name field in your playerfile -- please report this to an admin!\n"); + rfree(loginnameii); + return COM_LOGOUT; + } + if ((parray[p].adminLevel != 0) && (!in_list(p, L_ADMIN, parray[p].name))) { + pprintf(p, "\nYou've got a bad playerfile -- please report this to an admin!\n"); + pprintf(p, "Your handle is missing!"); + pprintf(p, "Please log on as an unreg until an admin can correct this.\n"); + rfree(loginnameii); + return COM_LOGOUT; + } + if ((parray[p].registered) && (parray[p].fullName == NULL)) { + pprintf(p, "\nYou've got a bad playerfile -- please report this to an admin!\n"); + pprintf(p, "Your FullName is missing!"); + pprintf(p, "Please log on as an unreg until an admin can correct this.\n"); + rfree(loginnameii); + return COM_LOGOUT; + } + if ((parray[p].registered) && (parray[p].emailAddress == NULL)) { + pprintf(p, "\nYou've got a bad playerfile -- please report this to an admin!\n"); + pprintf(p, "Your Email address is missing\n"); + pprintf(p, "Please log on as an unreg until an admin can correct this.\n"); + rfree(loginnameii); + return COM_LOGOUT; + } + } + } + + if (problem) { + psend_raw_file(p, mess_dir, MESS_LOGIN); + pprintf(p, "login: "); + } + return 0; +} + +void boot_out(int p, int p1) +{ + int fd; + pprintf(p, "\n **** %s is already logged in - kicking them out. ****\n", parray[p].name); + pprintf(p1, "**** %s has arrived - you can't both be logged in. ****\n", parray[p].name); + fd = parray[p1].socket; + process_disconnection(fd); + net_close_connection(fd); +} + +PUBLIC void rscan_news(FILE *fp, int p, int lc) { + + char junk[MAX_LINE_SIZE], count[10]; + int crtime; + char *junkp; + + fgets(junk, MAX_LINE_SIZE, fp); + if (feof(fp)) + return; + sscanf(junk, "%d %s", &crtime, count); + + if (crtime - lc < 0) + return; + else { + rscan_news(fp, p, lc); + junkp = junk; + junkp = nextword(junkp); + junkp = nextword(junkp); + pprintf(p, "%3s (%s) %s", count, fix_time(strltime(&crtime)), junkp); + } +} + +PRIVATE void check_news(int p, int admin) +{ + FILE *fp; + char filename[MAX_FILENAME_SIZE]; + char junk[MAX_LINE_SIZE]; + char *junkp; + int crtime; + int lc = player_lastconnect(p); + char count[10]; + + if (admin) { + + sprintf(filename, "%s/newadminnews.index", news_dir); + fp = fopen(filename, "r"); + if (!fp) { + fprintf(stderr, "Can't find admin news index.\n"); + return; + } + + if (num_anews == -1) { + num_anews = count_lines(fp); + fclose(fp); + fp = fopen(filename, "r"); + } + + fgets(junk, MAX_LINE_SIZE, fp); + sscanf(junk, "%d %s", &crtime, count); + if ((crtime - lc) < 0) { + pprintf(p, "There are no new admin news items since your last login.\n\n"); + fclose(fp); + return; + } else { + pprintf(p, "Index of new admin news items:\n"); + rscan_news(fp, p, lc); + junkp = junk; + junkp = nextword(junkp); + junkp = nextword(junkp); + pprintf(p, "%3s (%s) %s", count, fix_time(strltime(&crtime)), junkp); + pprintf(p, "(\"anews %d\" will display the most recent admin news file)\n", num_anews); + } + + } else { + + sprintf(filename, "%s/newnews.index", news_dir); + fp = fopen(filename, "r"); + if (!fp) { + fprintf(stderr, "Can't find news index.\n"); + return; + } + + if (num_news == -1) { + num_news = count_lines(fp); + fclose(fp); + fp = fopen(filename, "r"); + } + + fgets(junk, MAX_LINE_SIZE, fp); + sscanf(junk, "%d %s", &crtime, count); + if ((crtime - lc) < 0) { + pprintf(p, "There are no new news items since your last login (%s).\n", strltime(&lc)); + fclose(fp); + return; + } else { + pprintf(p, "Index of new news items:\n"); + rscan_news(fp, p, lc); + junkp = junk; + junkp = nextword(junkp); + junkp = nextword(junkp); + pprintf(p, "%3s (%s) %s", count, fix_time(strltime(&crtime)), junkp); + pprintf(p, "(\"news %d\" will display the most recent admin news file)\n", num_news); + } + } + + fclose(fp); +} + +PRIVATE int process_password(int p, char *password) +{ + int p1; + char salt[3]; + int fd; + unsigned int fromHost; + int messnum; +/* + struct hostent *hp; +*/ + int dummy; /* (to hold a return value) */ + + turn_echo_on(parray[p].socket); + +/* if (password[0] == '\n') { + parray[p].status = PLAYER_LOGIN; + return 0; + }*/ + + if (parray[p].passwd && parray[p].registered) { + salt[0] = parray[p].passwd[0]; + salt[1] = parray[p].passwd[1]; + salt[2] = '\0'; + if (strcmp(crypt(password, salt), parray[p].passwd)) { + fd = parray[p].socket; + fromHost = parray[p].thisHost; + player_clear(p); + parray[p].logon_time = parray[p].last_command_time = time(0); + parray[p].status = PLAYER_LOGIN; + parray[p].socket = fd; + parray[p].thisHost = fromHost; + if (*password) + pprintf(p, "\n\n**** Invalid password! ****\n\n"); + return COM_LOGOUT; + } + } + for (p1 = 0; p1 < p_num; p1++) { + if (parray[p1].name != NULL) { + if ((!strcasecmp(parray[p].name, parray[p1].name)) && (p != p1)) { + if (parray[p].registered == 0) { + pprintf(p, "\n*** Sorry %s is already logged in ***\n", parray[p].name); + return COM_LOGOUT; + } + boot_out(p, p1); + } + } + } + if (parray[p].adminLevel > 0) { + psend_raw_file(p, mess_dir, MESS_ADMOTD); + } else { + psend_raw_file(p, mess_dir, MESS_MOTD); + } + if (!parray[p].passwd && parray[p].registered) + pprintf(p, "\n*** You have no password. Please set one with the password command."); + if (!parray[p].registered) + psend_raw_file(p, mess_dir, MESS_UNREGISTERED); + parray[p].status = PLAYER_PROMPT; + player_write_login(p); + for (p1 = 0; p1 < p_num; p1++) { + if (p1 == p) + continue; + if (parray[p1].status != PLAYER_PROMPT) + continue; +#if 0 /* deleted by mann */ + if (parray[p1].thisHost == parray[p].thisHost) { + fprintf(stderr, "FICS: Players %s and %s - same host: %s\n", + parray[p].name, parray[p1].name, dotQuad(parray[p].thisHost)); + } +#endif + if (!parray[p1].i_login) + continue; + if (parray[p1].adminLevel > 0) { +/* + hp = gethostbyaddr((const char*)&(parray[p].thisHost), sizeof(parray[p].thisHost), AF_INET); +*/ + pprintf_prompt(p1, "\n[%s (%s: %s) has connected.]\n", parray[p].name, + (parray[p].registered ? "R" : "U"), + dotQuad(parray[p].thisHost)); + } else { + pprintf_prompt(p1, "\n[%s has connected.]\n", parray[p].name); + } + } + parray[p].num_comments = player_num_comments(p); + messnum = player_num_messages(p); + +/* loon: don't send unreg any news. when you change this, feel free to + put all the news junk in one source file:) no reason for identical code + in command.c and comproc.c */ + if (parray[p].registered) { + check_news(p, 0); + if (parray[p].adminLevel > 0) { + pprintf(p, "\n"); + check_news(p, 1); + } + } + if (messnum) { + pprintf(p, "\nYou have %d messages.\nUse \"messages\" to display them, or \"clearmessages\" to remove them.\n", messnum); + } + player_notify_present(p); + player_notify(p, "arrived", "arrival"); + showstored(p); + if (parray[p].registered && (parray[p].lastHost != 0) && + (parray[p].lastHost != parray[p].thisHost)) { +#if 0 /* removed by DAV - we don't need to know this rubbish */ + fprintf(stderr, "FICS: Player %s: Last login: %s ", parray[p].name, + dotQuad(parray[p].lastHost)); + fprintf(stderr, "This login: %s\n", dotQuad(parray[p].thisHost)); +#endif + pprintf(p, "\nPlayer %s: Last login: %s ", parray[p].name, + dotQuad(parray[p].lastHost)); + pprintf(p, "This login: %s", dotQuad(parray[p].thisHost)); + } + parray[p].lastHost = parray[p].thisHost; + if (parray[p].registered && !parray[p].timeOfReg) + parray[p].timeOfReg = time(0); + parray[p].logon_time = parray[p].last_command_time = time(0); + dummy = check_and_print_shutdown(p); /*Tells the user if we are going to shutdown */ + pprintf(p, "\n%s", parray[p].prompt); + return 0; +} + +PRIVATE int process_prompt(int p, char *command) +{ + int retval; + char *cmd = ""; + + command = eattailwhite(eatwhite(command)); + if (!*command) { + pprintf(p, "%s", parray[p].prompt); + return COM_OK; + } + retval = process_command(p, command, &cmd); + switch (retval) { + case COM_OK: + retval = COM_OK; + pprintf(p, "%s", parray[p].prompt); + break; + case COM_OK_NOPROMPT: + retval = COM_OK; + break; + case COM_ISMOVE: + retval = COM_OK; + +#ifdef TIMESEAL + if (parray[p].game >= 0 && garray[parray[p].game].status == GAME_ACTIVE + && parray[p].side == garray[parray[p].game].game_state.onMove + && garray[parray[p].game].flag_pending != FLAG_NONE) { + ExecuteFlagCmd(parray[p].game, con[parray[p].socket].time); + } +#endif + process_move(p, command); + pprintf(p, "%s", parray[p].prompt); + + break; + case COM_RIGHTS: + pprintf(p, "%s: Insufficient rights.\n", cmd); + pprintf(p, "%s", parray[p].prompt); + retval = COM_OK; + break; + case COM_AMBIGUOUS: +/* pprintf(p, "%s: Ambiguous command.\n", cmd); */ + { + int len = strlen(cmd); + int i = 0; + pprintf(p, "Ambiguous command. Matches:"); + while (command_list[i].comm_name) { + if ((!strncmp(command_list[i].comm_name, cmd, len)) && + (parray[p].adminLevel >= command_list[i].adminLevel)) { + pprintf(p, " %s", command_list[i].comm_name); + } + i++; + } + } + pprintf(p, "\n%s", parray[p].prompt); + retval = COM_OK; + break; + case COM_BADPARAMETERS: + printusage(p, command_list[lastCommandFound].comm_name); + pprintf(p, "%s", parray[p].prompt); + retval = COM_OK; + break; + case COM_FAILED: + case COM_BADCOMMAND: + pprintf(p, "%s: Command not found.\n", cmd); + retval = COM_OK; + pprintf(p, "%s", parray[p].prompt); + break; + case COM_LOGOUT: + retval = COM_LOGOUT; + break; + } + return retval; +} + +/* Return 1 to disconnect */ +PUBLIC int process_input(int fd, char *com_string) +{ + int p = player_find(fd); + int retval = 0; + + if (p < 0) { + fprintf(stderr, "FICS: Input from a player not in array!\n"); + return -1; + } + + commanding_player = p; + parray[p].last_command_time = time(0); + + switch (parray[p].status) { + case PLAYER_EMPTY: + fprintf(stderr, "FICS: Command from an empty player!\n"); + break; + case PLAYER_NEW: + fprintf(stderr, "FICS: Command from a new player!\n"); + break; + case PLAYER_INQUEUE: + /* Ignore input from player in queue */ + break; + case PLAYER_LOGIN: + retval = process_login(p, com_string); + if (retval == COM_LOGOUT && com_string != NULL) + fprintf (stderr, "%s tried to log in and failed.\n", com_string); + break; + case PLAYER_PASSWORD: + retval = process_password(p, com_string); + break; + case PLAYER_PROMPT: + parray[p].busy[0] = '\0'; + /* added this to stop buggy admin levels; shane */ + if (parray[p].adminLevel < 10) + parray[p].adminLevel = 0; + retval = process_prompt(p, com_string); + break; + } + + commanding_player = -1; + return retval; +} + +PUBLIC int process_new_connection(int fd, unsigned int fromHost) +{ + int p = player_new(); + + parray[p].status = PLAYER_LOGIN; + parray[p].socket = fd; + parray[p].thisHost = fromHost; + parray[p].logon_time = time(0); + psend_raw_file(p, mess_dir, MESS_WELCOME); + pprintf(p, "Head admin : %s Complaints to : %s\n", hadmin_handle, hadmin_email); + pprintf(p, "Server location: %s Server version : %s\n", fics_hostname, VERS_NUM); + psend_raw_file(p, mess_dir, MESS_LOGIN); + pprintf(p, "login: "); + return 0; +} + +PUBLIC int process_disconnection(int fd) +{ + int p = player_find(fd); + int p1; + + if (p < 0) { + fprintf(stderr, "FICS: Disconnect from a player not in array!\n"); + return -1; + } + if ((parray[p].game >=0) &&(garray[parray[p].game].status == GAME_EXAMINE)) { + pcommand(p, "unexamine"); + } + if ((parray[p].game >=0) &&in_list(p, L_ABUSER, parray[p].name)) { + pcommand(p, "resign"); + } + if (parray[p].status == PLAYER_PROMPT) { + for (p1 = 0; p1 < p_num; p1++) { + if (p1 == p) + continue; + if (parray[p1].status != PLAYER_PROMPT) + continue; + if (!parray[p1].i_login) + continue; + pprintf_prompt(p1, "\n[%s has disconnected.]\n", parray[p].name); + } + player_notify(p, "departed", "departure"); + player_notify_departure(p); + player_write_logout(p); + if (parray[p].registered) { + parray[p].totalTime += time(0) - parray[p].logon_time; + player_save(p); + } else { /* delete unreg history file */ + char fname[MAX_FILENAME_SIZE]; + sprintf(fname, "%s/player_data/%c/%s.games", stats_dir, parray[p].login[0], parray[p].login); + unlink(fname); + } + } + player_remove(p); + return 0; +} + +/* Called every few seconds */ +PUBLIC int process_heartbeat(int *fd) +{ + static int last_space = 0; + static int last_comfile = 0; +/* static int last_ratings = 0; */ + static int lastcalled = 0; + int time_since_last; + int p; +/* static int rpid = 0; */ + int now = time(0); + +/* game_update_times(); */ + + if (lastcalled == 0) + time_since_last = 0; + else + time_since_last = now - lastcalled; + lastcalled = now; + + /* Check for timed out connections */ + for (p = 0; p < p_num; p++) { + if (((parray[p].status == PLAYER_LOGIN) || + (parray[p].status == PLAYER_PASSWORD)) && + (player_idle(p) > MAX_LOGIN_IDLE)) { + pprintf(p, "\n**** LOGIN TIMEOUT ****\n"); + *fd = parray[p].socket; + return COM_LOGOUT; + } + if ((parray[p].status == PLAYER_PROMPT) && + (player_idle(p) > MAX_IDLE_TIME) && + (parray[p].adminLevel == 0) && + (!in_list(p, L_TD, parray[p].name))) { + pprintf(p, "\n**** Auto-logout because you were idle " + "more than one hour. ****\n"); + *fd = parray[p].socket; + return COM_LOGOUT; + } + } +/* loon: turning this fork off temporarily (28 Oct) for lag */ +#if 0 + if (rpid) { /* Rating calculating going on */ + int statusp; + + if (wait3(&statusp, WNOHANG, NULL) == rpid) { + fprintf(stderr, "FICS: Reinitting statistics.\n"); + rating_init(); /* Child finished, get the results. */ + rpid = 0; + } + } + /* Recalc ratings every 3 hours */ + /* This is done because the ratings stats and players can get out of sync + if there is a system crash. */ + /* This is done as a child process and read results when complete */ + if (last_ratings == 0) + last_ratings = (now - (5 * 60 * 60)) + 120; /* Do one in 2 minutes */ + else { + if (last_ratings + 6 * 60 * 60 < now) { + last_ratings = now; + rpid = fork(); + if (rpid < 0) { + fprintf(stderr, "FICS: Couldn't fork\n"); + } else { + if (rpid == 0) { /* The child */ + rating_recalc(); + exit(0); + fprintf(stderr, "Recalc process should never get here!\n"); + ASSERT(0); + } + } + } + } +#endif + /* Check for the communication file from mail updates every 10 minutes */ + /* That is probably too often, but who cares */ + if (MailGameResult) { + if (last_comfile == 0) + last_comfile = now; + else { + if (last_comfile + 10 * 60 < now) { + last_comfile = now; + /* Check for com file */ +/* hostinfo_checkcomfile(); */ + } + } + } + if (last_space == 0) + last_space = now; + else { + if (last_space + 60 < now) {/* Check the disk space every minute */ + last_space = now; + if (available_space() < 1000000) { + server_shutdown(60, " **** Disk space is dangerously low!!! ****\n"); + } + /* Check for com file */ + /* hostinfo_checkcomfile(); */ + } + } + ShutHeartBeat(); + return COM_OK; +} + +PUBLIC void commands_init() +{ + FILE *fp, *afp; + char fname[MAX_FILENAME_SIZE]; + int i = 0; + + sprintf(fname, "%s/commands", comhelp_dir); + fp = fopen(fname, "w"); + if (!fp) { + fprintf(stderr, "FICS: Could not write commands help file.\n"); + return; + } + sprintf(fname, "%s/admin_commands", adhelp_dir); + afp = fopen(fname, "w"); + if (!afp) { + fprintf(stderr, "FICS: Could not write admin_commands help file.\n"); + fclose(fp); + return; + } + while (command_list[i].comm_name) { + if (command_list[i].adminLevel >= ADMIN_ADMIN) { + fprintf(afp, "%s\n", command_list[i].comm_name); + } else { + fprintf(fp, "%s\n", command_list[i].comm_name); + } + i++; + } + fclose(fp); + fclose(afp); +} + +/* Need to save rated games */ +PUBLIC void TerminateCleanup() +{ + int p1; + int g; + + for (g = 0; g < g_num; g++) { + if (garray[g].status != GAME_ACTIVE) + continue; + if (garray[g].rated) { + game_ended(g, WHITE, END_ADJOURN); + } + } + for (p1 = 0; p1 < p_num; p1++) { + if (parray[p1].status == PLAYER_EMPTY) + continue; + pprintf(p1, "\n **** Server shutting down immediately. ****\n\n"); + if (parray[p1].status != PLAYER_PROMPT) { + close(parray[p1].socket); + } else { + pprintf(p1, "Logging you out.\n"); + psend_raw_file(p1, mess_dir, MESS_LOGOUT); + player_write_logout(p1); + if (parray[p1].registered) + parray[p1].totalTime += time(0) - parray[p1].logon_time; + player_save(p1); + } + } +} |