2 Copyright (c) 1993 Richard V. Nash.
3 Copyright (c) 2000 Dan Papasian
4 Copyright (C) Andrew Tridgell 2002
6 This program is free software; you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation; either version 2 of the License, or
9 (at your option) any later version.
11 This program is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 GNU General Public License for more details.
16 You should have received a copy of the GNU General Public License
17 along with this program; if not, write to the Free Software
18 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
22 #include "command_list.h"
24 const char *usage_dir[NUM_LANGS] = {USAGE_DIR, USAGE_SPANISH,
25 USAGE_FRENCH, USAGE_DANISH};
27 static int lastCommandFound = -1;
29 static char *guest_name(void);
30 static int check_user(char *user);
32 /* Copies command into comm, and returns pointer to parameters in
35 static int parse_command(char *com_string,
40 *parameters = eatword(com_string);
41 if (**parameters != '\0') {
44 *parameters = eatwhite(*parameters);
46 if (strlen(*comm) >= MAX_COM_LENGTH) {
47 return COM_BADCOMMAND;
52 /* numalias is the maximum number to search through */
53 int alias_lookup(char *tmp, struct alias_type *alias_list, int numalias)
57 for (i = 0; i < numalias && alias_list[i].comm_name; i++) {
58 if (!strcasecmp(tmp, alias_list[i].comm_name))
61 return -1; /* not found */
64 /* Puts alias substitution into alias_string */
65 static void alias_substitute(struct alias_type *alias_list, int num_alias,
66 char *com_str, char outalias[])
72 /* handle punctuation commands like '+' */
73 if (ispunct(*com_str)) {
75 for (n=0;ispunct(com_str[n]);n++) ;
76 name = strndup(com_str, n);
78 name = strndup(com_str, strcspn(com_str, " \t"));
80 com_str += strlen(name);
82 while (isspace(*com_str)) com_str++;
84 i = alias_lookup(name, alias_list, num_alias);
88 name = strdup(alias_list[i].alias);
91 /* now substitute '@' values in name */
92 while ((atpos = strchr(name+at_offset, '@'))) {
94 asprintf(&name2, "%*.*s%s%s",
95 atpos-name, atpos-name, name,
100 /* try to prevent loops */
101 at_offset = (atpos - name) + strlen(com_str);
107 /* there is an implicit @ after each alias */
108 if (at_offset == 0 && *com_str) {
109 sprintf(outalias, "%s %s", name, com_str);
111 strcpy(outalias, name);
117 /* Returns pointer to command that matches */
118 static int match_command(char *comm, int p)
122 int len = strlen(comm);
124 while (command_list[i].comm_name) {
125 if (strncmp(command_list[i].comm_name, comm, len) == 0 &&
126 check_admin(p, command_list[i].adminLevel)) {
128 return -COM_AMBIGUOUS;
138 if (in_list(p, L_REMOVEDCOM, command_list[gotIt].comm_name)) {
139 pprintf(p, "Due to a bug - this command has been temporarily removed.\n");
142 lastCommandFound = gotIt;
146 /* Gets the parameters for this command */
147 static int get_parameters(int command, char *parameters, param_list params)
154 punc[1] = '\0'; /* Holds punc parameters */
155 for (i = 0; i < MAXNUMPARAMS; i++)
156 (params)[i].type = TYPE_NULL; /* Set all parameters to NULL */
157 parlen = strlen(command_list[command].param_string);
158 for (i = 0; i < parlen; i++) {
159 c = command_list[command].param_string[i];
168 case 'o': /* word or optional word */
169 parameters = eatwhite(parameters);
171 return (c == 'o' ? COM_OK : COM_BADPARAMETERS);
172 (params)[i].val.word = parameters;
173 (params)[i].type = TYPE_WORD;
174 if (ispunct(*parameters)) {
175 punc[0] = *parameters;
176 (params)[i].val.word = punc;
178 if (*parameters && isspace(*parameters))
181 parameters = eatword(parameters);
182 if (*parameters != '\0') {
188 stolower((params)[i].val.word);
192 case 'p': /* optional or required integer */
193 parameters = eatwhite(parameters);
195 return (c == 'p' ? COM_OK : COM_BADPARAMETERS);
196 if (sscanf(parameters, "%d", &(params)[i].val.integer) != 1)
197 return COM_BADPARAMETERS;
198 (params)[i].type = TYPE_INT;
199 parameters = eatword(parameters);
200 if (*parameters != '\0') {
207 case 'n': /* optional or required word or integer */
208 parameters = eatwhite(parameters);
210 return (c == 'n' ? COM_OK : COM_BADPARAMETERS);
211 if (sscanf(parameters, "%d", &(params)[i].val.integer) != 1) {
212 (params)[i].val.word = parameters;
213 (params)[i].type = TYPE_WORD;
215 (params)[i].type = TYPE_INT;
217 if (ispunct(*parameters)) {
218 punc[0] = *parameters;
219 (params)[i].val.word = punc;
220 (params)[i].type = TYPE_WORD;
222 if (*parameters && isspace(*parameters))
225 parameters = eatword(parameters);
226 if (*parameters != '\0') {
231 if ((params)[i].type == TYPE_WORD)
233 stolower((params)[i].val.word);
237 case 't': /* optional or required string to end */
239 return (c == 't' ? COM_OK : COM_BADPARAMETERS);
240 (params)[i].val.string = parameters;
241 (params)[i].type = TYPE_STRING;
243 parameters = nextword(parameters);
245 stolower((params)[i].val.string);
250 return COM_BADPARAMETERS;
255 static void printusage(int p, char *command_str)
257 struct player *pp = &player_globals.parray[p];
258 int i, parlen, UseLang = pp->language;
262 char *filenames[1000]; /* enough for all usage names */
264 if ((command = match_command(command_str, p)) < 0) {
265 pprintf(p, " UNKNOWN COMMAND\n");
269 /*Usage added by DAV 11/19/95 */
270 /* First lets check if we have a text usage file for it */
272 i = search_directory(usage_dir[UseLang], command_str, filenames, 1000);
273 if (i == 0) { /* nope none in current Lang */
274 if (UseLang != LANG_DEFAULT) {
275 i += search_directory(usage_dir[LANG_DEFAULT], command_str, filenames, 1000);
277 pprintf(p, "No usage available in %s; using %s instead.\n",
278 Language(UseLang), Language(LANG_DEFAULT));
279 UseLang = LANG_DEFAULT;
285 if ((i == 1) || (!strcmp(*filenames, command_str))) { /* found it? then send */
286 if (psend_file(p, usage_dir[UseLang], *filenames)) {
287 /* we should never reach this unless the file was just deleted */
288 pprintf(p, "Usage file %s could not be found! ", *filenames);
289 pprintf(p, "Please inform an admin of this. Thank you.\n");
290 /* no need to print 'system' usage - should never happen */
292 pprintf(p, "\nSee '%s %s' for a complete description.\n",
293 ((command_list[lastCommandFound].adminLevel > ADMIN_USER) ? "ahelp" :
295 command_list[lastCommandFound].comm_name);
300 /* print the default 'system' usage files (which aren't much help!) */
302 pprintf(p, "Usage: %s", command_list[lastCommandFound].comm_name);
304 parlen = strlen(command_list[command].param_string);
305 for (i = 0; i < parlen; i++) {
306 c = command_list[command].param_string[i];
313 case 'o': /* optional word */
314 pprintf(p, " [word]");
316 case 'd': /* integer */
317 pprintf(p, " integer");
319 case 'p': /* optional integer */
320 pprintf(p, " [integer]");
322 case 'i': /* word or integer */
323 pprintf(p, " {word, integer}");
325 case 'n': /* optional word or integer */
326 pprintf(p, " [{word, integer}]");
328 case 's': /* string to end */
329 pprintf(p, " string");
331 case 't': /* optional string to end */
332 pprintf(p, " [string]");
336 pprintf(p, "\nSee '%s %s' for a complete description.\n",
337 ((command_list[lastCommandFound].adminLevel > ADMIN_USER) ? "ahelp" :
339 command_list[lastCommandFound].comm_name);
342 static int one_command(int p, char *command, char **cmd)
344 struct player *pp = &player_globals.parray[p];
345 int which_command, retval;
346 char *comm=NULL, *parameters=NULL;
349 if ((retval = parse_command(command, &comm, ¶meters)))
352 if ((game_globals.garray[pp->game].status == GAME_SETUP) && (is_drop(comm)))
356 if (pp->game == -1) {
357 pprintf(p, "You are not playing or examining a game.\n");
361 if (game_globals.garray[pp->game].status == GAME_SETUP)
362 return COM_ISMOVE_INSETUP;
366 stolower(comm); /* All commands are case-insensitive */
368 if ((which_command = match_command(comm, p)) < 0)
369 return -which_command;
370 if (!check_admin(p, command_list[which_command].adminLevel)) {
373 if ((retval = get_parameters(which_command, parameters, params)))
376 if (command_list[which_command].adminLevel >= ADMIN_ADMIN) {
377 admin_log(pp, command, params);
380 return command_list[which_command].comm_func(p, params);
383 static int process_command(int p, char *com_string, char **cmd)
385 struct player *pp = &player_globals.parray[p];
388 char astring1[MAX_STRING_LENGTH * 4];
389 char astring2[MAX_STRING_LENGTH * 4];
392 if (strcasecmp(pp->name, pp->login)) {
393 d_printf( "CHESSD: PROBLEM Name=%s, Login=%s\n",
394 pp->name, pp->login);
400 d_printf( "%s, %s, %d: >%s<\n",
401 pp->name, pp->login, pp->socket, com_string);
404 /* don't expand the alias command */
405 if (strncmp(com_string, "alias ", 6) == 0) {
406 return one_command(p, com_string, cmd);
409 /* don't expand the alias command */
410 if (com_string[0] == '$') {
411 return one_command(p, eatwhite(com_string+1), cmd);
414 alias_substitute(pp->alias_list, pp->numAlias,
415 com_string, astring1);
416 alias_substitute(g_alias_list, 999,
420 if (strcmp(com_string, astring2) != 0) {
421 d_printf( "%s -alias-: >%s< >%s<\n",
422 pp->name, com_string, astring2);
426 for (tok=strtok_r(astring2, ";", &ptr); tok; tok=strtok_r(NULL, ";", &ptr)) {
427 char alias_string1[MAX_STRING_LENGTH * 4];
428 char alias_string2[MAX_STRING_LENGTH * 4];
430 while (isspace(*tok)) tok++;
432 alias_substitute(pp->alias_list, pp->numAlias,
434 alias_substitute(g_alias_list, 999,
435 alias_string1, alias_string2);
438 if (strcmp(tok, alias_string2) != 0) {
439 d_printf( "%s -alias2-: >%s<\n",
440 pp->name, alias_string2);
444 retval = one_command(p, alias_string2, cmd);
446 /* stop on first error */
447 if (retval != COM_OK) return retval;
453 static int process_login(int p, char *loginname)
455 struct player *pp = &player_globals.parray[p];
456 char loginnameii[80];
459 loginname = eatwhite(loginname);
465 /* if 'guest' was specified then pick a random guest name */
466 if (strcasecmp(loginname, config_get_tmp("GUEST_LOGIN")) == 0) {
467 loginname = guest_name();
469 pprintf(p,"\nCreated temporary login '%s'\n", loginname);
472 strlcpy(loginnameii, loginname, sizeof(loginnameii));
474 if (!alphastring(loginnameii)) {
475 pprintf(p, "\nSorry, names can only consist of lower and upper case letters. Try again.\n");
478 if (strlen(loginnameii) < 3) {
479 pprintf(p, "\nA name should be at least three characters long! Try again.\n");
483 if (strlen(loginnameii) > (MAX_LOGIN_NAME - 1)) {
484 pprintf(p, "\nSorry, names may be at most %d characters long. Try again.\n",
489 if (in_list(p, L_BAN, loginnameii)) {
490 pprintf(p, "\nPlayer \"%s\" is banned.\n", loginnameii);
494 if (!in_list(p, L_ADMIN, loginnameii) &&
495 player_count(0) >= config_get_int("MAX_PLAYER", DEFAULT_MAX_PLAYER)) {
496 psend_raw_file(p, MESS_DIR, MESS_FULL);
500 if (player_read(p, loginnameii) != 0) {
502 config_get_int("GUEST_PREFIX_ONLY", DEFAULT_GUEST_PREFIX_ONLY)) {
505 /* they are not registered */
506 strcpy(pp->name, loginnameii);
507 if (in_list(p, L_FILTER, dotQuad(pp->thisHost))) {
508 pprintf(p, "\nDue to abusive behavior, nobody from your site may login.\n");
509 pprintf(p, "If you wish to use this server please email %s\n",
510 config_get_tmp("REGISTRATION_ADDRESS"));
511 pprintf(p, "Include details of a nick-name to be called here, e-mail address and your real name.\n");
512 pprintf(p, "We will send a password to you. Thanks.\n");
516 if (player_count(0) >= config_get_int("MAX_PLAYER", DEFAULT_MAX_PLAYER) - 100) {
517 psend_raw_file(p, MESS_DIR, MESS_FULL_UNREG);
521 pprintf_noformat(p, "\n\"%s\" is not a registered name. You may play unrated games as a guest.\n(After logging in, do \"help register\" for more info on how to register.)\n\nPress return to enter the FICS as \"%s\":",
522 loginnameii, loginnameii);
523 pp->status = PLAYER_PASSWORD;
524 turn_echo_off(pp->socket);
528 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: ",
530 pp->status = PLAYER_PASSWORD;
531 turn_echo_off(pp->socket);
532 if (strcasecmp(loginnameii, pp->name)) {
533 pprintf(p, "\nYou've got a bad name field in your playerfile -- please report this to an admin!\n");
537 if (CheckPFlag(p, PFLAG_REG)
538 && (pp->fullName == NULL)) {
539 pprintf(p, "\nYou've got a bad playerfile -- please report this to an admin!\n");
540 pprintf(p, "Your FullName is missing!");
541 pprintf(p, "Please log on as an unreg until an admin can correct this.\n");
544 if (CheckPFlag(p, PFLAG_REG)
545 && (pp->emailAddress == NULL)) {
546 pprintf(p, "\nYou've got a bad playerfile -- please report this to an admin!\n");
547 pprintf(p, "Your Email address is missing\n");
548 pprintf(p, "Please log on as an unreg until an admin can correct this.\n");
556 /* give them a new prompt */
557 psend_raw_file(p, MESS_DIR, MESS_LOGIN);
558 pprintf(p, "login: ");
562 static void boot_out(int p, int p1)
564 struct player *pp = &player_globals.parray[p];
566 pprintf(p, "\n **** %s is already logged in - kicking them out. ****\n", pp->name);
567 pprintf(p1, "**** %s has arrived - you can't both be logged in. ****\n", pp->name);
568 fd = player_globals.parray[p1].socket;
569 process_disconnection(fd);
570 net_close_connection(fd);
573 static int process_password(int p, char *password)
575 struct player *pp = &player_globals.parray[p];
576 static int Current_ad;
580 struct in_addr fromHost;
583 int dummy; /* (to hold a return value) */
585 turn_echo_on(pp->socket);
587 if (pp->passwd && CheckPFlag(p, PFLAG_REG)) {
588 salt[0] = pp->passwd[3];
589 salt[1] = pp->passwd[4];
591 if (strcmp(chessd_crypt(password,salt), pp->passwd)) {
593 fromHost = pp->thisHost;
595 pprintf(p, "\n\n**** Invalid password! ****\n\n");
596 d_printf("FICS (process_password): Bad password for %s [%s] [%s] [%s]\n",
603 pp->logon_time = pp->last_command_time = time(0);
604 pp->status = PLAYER_LOGIN;
606 if (fd >= net_globals.no_file)
607 d_printf("FICS (process_password): Out of range fd!\n");
609 pp->thisHost = fromHost;
611 psend_raw_file(p, MESS_DIR, MESS_LOGIN);
612 pprintf(p, "login: ");
616 for (p1 = 0; p1 < player_globals.p_num; p1++) {
617 if (player_globals.parray[p1].name != NULL) {
618 if ((!strcasecmp(pp->name, player_globals.parray[p1].name)) && (p != p1)) {
619 if (!CheckPFlag(p, PFLAG_REG)) {
620 pprintf(p, "\n*** Sorry %s is already logged in ***\n", pp->name);
628 if (player_ishead(p)) {
629 pprintf(p,"\n ** LOGGED IN AS HEAD ADMIN **\n");
630 pp->adminLevel = ADMIN_GOD;
635 if (pp->adminLevel > 0) {
636 psend_raw_file(p, MESS_DIR, MESS_ADMOTD);
638 psend_raw_file(p, MESS_DIR, MESS_MOTD);
640 if (MAX_ADVERTS >=0) {
642 sprintf (fname,"%d",Current_ad);
643 Current_ad = (Current_ad + 1) % MAX_ADVERTS;
644 psend_raw_file(p, ADVERT_DIR, fname);
646 if (!pp->passwd && CheckPFlag(p, PFLAG_REG))
647 pprintf(p, "\n*** You have no password. Please set one with the password command.");
648 if (!CheckPFlag(p, PFLAG_REG))
649 psend_raw_file(p, MESS_DIR, MESS_UNREGISTERED);
650 pp->status = PLAYER_PROMPT;
651 player_write_login(p);
652 for (p1 = 0; p1 < player_globals.p_num; p1++) {
655 if (player_globals.parray[p1].status != PLAYER_PROMPT)
657 if (!CheckPFlag(p1, PFLAG_PIN))
659 if (player_globals.parray[p1].adminLevel > 0) {
660 pprintf_prompt(p1, "\n[%s (%s: %s) has connected.]\n", pp->name,
661 (CheckPFlag(p, PFLAG_REG) ? "R" : "U"),
662 dotQuad(pp->thisHost));
664 pprintf_prompt(p1, "\n[%s has connected.]\n", pp->name);
667 pp->num_comments = player_num_comments(p);
668 messnum = player_num_messages(p);
672 pprintf(p, "\nYou have 1 message.\nUse \"messages\" to display it, or \"clearmessages\" to remove it.\n");
674 pprintf(p, "\nYou have %d messages.\nUse \"messages\" to display them, or \"clearmessages\" to remove them.\n", messnum);
677 player_notify_present(p);
678 player_notify(p, "arrived", "arrival");
681 if (CheckPFlag(p, PFLAG_REG) && (pp->lastHost.s_addr != 0) &&
682 (pp->lastHost.s_addr != pp->thisHost.s_addr)) {
683 pprintf(p, "\nPlayer %s: Last login: %s ", pp->name,
684 dotQuad(pp->lastHost));
685 pprintf(p, "This login: %s", dotQuad(pp->thisHost));
687 pp->lastHost = pp->thisHost;
688 if (CheckPFlag(p, PFLAG_REG) && !pp->timeOfReg)
689 pp->timeOfReg = time(0);
690 pp->logon_time = pp->last_command_time = time(0);
691 dummy = check_and_print_shutdown(p);
692 pprintf_prompt(p, "\n");
693 if (CheckPFlag(p, PFLAG_REG))
698 static int process_prompt(int p, char *command)
700 struct player *pp = &player_globals.parray[p];
704 command = eattailwhite(eatwhite(command));
709 retval = process_command(p, command, &cmd);
715 case COM_OK_NOPROMPT:
721 if (pp->game >= 0 && game_globals.garray[pp->game].status == GAME_ACTIVE
722 && pp->side == game_globals.garray[pp->game].game_state.onMove
723 && game_globals.garray[pp->game].flag_pending != FLAG_NONE) {
724 ExecuteFlagCmd(p, net_globals.con[pp->socket]);
727 process_move(p, command);
731 case COM_ISMOVE_INSETUP:
732 pprintf(p, "You are still setting up the position.\n");
733 pprintf_prompt(p, "Type: 'setup done' when you are finished editing.\n");
737 pprintf_prompt(p, "%s: Insufficient rights.\n", cmd);
741 /* pprintf(p, "%s: Ambiguous command.\n", cmd); */
743 int len = strlen(cmd);
745 pprintf(p, "Ambiguous command. Matches:");
746 while (command_list[i].comm_name) {
747 if (strncmp(command_list[i].comm_name, cmd, len) == 0 &&
748 check_admin(p, command_list[i].adminLevel)) {
749 pprintf(p, " %s", command_list[i].comm_name);
754 pprintf_prompt(p, "\n");
757 case COM_BADPARAMETERS:
758 printusage(p, command_list[lastCommandFound].comm_name);
764 pprintf_prompt(p, "%s: Command not found.\n", cmd);
765 d_printf("Command not found [%s]\n", cmd);
775 /* Return 1 to disconnect */
776 int process_input(int fd, char *com_string)
778 int p = player_find(fd);
783 d_printf( "CHESSD: Input from a player not in array!\n");
787 pp = &player_globals.parray[p];
789 command_globals.commanding_player = p;
790 pp->last_command_time = time(0);
792 switch (pp->status) {
794 d_printf( "CHESSD: Command from an empty player!\n");
797 d_printf( "CHESSD: Command from a new player!\n");
800 /* Ignore input from player in queue */
803 retval = process_login(p, com_string);
804 if (retval == COM_LOGOUT && com_string != NULL)
805 d_printf("%s tried to log in and failed.\n", com_string);
807 case PLAYER_PASSWORD:
808 retval = process_password(p, com_string);
812 retval = process_prompt(p, com_string);
816 command_globals.commanding_player = -1;
820 int process_new_connection(int fd, struct in_addr fromHost)
823 int p = player_new();
825 pp = &player_globals.parray[p];
827 pp->status = PLAYER_LOGIN;
828 if (fd >= net_globals.no_file)
829 d_printf("FICS (process_new_connection): Out of range fd!\n");
832 pp->thisHost = fromHost;
833 pp->logon_time = time(0);
834 psend_raw_file(p, MESS_DIR, MESS_WELCOME);
835 pprintf(p, "Head admin : %s", config_get_tmp("HEAD_ADMIN"));
836 pprintf(p, " Complaints to : %s\n", config_get_tmp("HEAD_ADMIN_EMAIL"));
837 pprintf(p, "Server location: %s", config_get_tmp("SERVER_LOCATION"));
838 pprintf(p, " Server version : %s\n", VERS_NUM);
839 pprintf(p, "Server name : %s\n", config_get_tmp("SERVER_HOSTNAME"));
840 psend_raw_file(p, MESS_DIR, MESS_LOGIN);
841 pprintf(p, "login: ");
846 int process_disconnection(int fd)
848 int p = player_find(fd);
854 d_printf( "CHESSD: Disconnect from a player not in array!\n");
858 pp = &player_globals.parray[p];
860 if (CheckPFlag(p, PFLAG_REG) && CheckPFlag(p, PFLAG_OPEN) &&
862 announce_notavail(p);
863 if ((pp->game >=0) && ((game_globals.garray[pp->game].status == GAME_EXAMINE) || (game_globals.garray[pp->game].status == GAME_SETUP))) {
864 pcommand(p, "unexamine");
866 if ((pp->game >=0) && (in_list(p, L_ABUSER, pp->name)
867 || (game_globals.garray[pp->game].link >= 0)))
869 pcommand(p, "resign");
872 pcommand (p,"tell 0 I am logging out now - conversation forwarding stopped.");
876 if (pp->status == PLAYER_PROMPT) {
877 for (p1 = 0; p1 < player_globals.p_num; p1++) {
880 if (player_globals.parray[p1].status != PLAYER_PROMPT)
883 if (player_globals.parray[p1].ftell == p) {
884 player_globals.parray[p1].ftell = -1;
885 sprintf (command,"tell 0 *%s* has logged out - conversation forwarding stopped.",pp->name);
886 pcommand (p1,command);
887 pprintf_prompt (p1,"%s, whose tells you were forwarding has logged out.\n",
891 if (!CheckPFlag(p1, PFLAG_PIN))
893 pprintf_prompt(p1, "\n[%s has disconnected.]\n", pp->name);
895 player_notify(p, "departed", "departure");
896 player_notify_departure(p);
897 player_write_logout(p);
898 if (CheckPFlag(p, PFLAG_REG)) {
899 pp->totalTime += time(0) - pp->logon_time;
901 } else { /* delete unreg history file */
902 char fname[MAX_FILENAME_SIZE];
903 sprintf(fname, "%s/player_data/%c/%s.games", STATS_DIR, pp->login[0], pp->login);
911 /* Called every few seconds */
912 int process_heartbeat(int *fd)
916 time_t now = time(0);
917 unsigned idle_timeout = config_get_int("IDLE_TIMEOUT", DEFAULT_IDLE_TIMEOUT);
918 unsigned login_timeout = config_get_int("LOGIN_TIMEOUT", DEFAULT_LOGIN_TIMEOUT);
920 /* Check for timed out connections */
921 for (p = 0; p < player_globals.p_num; p++) {
922 struct player *pp = &player_globals.parray[p];
924 if ((pp->status == PLAYER_LOGIN ||
925 pp->status == PLAYER_PASSWORD) &&
926 player_idle(p) > login_timeout) {
927 pprintf(p, "\n**** LOGIN TIMEOUT ****\n");
931 if (pp->status == PLAYER_PROMPT &&
932 player_idle(p) > idle_timeout &&
933 !check_admin(p, ADMIN_ADMIN) &&
934 !in_list(p, L_TD, pp->name)) {
935 pprintf(p, "\n**** Auto-logout - you were idle more than %u minutes. ****\n",
941 nowtm=localtime((time_t *)&now);
942 if (nowtm->tm_min==0) {
943 gics_globals.userstat.users[nowtm->tm_hour*2]=player_count(1);
946 if (nowtm->tm_min==30) {
947 gics_globals.userstat.users[nowtm->tm_hour*2+1]=player_count(1);
950 if (command_globals.player_high > gics_globals.userstat.usermax) {
951 gics_globals.userstat.usermax=command_globals.player_high;
952 gics_globals.userstat.usermaxtime=now;
955 if (command_globals.game_high > gics_globals.userstat.gamemax) {
956 gics_globals.userstat.gamemax=command_globals.game_high;
957 gics_globals.userstat.gamemaxtime=now;
965 /* helper function for sorting command list */
966 static int command_compare(struct command_type *c1, struct command_type *c2)
968 return strcasecmp(c1->comm_name, c2->comm_name);
971 void commands_init(void)
975 int count=0, acount=0;
977 /* sort the command list */
979 (sizeof(command_list)/sizeof(command_list[0])) - 1,
980 sizeof(command_list[0]),
981 (COMPAR_FN_T)command_compare);
983 command_globals.commanding_player = -1;
985 fp = fopen_s(HELP_DIR "/commands", "w");
987 d_printf( "CHESSD: Could not write commands help file.\n");
990 afp = fopen_s(ADHELP_DIR "/commands", "w");
992 d_printf( "CHESSD: Could not write admin commands help file.\n");
997 while (command_list[i].comm_name) {
998 if (command_list[i].adminLevel >= ADMIN_ADMIN) {
999 fprintf(afp, "%-19s", command_list[i].comm_name);
1001 if (acount % 4 == 0) {
1005 fprintf(fp, "%-19s", command_list[i].comm_name);
1007 if (count % 4 == 0) {
1020 d_printf("CHESSD: Loaded %d commands (admin=%d normal=%d)\n", i, acount, count);
1023 /* Need to save rated games */
1024 void TerminateCleanup(void)
1031 for (g = 0; g < game_globals.g_num; g++) {
1032 if (game_globals.garray[g].status != GAME_ACTIVE)
1034 if (game_globals.garray[g].rated) {
1035 game_ended(g, WHITE, END_ADJOURN);
1038 for (p1 = 0; p1 < player_globals.p_num; p1++) {
1039 if (player_globals.parray[p1].status == PLAYER_EMPTY)
1041 pprintf(p1, "\n **** Server shutting down immediately. ****\n\n");
1042 if (player_globals.parray[p1].status != PLAYER_PROMPT) {
1043 close(player_globals.parray[p1].socket);
1045 pprintf(p1, "Logging you out.\n");
1046 psend_raw_file(p1, MESS_DIR, MESS_LOGOUT);
1047 player_write_logout(p1);
1048 if (CheckPFlag(p1, PFLAG_REG))
1049 player_globals.parray[p1].totalTime += time(0) - player_globals.parray[p1].logon_time;
1056 static char *guest_name(void)
1058 static char name[20];
1061 #define RANDLET ((char)('A' + (random() % 26)))
1066 snprintf(name,sizeof(name), "Guest%c%c%c%c", RANDLET, RANDLET, RANDLET, RANDLET);
1067 found = check_user(name);
1073 static int check_user(char *user)
1077 for (i = 0; i < player_globals.p_num; i++) {
1078 if (player_globals.parray[i].name != NULL) {
1079 if (!strcasecmp(user, player_globals.parray[i].name))
1087 /* return the global alias list */
1088 const struct alias_type *alias_list_global(void)
1090 return g_alias_list;
1093 /* return a personal alias list */
1094 const struct alias_type *alias_list_personal(int p, int *n)
1096 struct player *pp = &player_globals.parray[p];
1099 return pp->alias_list;
1103 report on any missing help pages
1105 int com_acheckhelp(int p, param_list param)
1110 for (count=i=0; command_list[i].comm_name; i++) {
1112 asprintf(&fname, "%s/%s",
1113 command_list[i].adminLevel?ADHELP_DIR:HELP_DIR,
1114 command_list[i].comm_name);
1115 if (!file_exists(fname)) {
1116 pprintf(p, "Help for command '%s' is missing%s\n",
1117 command_list[i].comm_name,
1118 command_list[i].adminLevel?" (admin)":"");
1124 pprintf(p, "%d commands are missing help files\n", count);
1126 for (count=i=0; command_list[i].comm_name; i++) {
1128 asprintf(&fname, "%s/%s", USAGE_DIR, command_list[i].comm_name);
1129 if (!file_exists(fname)) {
1130 pprintf(p, "Usage for command '%s' is missing%s\n",
1131 command_list[i].comm_name,
1132 command_list[i].adminLevel?" (admin)":"");
1138 pprintf(p, "%d commands are missing usage files\n", count);