aboutsummaryrefslogtreecommitdiffstats
path: root/FICS/command.c
diff options
context:
space:
mode:
authorMarkus Uhlin <markus@nifty-networks.net>2023-12-07 21:31:49 +0100
committerMarkus Uhlin <markus@nifty-networks.net>2023-12-07 21:31:49 +0100
commit79b59f9b30fb6a1fdf8c3efb446271f7cb00d434 (patch)
treef6ade4ccbc3af20d825edacfd12b5da8ded8d240 /FICS/command.c
FICS 1.6.2
Diffstat (limited to 'FICS/command.c')
-rw-r--r--FICS/command.c1080
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, &parameters)))
+ 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);
+ }
+ }
+}