diff options
53 files changed, 1892 insertions, 721 deletions
diff --git a/CHANGELOG.md b/CHANGELOG.md index 0813853..8aab73c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,10 +3,62 @@ All notable changes to this fork of FICS version 1.6.2 will be documented in this file. -## [Unreleased] ## -- Added an autorun script suitable to be run as a cron job. -- Added width specifications to multiple `fscanf()` and `sscanf()` - calls, thus eliminated the risk of overflow. +## [1.4.5] - 2025-04-09 ## +- **Changed** the addplayer program to output a restart notice if an admin + account is created. +- **Changed** the program to avoid calculating the same string multiple + times. Multiple occurrences, found by PVS-Studio. +- **Fixed** `-Wshadow` warnings. Multiple occurrences. +- **Fixed** calls of risky functions. +- **Fixed** constant expression result in `tell()`. +- **Fixed** double `free()` in `process_login()`. +- **Fixed** excessive checks. +- **Fixed** improper use of negative values. +- **Fixed** memory leak in `process_login()`. +- **Fixed** _multiple_ Clang Tidy warnings. +- **Fixed** negative array index read in `accept_match()`. +- **Fixed** null pointer dereferences. +- **Fixed** out-of-bounds array access in `has_legal_move()`. +- **Fixed** overflowed array index read/write. Multiple occurrences. +- **Fixed** overflowed return value in `player_search()`. +- **Fixed** possible buffer overflow in `FindHistory2()`. +- **Fixed** possible memory corruptions and incorrect computations. +- **Fixed** truncated stdio return value in `ReadGameState()`. +- **Fixed** unchecked function return values. Multiple occurrences. +- **Fixed** uninitialized variables. +- **Fixed** untrusted array indices. +- **Fixed** untrusted loop bounds. +- **Fixed** use of 32-bit `time_t`. Y2K38 safety. Multiple occurrences. + +## [1.4.4] - 2024-12-07 ## +- **Added** an autorun script suitable to be run as a cron job. +- **Added** command `sought`, which currently behaves as a no-op. Code is + to be added in a later version. We also want the seek/unseek + commands. +- **Added** command-line option `v` (Display version.) +- **Added** missing calls to `fclose()`. +- **Added** null checks. +- **Added** player number checks, i.e. validate that the player number is + OK and within bounds. +- **Added** usage of `time_t`. +- **Added** usage of macros. +- **Added** variable `seek`. +- **Added** width specifications to multiple `fscanf()` and `sscanf()` + calls, thus eliminated the risk of overflow. Multiple occurrences. + (Found by PVS-Studio.) +- Compile using `-D_FORTIFY_SOURCE=3`. +- **Fixed** a bug in `net_send_string()`, where the expression was + calculated as `A = (B >= C)`. (Found by PVS-Studio.) +- **Fixed** bughouse. (A board was missing.) +- **Fixed** bugs in `game_write_complete()`. +- **Fixed** bugs in `movesToString()`. +- **Fixed** cases of possible out-of-bounds array access. +- **Fixed** ignored return values of important functions such as + `fgets()`, `fscanf()` and `sscanf()`. Multiple occurrences. +- **Fixed** incorrect format strings. +- **Fixed** uninitialized variables. +- Trimmed newlines after `fgets()` calls with `strcspn()`. +- Usage of begin/end decls in headers. ## [1.4.3] - 2024-08-04 ## - **Added** command-line option `d` (Run in the background.) diff --git a/FICS/ISC_LICENSE b/FICS/ISC_LICENSE new file mode 100644 index 0000000..eba7139 --- /dev/null +++ b/FICS/ISC_LICENSE @@ -0,0 +1,13 @@ +Copyright (c) 2023, 2024 Markus Uhlin <maxxe@rpblc.net> + +Permission to use, copy, modify, and distribute this software for any +purpose with or without fee is hereby granted, provided that the above +copyright notice and this permission notice appear in all copies. + +THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES +WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR +ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF +OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. diff --git a/FICS/adminproc.c b/FICS/adminproc.c index 32746e7..94a9448 100644 --- a/FICS/adminproc.c +++ b/FICS/adminproc.c @@ -1,23 +1,25 @@ /* - * 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. - */ + 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. +*/ #include "stdinclude.h" #include "common.h" #include <sys/param.h> +#include <err.h> + #include "adminproc.h" #include "command.h" #include "comproc.h" @@ -181,9 +183,10 @@ create_news_file(int p, param_list param, int admin) msnprintf(filename, sizeof filename, "%s/adminnews.%d", news_dir, param[0].val.integer); - fp = fopen(filename, "w"); - fprintf(fp, "%s\n", param[1].val.string); - fclose(fp); + if ((fp = fopen(filename, "w")) != NULL) { + fprintf(fp, "%s\n", param[1].val.string); + fclose(fp); + } } } else { if (param[0].val.integer > num_news) { @@ -193,9 +196,10 @@ create_news_file(int p, param_list param, int admin) msnprintf(filename, sizeof filename, "%s/news.%d", news_dir, param[0].val.integer); - fp = fopen(filename, "w"); - fprintf(fp, "%s\n", param[1].val.string); - fclose(fp); + if ((fp = fopen(filename, "w")) != NULL) { + fprintf(fp, "%s\n", param[1].val.string); + fclose(fp); + } } } @@ -219,21 +223,21 @@ add_item(char *new_item, char *filename) goto end; while (1) { - fgets(junk, MAX_LINE_SIZE, old_fp); - - if (feof(old_fp)) + if (fgets(junk, sizeof junk, old_fp) == NULL || + feof(old_fp)) break; fprintf(new_fp, "%s", junk); } end: - fclose(new_fp); + (void) fclose(new_fp); + if (old_fp) { - fclose(old_fp); - remove(filename); + (void) fclose(old_fp); + (void) remove(filename); } - rename(tmp_file, filename); + (void) rename(tmp_file, filename); return 1; } @@ -362,14 +366,14 @@ com_canewsf(int p, param_list param) PUBLIC int com_anews(int p, param_list param) { - FILE *fp; - char *junkp; - char count[10]; + FILE *fp = NULL; + char *junkp = NULL; + char count[10] = { '\0' }; char filename[MAX_FILENAME_SIZE] = { '\0' }; char junk[MAX_LINE_SIZE] = { '\0' }; int found = 0; - long int lval; - time_t crtime; + long int lval = 0; + time_t crtime = 0; msnprintf(filename, sizeof filename, "%s/newadminnews.index", news_dir); @@ -378,6 +382,9 @@ com_anews(int p, param_list param) return COM_OK; } +#define SCAN_JUNK "%ld %9s" + _Static_assert(9 < ARRAY_SIZE(count), "Array too small"); + if (param[0].type == 0) { /* * No params - then just display index over news. @@ -387,8 +394,18 @@ com_anews(int p, param_list param) news_dir); pprintf(p, "Index of recent admin news items:\n"); - fgets(junk, MAX_LINE_SIZE, fp); - sscanf(junk, "%ld %s", &lval, count); + + if (fgets(junk, sizeof junk, fp) == NULL) { + warnx("%s: fgets() error", __func__); + fclose(fp); + return COM_FAILED; + } + if (sscanf(junk, SCAN_JUNK, &lval, count) != 2) { + warnx("%s: sscanf() error: too few items", __func__); + fclose(fp); + return COM_FAILED; + } + rscan_news2(fp, p, 9); junkp = junk; @@ -406,8 +423,18 @@ com_anews(int p, param_list param) */ pprintf(p, "Index of all admin news items:\n"); - fgets(junk, MAX_LINE_SIZE, fp); - sscanf(junk, "%ld %s", &lval, count); + + if (fgets(junk, sizeof junk, fp) == NULL) { + warnx("%s: fgets() error", __func__); + fclose(fp); + return COM_FAILED; + } + if (sscanf(junk, SCAN_JUNK, &lval, count) != 2) { + warnx("%s: sscanf() error: too few items", __func__); + fclose(fp); + return COM_FAILED; + } + rscan_news(fp, p, 0); junkp = junk; @@ -421,13 +448,14 @@ com_anews(int p, param_list param) } else { while (!feof(fp) && !found) { junkp = junk; - fgets(junk, MAX_LINE_SIZE, fp); - if (feof(fp)) + if (fgets(junk, sizeof junk, fp) == NULL) break; if (strlen(junk) > 1) { - sscanf(junkp, "%ld %s", &lval, count); + if (sscanf(junkp, SCAN_JUNK, &lval, count) != 2) + warnx("%s: sscanf() error...", __func__); + crtime = lval; if (!strcmp(count, param[0].val.word)) { @@ -474,9 +502,15 @@ com_anews(int p, param_list param) PUBLIC int strcmpwild(char *mainstr, char *searchstr) { - if (strlen(mainstr) < strlen(searchstr)) + size_t len[2]; + + len[0] = strlen(mainstr); + len[1] = strlen(searchstr); + + if (len[0] < len[1]) return 1; - for (size_t i = 0; i < strlen(mainstr); i++) { + + for (size_t i = 0; i < len[0]; i++) { if (searchstr[i] == '*') return 0; if (mainstr[i] != searchstr[i]) @@ -564,7 +598,7 @@ com_checkSOCKET(int p, param_list param) PUBLIC int com_checkPLAYER(int p, param_list param) { - char *player = param[0].val.word; + char *v_player = param[0].val.word; int p1; ASSERT(parray[p].adminLevel >= ADMIN_ADMIN); @@ -576,8 +610,8 @@ com_checkPLAYER(int p, param_list param) if (p1 < 0) { p1 = (-p1) - 1; - pprintf(p, "%s is not logged in.\n", player); - stolower(player); + pprintf(p, "%s is not logged in.\n", v_player); + stolower(v_player); pprintf(p, "name = %s\n", parray[p1].name); pprintf(p, "login = %s\n", parray[p1].login); @@ -597,7 +631,7 @@ com_checkPLAYER(int p, param_list param) } else { p1 = p1 - 1; - pprintf(p, "%s is number %d in parray of size %d\n", player, p1, + pprintf(p, "%s is number %d in parray of size %d\n", v_player, p1, (p_num + 1)); pprintf(p, "name = %s\n", parray[p1].name); pprintf(p, "login = %s\n", parray[p1].login); @@ -688,7 +722,8 @@ com_checkGAME(int p, param_list param) // than that for (g = 0; g < g_num; g++) { - multicol_store(m, tmp); + memset(tmp, 0, sizeof tmp); // XXX + multicol_store(m, tmp); // is this call right? if (!strcasecmp(garray[g].white_name, param[0].val.word)) { @@ -852,13 +887,13 @@ com_checkGAME(int p, param_list param) PUBLIC int com_remplayer(int p, param_list param) { - char *player = param[0].val.word; + char *v_player = param[0].val.word; char playerlower[MAX_LOGIN_NAME] = { '\0' }; int p1, lookup; ASSERT(parray[p].adminLevel >= ADMIN_ADMIN); - mstrlcpy(playerlower, player, sizeof(playerlower)); + mstrlcpy(playerlower, v_player, sizeof(playerlower)); stolower(playerlower); p1 = player_new(); lookup = player_read(p1, playerlower); @@ -876,7 +911,7 @@ com_remplayer(int p, param_list param) player_remove(p1); if (lookup) { - pprintf(p, "No player by the name %s is registered.\n", player); + pprintf(p, "No player by the name %s is registered.\n", v_player); return COM_OK; } @@ -886,10 +921,10 @@ com_remplayer(int p, param_list param) } if (!player_kill(playerlower)) { - pprintf(p, "Player %s removed.\n", player); - UpdateRank(TYPE_BLITZ, NULL, NULL, player); - UpdateRank(TYPE_STAND, NULL, NULL, player); - UpdateRank(TYPE_WILD, NULL, NULL, player); + pprintf(p, "Player %s removed.\n", v_player); + UpdateRank(TYPE_BLITZ, NULL, NULL, v_player); + UpdateRank(TYPE_STAND, NULL, NULL, v_player); + UpdateRank(TYPE_WILD, NULL, NULL, v_player); } else { pprintf(p, "Remplayer failed.\n"); } @@ -914,14 +949,14 @@ com_remplayer(int p, param_list param) PUBLIC int com_raisedead(int p, param_list param) { - char *player = param[0].val.word; + char *v_player = param[0].val.word; char newplayerlower[MAX_LOGIN_NAME] = { '\0' }; char playerlower[MAX_LOGIN_NAME] = { '\0' }; int p1, p2, lookup; ASSERT(parray[p].adminLevel >= ADMIN_ADMIN); - mstrlcpy(playerlower, player, sizeof playerlower); + mstrlcpy(playerlower, v_player, sizeof playerlower); stolower(playerlower); if (player_find_bylogin(playerlower) >= 0) { @@ -936,7 +971,7 @@ com_raisedead(int p, param_list param) if (!lookup) { pprintf(p, "A player by the name %s is already registered.\n", - player); + v_player); pprintf(p, "Obtain a new handle for the dead person.\n"); pprintf(p, "Then use raisedead [oldname] [newname].\n"); return COM_OK; @@ -944,22 +979,22 @@ com_raisedead(int p, param_list param) if (param[1].type == TYPE_NULL) { if (!player_raise(playerlower)) { - pprintf(p, "Player %s raised from dead.\n", player); + pprintf(p, "Player %s raised from dead.\n", v_player); p1 = player_new(); if (!player_read(p1, playerlower)) { if (parray[p1].s_stats.rating > 0) { - UpdateRank(TYPE_STAND, player, - &parray[p1].s_stats, player); + UpdateRank(TYPE_STAND, v_player, + &parray[p1].s_stats, v_player); } if (parray[p1].b_stats.rating > 0) { - UpdateRank(TYPE_BLITZ, player, - &parray[p1].b_stats, player); + UpdateRank(TYPE_BLITZ, v_player, + &parray[p1].b_stats, v_player); } if (parray[p1].w_stats.rating > 0) { - UpdateRank(TYPE_WILD, player, - &parray[p1].w_stats, player); + UpdateRank(TYPE_WILD, v_player, + &parray[p1].w_stats, v_player); } } @@ -988,14 +1023,14 @@ com_raisedead(int p, param_list param) if (!lookup) { pprintf(p, "A player by the name %s is already " - "registered.\n", player); + "registered.\n", v_player); pprintf(p, "Obtain another new handle for the dead " "person.\n"); return COM_OK; } if (!player_reincarn(playerlower, newplayerlower)) { - pprintf(p, "Player %s reincarnated to %s.\n", player, + pprintf(p, "Player %s reincarnated to %s.\n", v_player, newplayer); p2 = player_new(); @@ -1518,14 +1553,14 @@ PUBLIC int com_asethandle(int p, param_list param) { char *newplayer = param[1].val.word; - char *player = param[0].val.word; + char *v_player = param[0].val.word; char newplayerlower[MAX_LOGIN_NAME] = { '\0' }; char playerlower[MAX_LOGIN_NAME] = { '\0' }; int p1; ASSERT(parray[p].adminLevel >= ADMIN_ADMIN); - mstrlcpy(playerlower, player, sizeof playerlower); + mstrlcpy(playerlower, v_player, sizeof playerlower); stolower(playerlower); mstrlcpy(newplayerlower, newplayer, sizeof newplayerlower); @@ -1544,7 +1579,7 @@ com_asethandle(int p, param_list param) p1 = player_new(); if (player_read(p1, playerlower)) { - pprintf(p, "No player by the name %s is registered.\n", player); + pprintf(p, "No player by the name %s is registered.\n", v_player); player_remove(p1); return COM_OK; } else { @@ -1571,22 +1606,22 @@ com_asethandle(int p, param_list param) if ((!player_rename(playerlower, newplayerlower)) && (!player_read(p1, newplayerlower))) { - pprintf(p, "Player %s renamed to %s.\n", player, newplayer); + pprintf(p, "Player %s renamed to %s.\n", v_player, newplayer); strfree(parray[p1].name); parray[p1].name = xstrdup(newplayer); player_save(p1); if (parray[p1].s_stats.rating > 0) { UpdateRank(TYPE_STAND, newplayer, &parray[p1].s_stats, - player); + v_player); } if (parray[p1].b_stats.rating > 0) { UpdateRank(TYPE_BLITZ, newplayer, &parray[p1].b_stats, - player); + v_player); } if (parray[p1].w_stats.rating > 0) { UpdateRank(TYPE_WILD, newplayer, &parray[p1].w_stats, - player); + v_player); } } else { pprintf(p, "Asethandle failed.\n"); diff --git a/FICS/algcheck.c b/FICS/algcheck.c index 250c129..15642fd 100644 --- a/FICS/algcheck.c +++ b/FICS/algcheck.c @@ -21,11 +21,16 @@ name email yy/mm/dd Change Richard Nash 93/10/22 Created Markus Uhlin 24/05/05 Revised + Markus Uhlin 25/04/05 alg_parse_move: + return ambiguous move on + out-of-bounds array read/write. */ #include "stdinclude.h" #include "common.h" +#include <err.h> + #include "algcheck.h" #include "board.h" #include "maxxes-utils.h" @@ -250,6 +255,11 @@ alg_parse_move(char *mstr, game_state_t *gs, move_t *mt) NextPieceLoop(gs->board, &f, &r, gs->onMove);) { if ((ff != ALG_UNKNOWN) && (ff != f)) continue; + if (r < 0 || r >= 8) { + warnx("%s: out-of-bounds array read/write: " + "r=%d", __func__, r); + return MOVE_AMBIGUOUS; + } if (piecetype(gs->board[f][r]) != piece) continue; if (gs->onMove == WHITE) { @@ -257,6 +267,11 @@ alg_parse_move(char *mstr, game_state_t *gs, move_t *mt) } else { tmpr = r - 1; } + if (tmpr < 0 || tmpr >= 8) { + warnx("%s: out-of-bounds array read/write: " + "tmpr=%d", __func__, tmpr); + return MOVE_AMBIGUOUS; + } if (gs->board[tf][tmpr] == NOPIECE) { if ((gs->ep_possible[((gs->onMove == WHITE) ? diff --git a/FICS/board.c b/FICS/board.c index f687b3e..d0b65a2 100644 --- a/FICS/board.c +++ b/FICS/board.c @@ -25,6 +25,7 @@ Markus Uhlin 24/04/13 Added usage of the functions from 'maxxes-utils.h'. Markus Uhlin 24/06/01 Added and made use of brand(). + Markus Uhlin 25/04/06 Fixed Clang Tidy warnings. */ #include "stdinclude.h" @@ -1133,7 +1134,7 @@ board_read_file(char *category, char *gname, game_state_t *gs) case 'g': case 'h': onFile = (c - 'a'); - onRank = -1; + onRank = -1; // NOLINT: dead store break; case '1': case '2': @@ -1161,7 +1162,7 @@ board_read_file(char *category, char *gname, game_state_t *gs) onColor = -1; onPiece = -1; onFile = -1; - onRank = -1; + onRank = -1; // NOLINT: dead store break; default: break; diff --git a/FICS/build.mk b/FICS/build.mk index 3dad928..834ebef 100644 --- a/FICS/build.mk +++ b/FICS/build.mk @@ -18,6 +18,7 @@ OBJS = $(SRC_DIR)adminproc.o\ $(SRC_DIR)gameproc.o\ $(SRC_DIR)iset.o\ $(SRC_DIR)legal.o\ + $(SRC_DIR)legal2.o\ $(SRC_DIR)lists.o\ $(SRC_DIR)matchproc.o\ $(SRC_DIR)maxxes-utils.o\ @@ -30,11 +31,46 @@ OBJS = $(SRC_DIR)adminproc.o\ $(SRC_DIR)ratings.o\ $(SRC_DIR)rmalloc.o\ $(SRC_DIR)shutdown.o\ + $(SRC_DIR)sought.o\ $(SRC_DIR)talkproc.o\ $(SRC_DIR)utils.o\ $(SRC_DIR)variable.o\ $(SRC_DIR)vers.o +SRCS = $(SRC_DIR)adminproc.c\ + $(SRC_DIR)algcheck.c\ + $(SRC_DIR)assert_error.c\ + $(SRC_DIR)board.c\ + $(SRC_DIR)command.c\ + $(SRC_DIR)comproc.c\ + $(SRC_DIR)eco.c\ + $(SRC_DIR)fics_getsalt.cpp\ + $(SRC_DIR)ficslim.cpp\ + $(SRC_DIR)ficsmain.c\ + $(SRC_DIR)formula.c\ + $(SRC_DIR)gamedb.c\ + $(SRC_DIR)gameproc.c\ + $(SRC_DIR)iset.cpp\ + $(SRC_DIR)legal.c\ + $(SRC_DIR)legal2.c\ + $(SRC_DIR)lists.c\ + $(SRC_DIR)matchproc.c\ + $(SRC_DIR)maxxes-utils.c\ + $(SRC_DIR)movecheck.c\ + $(SRC_DIR)multicol.c\ + $(SRC_DIR)network.c\ + $(SRC_DIR)obsproc.c\ + $(SRC_DIR)playerdb.c\ + $(SRC_DIR)rating_conv.c\ + $(SRC_DIR)ratings.c\ + $(SRC_DIR)rmalloc.c\ + $(SRC_DIR)shutdown.c\ + $(SRC_DIR)sought.cpp\ + $(SRC_DIR)talkproc.c\ + $(SRC_DIR)utils.c\ + $(SRC_DIR)variable.c\ + $(SRC_DIR)vers.c + AP_OBJS = $(SRC_DIR)fics_addplayer.o MR_OBJS = $(SRC_DIR)makerank.o diff --git a/FICS/command.c b/FICS/command.c index 1161cfd..569c27a 100644 --- a/FICS/command.c +++ b/FICS/command.c @@ -27,6 +27,15 @@ functions. Markus Uhlin 24/05/01 Replaced unbounded string handling functions. + Markus Uhlin 24/11/17 process_prompt: + handle 'COM_FAILED' differently + Markus Uhlin 24/11/23 Improved: + check_news() and + rscan_news(). + Markus Uhlin 24/11/25 Null checks + Markus Uhlin 25/03/09 Fixed double free() + Markus Uhlin 25/03/11 Fixed memleak + Markus Uhlin 25/03/16 Fixed use of 32-bit 'time_t' */ #include "stdinclude.h" @@ -123,7 +132,11 @@ parse_command(char *com_string, char **comm, char **parameters) PUBLIC int alias_lookup(char *tmp, alias_type *alias_list, int numalias) { - for (int i = 0; (alias_list[i].comm_name && i < numalias); i++) { + if (numalias >= MAX_ALIASES) + return -1; + for (int i = 0; + (i < numalias && alias_list[i].comm_name != NULL); + i++) { if (!strcmp(tmp, alias_list[i].comm_name)) return i; } @@ -565,12 +578,18 @@ process_login(int p, char *loginname) if (!alphastring(loginname)) { pprintf(p, "\nSorry, names can only consist of lower " "and upper case letters. Try again.\n"); + rfree(loginnameii); + loginnameii = NULL; } else if (strlen(loginname) < 3) { pprintf(p, "\nA name should be at least three " "characters long! Try again.\n"); + rfree(loginnameii); + loginnameii = NULL; } else if (strlen(loginname) > 17) { pprintf(p, "\nSorry, names may be at most 17 " "characters long. Try again.\n"); + rfree(loginnameii); + loginnameii = NULL; } else if (in_list(p, L_BAN, loginnameii)) { pprintf(p, "\nPlayer \"%s\" is banned.\n", loginname); rfree(loginnameii); @@ -630,12 +649,12 @@ process_login(int p, char *loginname) parray[p].status = PLAYER_PASSWORD; turn_echo_off(parray[p].socket); rfree(loginnameii); + loginnameii = NULL; // XXX 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; } @@ -646,7 +665,6 @@ process_login(int p, char *loginname) 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; } @@ -657,7 +675,6 @@ process_login(int p, char *loginname) 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; } @@ -668,7 +685,6 @@ process_login(int p, char *loginname) 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; } } @@ -698,20 +714,25 @@ boot_out(int p, int p1) } PUBLIC void -rscan_news(FILE *fp, int p, int lc) +rscan_news(FILE *fp, int p, time_t lc) { - char *junkp; - char count[10]; - char junk[MAX_LINE_SIZE]; - long int lval; - time_t crtime; + char *junkp = NULL; + char count[10] = { '\0' }; + char junk[MAX_LINE_SIZE] = { '\0' }; + long int lval = 0; + time_t crtime = 0; + + if (fgets(junk, sizeof junk, fp) == NULL || + feof(fp)) + return; - fgets(junk, MAX_LINE_SIZE, fp); + _Static_assert(ARRAY_SIZE(count) > 9, "Unexpected array size"); - if (feof(fp)) + if (sscanf(junk, "%ld %9s", &lval, count) != 2) { + warnx("%s: sscanf() error: too few items", __func__); return; + } - sscanf(junk, "%ld %s", &lval, count); crtime = lval; if ((crtime - lc) < 0) @@ -731,15 +752,18 @@ rscan_news(FILE *fp, int p, int lc) PRIVATE void check_news(int p, int admin) { - FILE *fp; - char count[10]; - char filename[MAX_FILENAME_SIZE]; - char junk[MAX_LINE_SIZE]; - char *junkp; - long int lval; - time_t crtime; +#define SCAN_JUNK "%ld %9s" + FILE *fp = NULL; + char count[10] = { '\0' }; + char filename[MAX_FILENAME_SIZE] = { '\0' }; + char junk[MAX_LINE_SIZE] = { '\0' }; + char *junkp = NULL; + long int lval = 0; + time_t crtime = 0; time_t lc = player_lastconnect(p); + _Static_assert(ARRAY_SIZE(count) > 9, "Unexpected array size"); + if (admin) { msnprintf(filename, sizeof filename, "%s/newadminnews.index", news_dir); @@ -753,11 +777,23 @@ check_news(int p, int admin) if (num_anews == -1) { num_anews = count_lines(fp); fclose(fp); - fp = fopen(filename, "r"); + if ((fp = fopen(filename, "r")) == NULL) { + warn("%s: can't find admin news index (%s)", + __func__, filename); + return; + } + } + + if (fgets(junk, sizeof junk, fp) == NULL) { + warnx("%s: fgets() error", __func__); + fclose(fp); + return; + } else if (sscanf(junk, SCAN_JUNK, &lval, count) != 2) { + warnx("%s: sscanf() error", __func__); + fclose(fp); + return; } - fgets(junk, MAX_LINE_SIZE, fp); - sscanf(junk, "%ld %s", &lval, count); crtime = lval; if ((crtime - lc) < 0) { @@ -798,8 +834,16 @@ check_news(int p, int admin) } } - fgets(junk, MAX_LINE_SIZE, fp); - sscanf(junk, "%ld %s", &lval, count); + if (fgets(junk, sizeof junk, fp) == NULL) { + warnx("%s: fgets() error", __func__); + fclose(fp); + return; + } else if (sscanf(junk, SCAN_JUNK, &lval, count) != 2) { + warnx("%s: sscanf() error", __func__); + fclose(fp); + return; + } + crtime = lval; if ((crtime - lc) < 0) { @@ -1029,6 +1073,10 @@ process_prompt(int p, char *command) retval = COM_OK; break; case COM_FAILED: + pprintf(p, "%s: Command has failed.\n", cmd); + retval = COM_OK; + pprintf(p, "%s", parray[p].prompt); + break; case COM_BADCOMMAND: pprintf(p, "%s: Command not found.\n", cmd); retval = COM_OK; @@ -1052,6 +1100,9 @@ process_input(int fd, char *com_string) if (p < 0) { fprintf(stderr, "FICS: Input from a player not in array!\n"); return -1; + } else if (com_string == NULL) { + fprintf(stderr, "FICS: Command string is NULL!\n"); + return -1; } commanding_player = p; @@ -1070,7 +1121,7 @@ process_input(int fd, char *com_string) case PLAYER_LOGIN: retval = process_login(p, com_string); - if (retval == COM_LOGOUT && com_string != NULL) { + if (retval == COM_LOGOUT) { // && com_string != NULL fprintf(stderr, "%s tried to log in and failed.\n", com_string); } @@ -1173,10 +1224,10 @@ PUBLIC int process_heartbeat(int *fd) { time_t now = time(NULL); - int time_since_last; - static int last_comfile = 0; - static int last_space = 0; - static int lastcalled = 0; + time_t time_since_last; + static time_t last_comfile = 0; + static time_t last_space = 0; + static time_t lastcalled = 0; if (lastcalled == 0) time_since_last = 0; diff --git a/FICS/command_list.h b/FICS/command_list.h index 4cccd20..9d64e76 100644 --- a/FICS/command_list.h +++ b/FICS/command_list.h @@ -23,6 +23,7 @@ Markus Uhlin 23/12/10 Cleaned up the file a bit Markus Uhlin 24/07/08 Reformatted and sorted the lists Markus Uhlin 24/07/13 Added 'iset' to the command list + Markus Uhlin 24/11/24 Added 'sought' to the command list */ #ifndef _COMMAND_LIST_H @@ -40,6 +41,7 @@ #include "rating_conv.h" #include "ratings.h" #include "shutdown.h" +#include "sought.h" #include "talkproc.h" extern command_type command_list[]; @@ -156,6 +158,7 @@ PUBLIC command_type command_list[] = { {"simpass", "", com_simpass, ADMIN_USER}, {"simprev", "", com_simprev, ADMIN_USER}, {"smoves", "wi", com_smoves, ADMIN_USER}, + {"sought", "o", com_sought, ADMIN_USER}, {"sposition", "ww", com_sposition, ADMIN_USER}, {"statistics", "", com_statistics, ADMIN_USER}, {"stored", "o", com_stored, ADMIN_USER}, diff --git a/FICS/common.h b/FICS/common.h index b30fd1d..66823ea 100644 --- a/FICS/common.h +++ b/FICS/common.h @@ -24,8 +24,10 @@ Markus Uhlin 24/01/03 Added/deleted macros and redefined ASSERT(). Markus Uhlin 24/01/04 Added PRINTFLIKE(). - Markus Uhlin 24/04/02 Defined UNUSED_PARAM() and - UNUSED_VAR() + Markus Uhlin 24/04/02 Defined: + UNUSED_PARAM() and + UNUSED_VAR(). + Markus Uhlin 24/11/20 Added FICS begin/end macros. */ #ifndef _COMMON_H @@ -60,4 +62,12 @@ #define ASSERT(expression) ((void) 0) #endif +#ifdef __cplusplus +#define __FICS_BEGIN_DECLS extern "C" { +#define __FICS_END_DECLS } +#else +#define __FICS_BEGIN_DECLS +#define __FICS_END_DECLS +#endif + #endif /* _COMMON_H */ diff --git a/FICS/comproc.c b/FICS/comproc.c index 405a59f..2b5a04e 100644 --- a/FICS/comproc.c +++ b/FICS/comproc.c @@ -33,6 +33,16 @@ format strings. Markus Uhlin 24/03/23 Size-bounded string handling plus truncation checks. + Markus Uhlin 24/11/19 Improved com_news(). + plogins: fscanf: + added width specification + Markus Uhlin 25/03/08 Calc string length once + Markus Uhlin 25/03/11 Fixed possibly uninitialized + value 'rat' in who_terse(). + Markus Uhlin 25/03/16 Fixed use of 32-bit 'time_t'. + Markus Uhlin 25/03/16 Fixed untrusted array index. + Markus Uhlin 25/03/25 com_unalias: fixed overflowed + array index read/write. */ #include "stdinclude.h" @@ -141,14 +151,14 @@ rscan_news2(FILE *fp, int p, int num) PUBLIC int com_news(int p, param_list param) { - FILE *fp; - char *junkp; + FILE *fp = NULL; + char *junkp = NULL; char count[10] = { '\0' }; char filename[MAX_FILENAME_SIZE] = { '\0' }; char junk[MAX_LINE_SIZE] = { '\0' }; int found = 0; - long int lval; - time_t crtime; + long int lval = 0; + time_t crtime = 0; snprintf(filename, sizeof filename, "%s/newnews.index", news_dir); @@ -157,15 +167,24 @@ com_news(int p, param_list param) return COM_OK; } +#define SCAN_JUNK "%ld %9s" + _Static_assert(9 < ARRAY_SIZE(count), "'count' too small"); + if (param[0].type == 0) { /* * No params - then just display index of last ten * news items. */ - pprintf(p,"Index of recent news items:\n"); - fgets(junk, MAX_LINE_SIZE, fp); - sscanf(junk, "%ld %s", &lval, count); + pprintf(p, "Index of recent news items:\n"); + + if (fgets(junk, sizeof junk, fp) == NULL || + sscanf(junk, SCAN_JUNK, &lval, count) != 2) { + warnx("%s: error: fgets() or sscanf()", __func__); + fclose(fp); + return COM_FAILED; + } + rscan_news2(fp, p, 9); junkp = junk; @@ -183,8 +202,14 @@ com_news(int p, param_list param) */ pprintf(p, "Index of all news items:\n"); - fgets(junk, MAX_LINE_SIZE, fp); - sscanf(junk, "%ld %s", &lval, count); + + if (fgets(junk, sizeof junk, fp) == NULL || + sscanf(junk, SCAN_JUNK, &lval, count) != 2) { + warnx("%s: error: fgets() or sscanf()", __func__); + fclose(fp); + return COM_FAILED; + } + rscan_news(fp, p, 0); junkp = junk; @@ -205,8 +230,8 @@ com_news(int p, param_list param) if (fgets(junk, sizeof junk, fp) == NULL || feof(fp)) break; - - sscanf(junkp, "%ld %s", &lval, count); + if (sscanf(junkp, SCAN_JUNK, &lval, count) != 2) + warnx("%s: sscanf() error...", __func__); crtime = lval; if (!strcmp(count, param[0].val.word)) { @@ -382,11 +407,10 @@ com_stats(int p, param_list param) (MAX_OBSERVE > MAX_SIMUL ? MAX_OBSERVE : MAX_SIMUL) char line[255] = { '\0' }; char tmp[255] = { '\0' }; - int g, i; + int g, i, t; int numbers[NUMBERS_SIZE]; int onTime; int p1, connected; - time_t t; if (param[0].type == TYPE_WORD) { if (!FindPlayer(p, param[0].val.word, &p1, &connected)) @@ -405,9 +429,11 @@ com_stats(int p, param_list param) snprintf(tmp, sizeof tmp, " Idle: %s\n", hms_desc(player_idle(p1))); } else { - if ((t = player_lastdisconnect(p1))) { + time_t last; + + if ((last = player_lastdisconnect(p1)) != 0) { snprintf(tmp, sizeof tmp, "(Last disconnected %s):\n", - strltime(&t)); + strltime(&last)); } else strlcpy(tmp, "(Never connected.)\n", sizeof tmp); } @@ -563,7 +589,7 @@ com_stats(int p, param_list param) onTime = ((time(NULL) - parray[p1].logon_time) + parray[p1].totalTime); - pprintf(p, "Total time on-line: %s\n", hms_desc(onTime) ); + pprintf(p, "Total time on-line: %s\n", hms_desc(onTime)); pprintf(p, "%% of life on-line: %3.1f (since %s)\n", // XXX (double) ((onTime * 100) / (double) (time(NULL) - parray[p1].timeOfReg)), @@ -716,21 +742,25 @@ com_date(int p, param_list param) PRIVATE int plogins(int p, char *fname) { - FILE *fp; - char ipstr[20]; - char loginName[MAX_LOGIN_NAME + 1]; - int inout, registered; - long int lval; - time_t tval; + FILE *fp = NULL; + char ipstr[20] = { '\0' }; + char loginName[MAX_LOGIN_NAME + 1] = { '\0' }; + int registered = 0; + long int lval = 0; + time_t tval = 0; + uint16_t inout = 0; if ((fp = fopen(fname, "r")) == NULL) { pprintf(p, "Sorry, no login information available.\n"); return COM_OK; } + _Static_assert(19 < ARRAY_SIZE(ipstr), "'ipstr' too small"); + _Static_assert(19 < ARRAY_SIZE(loginName), "'loginName' too small"); + while (!feof(fp)) { - if (fscanf(fp, "%d %s %ld %d %s\n", &inout, loginName, &lval, - ®istered, ipstr) != 5) { + if (fscanf(fp, "%hu %19s %ld %d %19s\n", &inout, loginName, + &lval, ®istered, ipstr) != 5) { fprintf(stderr, "FICS: Error in login info format. " "%s\n", fname); fclose(fp); @@ -739,8 +769,13 @@ plogins(int p, char *fname) tval = lval; - pprintf(p, "%s: %-17s %-6s", strltime(&tval), loginName, - inout_string[inout]); + if (inout >= ARRAY_SIZE(inout_string)) { + warnx("%s: %s: 'inout' too large (%u)", __func__, fname, + inout); + } else { + pprintf(p, "%s: %-17s %-6s", strltime(&tval), loginName, + inout_string[inout]); + } if (parray[p].adminLevel > 0) pprintf(p, " from %s\n", ipstr); @@ -821,6 +856,8 @@ who_terse(int p, int num, int *plist, int type) rat = parray[p1].s_stats.rating; else if (type == light_rat) rat = parray[p1].l_stats.rating; + else // Fallback to std... + rat = parray[p1].s_stats.rating; if (type == none) { strlcpy(ptmp, " ", sizeof ptmp); @@ -845,8 +882,10 @@ who_terse(int p, int num, int *plist, int type) } if (p == p1) { - psprintf_highlight(p, ptmp + strlen(ptmp), - sizeof ptmp - strlen(ptmp), "%s", parray[p1].name); + const size_t len = strlen(ptmp); + + psprintf_highlight(p, ptmp + len, + sizeof ptmp - len, "%s", parray[p1].name); } else { strlcat(ptmp, parray[p1].name, sizeof ptmp); } @@ -908,9 +947,12 @@ who_verbose(int p, int num, int plist[]) p1WithAttrs[17] = '\0'; if (p == p1) { + size_t len; + strlcpy(tmp, " ", sizeof tmp); - psprintf_highlight(p, tmp + strlen(tmp), - sizeof tmp - strlen(tmp), "%-17s", p1WithAttrs); + len = strlen(tmp); + psprintf_highlight(p, tmp + len, sizeof tmp - len, + "%-17s", p1WithAttrs); } else { ret = snprintf(tmp, sizeof tmp, " %-17s", p1WithAttrs); @@ -993,7 +1035,7 @@ who_winloss(int p, int num, int plist[]) snprintf(tmp, sizeof tmp, "%5s\n", hms(player_idle(p1), 0, 0, 0)); } else { - strlcpy(tmp, " \n", sizeof tmp); + strlcpy(tmp, "\n", sizeof tmp); } strlcat(playerLine, tmp, sizeof playerLine); @@ -1278,10 +1320,7 @@ com_who(int p, param_list param) sel_bits |= WHO_REGISTERED; break; case 'l': // Sort order - cmp_func = alpha_cmp; - sort_type = none; - break; - case 'A': // Sort order + case 'A': cmp_func = alpha_cmp; sort_type = none; break; @@ -1599,14 +1638,32 @@ com_unalias(int p, param_list param) pprintf(p, "You have no alias named '%s'.\n", param[0].val.word); } else { + bool removed = false; + const int sz = (int) ARRAY_SIZE(parray[0].alias_list); + rfree(parray[p].alias_list[al].comm_name); rfree(parray[p].alias_list[al].alias); + parray[p].alias_list[al].comm_name = NULL; + parray[p].alias_list[al].alias = NULL; + for (int i = al; i < parray[p].numAlias; i++) { + if (i >= sz || i + 1 >= sz) { + warnx("%s: overflowed array index read/write", + __func__); + break; + } + parray[p].alias_list[i].comm_name = parray[p].alias_list[i + 1].comm_name; parray[p].alias_list[i].alias = parray[p].alias_list[i + 1].alias; + removed = true; + } + + if (!removed) { + pprintf(p, "Remove error.\n"); + return COM_FAILED; } parray[p].numAlias--; @@ -1750,10 +1807,9 @@ FindAndShowFile(int p, param_list param, char *dir) { char *iwant, *filenames[1000]; int i; - static char nullify = '\0'; if (param[0].type == TYPE_NULL) { - iwant = &nullify; + iwant = NULL; } else { iwant = param[0].val.word; @@ -1767,8 +1823,9 @@ FindAndShowFile(int p, param_list param, char *dir) i = search_directory(dir, iwant, filenames, ARRAY_SIZE(filenames)); if (i == 0) { - pprintf(p, "No information available on \"%s\".\n", iwant); - } else if (i == 1 || !strcmp(*filenames, iwant)) { + pprintf(p, "No information available on \"%s\".\n", + (iwant ? iwant : "")); + } else if (i == 1 || !strcmp(*filenames, iwant ? iwant : "")) { if (psend_file(p, dir, *filenames)) { /* * We should never reach this unless the file @@ -1779,7 +1836,7 @@ FindAndShowFile(int p, param_list param, char *dir) "Thank you.\n"); } } else { - if (*iwant) + if (iwant && *iwant) pprintf(p, "Matches:\n"); display_directory(p, filenames, i); } @@ -1807,7 +1864,6 @@ com_mailsource(int p, param_list param) char fname[MAX_FILENAME_SIZE]; char subj[120]; int count; - static char nullify = '\0'; if (!parray[p].registered) { pprintf(p, "Only registered people can use the mailsource " @@ -1816,14 +1872,15 @@ com_mailsource(int p, param_list param) } if (param[0].type == TYPE_NULL) - iwant = &nullify; + iwant = NULL; else iwant = param[0].val.word; if ((count = search_directory(source_dir, iwant, buffer, ARRAY_SIZE(buffer))) == 0) { - pprintf(p, "Found no source file matching \"%s\".\n", iwant); - } else if ((count == 1) || !strcmp(iwant, *buffer)) { + pprintf(p, "Found no source file matching \"%s\".\n", + (iwant ? iwant : "")); + } else if ((count == 1) || !strcmp(iwant ? iwant : "", *buffer)) { snprintf(subj, sizeof subj, "FICS source file from server " "%s: %s", fics_hostname, @@ -1839,7 +1896,7 @@ com_mailsource(int p, param_list param) } else { pprintf(p, "Found %d source files matching that:\n", count); - if (*iwant) { + if (iwant && *iwant) { display_directory(p, buffer, count); } else { // this junk is to get *.c *.h char *s; @@ -1870,7 +1927,6 @@ com_mailhelp(int p, param_list param) char subj[120]; int count; int lang = parray[p].language; - static char nullify = '\0'; if (!parray[p].registered) { pprintf(p, "Only registered people can use the mailhelp " @@ -1879,7 +1935,7 @@ com_mailhelp(int p, param_list param) } if (param[0].type == TYPE_NULL) - iwant = &nullify; + iwant = NULL; else iwant = param[0].val.word; @@ -1900,8 +1956,9 @@ com_mailhelp(int p, param_list param) } if (count == 0) { - pprintf(p, "Found no help file matching \"%s\".\n", iwant); - } else if (count == 1 || !strcmp(*buffer, iwant)) { + pprintf(p, "Found no help file matching \"%s\".\n", + (iwant ? iwant : "")); + } else if (count == 1 || !strcmp(*buffer, iwant ? iwant : "")) { snprintf(subj, sizeof subj, "FICS help file from server %s: %s", fics_hostname, *buffer); diff --git a/FICS/comproc.h b/FICS/comproc.h index bee30b9..30ef3ff 100644 --- a/FICS/comproc.h +++ b/FICS/comproc.h @@ -79,7 +79,7 @@ extern int com_uptime(int, param_list); extern int com_uscf(int, param_list); extern int com_who(int, param_list); -extern void rscan_news(FILE *, int, int); +extern void rscan_news(FILE *, int, time_t); extern void rscan_news2(FILE *, int, int); #endif /* _COMPROC_H */ diff --git a/FICS/config.h b/FICS/config.h index 9b965dd..6edcb66 100644 --- a/FICS/config.h +++ b/FICS/config.h @@ -32,7 +32,7 @@ /* * Must be a dns recognisable host name */ -#define SERVER_HOSTNAME "jujube.rpblc.net" +#define SERVER_HOSTNAME "rpblc.net" /* * At AFICS we just use 'fics'. But for your server you might want to @@ -21,6 +21,18 @@ #include <bsd/string.h> #endif +#define FENPOS_SIZE 73 +#define ONMOVE_SIZE 2 + +#define ECO_MAXFILENAME 1024 +#define ECO_MAXTMP 1024 + +#define SCAN_FP_AND_ONMOVE "%72[\x21-z] %1s" + +#define SCAN_ECO "%3[0-z]" +#define SCAN_NIC "%5[.-z]" +#define SCAN_LONG "%255[^*\n]" + PRIVATE char *book_dir = DEFAULT_BOOK; PRIVATE ECO_entry *ECO_book[1096]; @@ -121,10 +133,10 @@ ECO_init(void) { FILE *fp; char ECO[4] = {0,0,0,0}; - char FENpos[73] = { '\0' }; - char filename[1024] = { '\0' }; - char onMove[2] = {0,0}; - char tmp[1024] = { '\0' }; + char FENpos[FENPOS_SIZE] = { '\0' }; + char filename[ECO_MAXFILENAME] = { '\0' }; + char onMove[ONMOVE_SIZE] = {0,0}; + char tmp[ECO_MAXTMP] = { '\0' }; char *ptmp = tmp; int i = 0; @@ -135,21 +147,32 @@ ECO_init(void) while (!feof(fp)) { (void) strlcpy(ptmp, "", sizeof tmp); - fgets(ptmp, 1024, fp); - if (feof(fp)) - continue; - - /* XXX */ - sscanf(ptmp, "%[\x21-z] %s", FENpos, onMove); - (void) strlcat(FENpos, " ", sizeof FENpos); - (void) strlcat(FENpos, onMove, sizeof FENpos); + if (fgets(ptmp, sizeof tmp, fp) == NULL || + feof(fp)) + break; + + if (sscanf(ptmp, SCAN_FP_AND_ONMOVE, FENpos, onMove) != 2) { + warnx("%s: sscanf() error (%s:%d)", __func__, + filename, i); + break; + } else if (strlcat(FENpos, " ", sizeof FENpos) >= sizeof FENpos || + strlcat(FENpos, onMove, sizeof FENpos) >= sizeof FENpos) { + warnx("%s: strlcat() error (%s:%d)", __func__, + filename, i); + break; + } (void) strlcpy(ptmp, "", sizeof tmp); - fgets(ptmp, 1024, fp); - if (feof(fp)) - continue; - sscanf(ptmp, "%[0-z]", ECO); + + if (fgets(ptmp, sizeof tmp, fp) == NULL || + feof(fp)) + break; + else if (sscanf(ptmp, SCAN_ECO, ECO) != 1) { + warnx("%s: scan eco error (%s:%d)", __func__, + filename, i); + break; + } if ((ECO_book[i] = malloc(sizeof(ECO_entry))) == NULL) err(1, "Cound not alloc mem for ECO entry %d", i); @@ -180,11 +203,11 @@ PUBLIC void NIC_init(void) { FILE *fp; - char FENpos[73] = { '\0' }; + char FENpos[FENPOS_SIZE] = { '\0' }; char NIC[6] = {0,0,0,0,0,0}; - char filename[1024] = { '\0' }; - char onMove[2] = {0,0}; - char tmp[1024] = { '\0' }; + char filename[ECO_MAXFILENAME] = { '\0' }; + char onMove[ONMOVE_SIZE] = {0,0}; + char tmp[ECO_MAXTMP] = { '\0' }; char *ptmp = tmp; int i = 0; @@ -195,20 +218,32 @@ NIC_init(void) while (!feof(fp)) { (void) strlcpy(ptmp, "", sizeof tmp); - fgets(ptmp, 1024, fp); - if (feof(fp)) - continue; - - sscanf(ptmp, "%[\x21-z] %s", FENpos, onMove); - (void) strlcat(FENpos, " ", sizeof FENpos); - (void) strlcat(FENpos, onMove, sizeof FENpos); + if (fgets(ptmp, sizeof tmp, fp) == NULL || + feof(fp)) + break; + + if (sscanf(ptmp, SCAN_FP_AND_ONMOVE, FENpos, onMove) != 2) { + warnx("%s: sscanf() error (%s:%d)", __func__, + filename, i); + break; + } else if (strlcat(FENpos, " ", sizeof FENpos) >= sizeof FENpos || + strlcat(FENpos, onMove, sizeof FENpos) >= sizeof FENpos) { + warnx("%s: strlcat() error (%s:%d)", __func__, + filename, i); + break; + } (void) strlcpy(ptmp, "", sizeof tmp); - fgets(ptmp, 1024, fp); - if (feof(fp)) - continue; - sscanf(ptmp, "%[.-z]", NIC); + + if (fgets(ptmp, sizeof tmp, fp) == NULL || + feof(fp)) + break; + else if (sscanf(ptmp, SCAN_NIC, NIC) != 1) { + warnx("%s: scan nic error (%s:%d)", __func__, + filename, i); + break; + } if ((NIC_book[i] = malloc(sizeof(NIC_entry))) == NULL) err(1, "Cound not alloc mem for NIC entry %d", i); @@ -232,11 +267,11 @@ PUBLIC void LONG_init(void) { FILE *fp; - char FENpos[73] = { '\0' }; + char FENpos[FENPOS_SIZE] = { '\0' }; char LONG[256] = { '\0' }; - char filename[1024] = { '\0' }; - char onMove[2] = {0,0}; - char tmp[1024] = { '\0' }; + char filename[ECO_MAXFILENAME] = { '\0' }; + char onMove[ONMOVE_SIZE] = {0,0}; + char tmp[ECO_MAXTMP] = { '\0' }; char *ptmp = tmp; int i = 0; @@ -247,21 +282,32 @@ LONG_init(void) while (!feof(fp)) { (void) strlcpy(ptmp, "", sizeof tmp); - fgets(ptmp, 1024, fp); - if (feof(fp)) - continue; - - /* XXX */ - sscanf(ptmp, "%[\x21-z] %s", FENpos, onMove); - (void) strlcat(FENpos, " ", sizeof FENpos); - (void) strlcat(FENpos, onMove, sizeof FENpos); + if (fgets(ptmp, sizeof tmp, fp) == NULL || + feof(fp)) + break; + + if (sscanf(ptmp, SCAN_FP_AND_ONMOVE, FENpos, onMove) != 2) { + warnx("%s: sscanf() error (%s:%d)", __func__, + filename, i); + break; + } else if (strlcat(FENpos, " ", sizeof FENpos) >= sizeof FENpos || + strlcat(FENpos, onMove, sizeof FENpos) >= sizeof FENpos) { + warnx("%s: strlcat() error (%s:%d)", __func__, + filename, i); + break; + } (void) strlcpy(ptmp, "", sizeof tmp); - fgets(ptmp, 1024, fp); - if (feof(fp)) - continue; - sscanf(ptmp, "%[^*\n]", LONG); + + if (fgets(ptmp, sizeof tmp, fp) == NULL || + feof(fp)) + break; + else if (sscanf(ptmp, SCAN_LONG, LONG) != 1) { + warnx("%s: scan long error (%s:%d)", __func__, + filename, i); + break; + } if ((LONG_book[i] = malloc(sizeof(LONG_entry))) == NULL) err(1, "Cound not alloc mem for LONG entry %d", i); diff --git a/FICS/fics_addplayer.c b/FICS/fics_addplayer.c index 80be9fe..ad6c43f 100644 --- a/FICS/fics_addplayer.c +++ b/FICS/fics_addplayer.c @@ -27,6 +27,8 @@ Markus Uhlin 24/04/01 Fixed empty hostname Markus Uhlin 24/04/04 Added usage of explicit_bzero() Markus Uhlin 24/05/25 Added command-line option 'a' + Markus Uhlin 25/03/23 Output restart notice if the + player is admin. */ #include "stdinclude.h" @@ -157,6 +159,11 @@ main(int argc, char *argv[]) funame, fname, email, password); printf("Admin: %s\n", (parray[p].adminLevel > 0 ? "Yes" : "No")); + if (parray[p].adminLevel > 0) { + printf("Player is admin. Please restart FICS in order for " + "the changes to take effect.\n"); + } + snprintf(text, sizeof text, "\nYour player account has been created.\n\n" "Login Name: %s\n" diff --git a/FICS/fics_getsalt.h b/FICS/fics_getsalt.h index c22f9c8..317ae27 100644 --- a/FICS/fics_getsalt.h +++ b/FICS/fics_getsalt.h @@ -1,17 +1,13 @@ #ifndef FICS_GETSALT_H #define FICS_GETSALT_H +#include "common.h" + #define FICS_SALT_BEG "$2b$10$" #define FICS_SALT_SIZE 30 -#ifdef __cplusplus -extern "C" { -#endif - +__FICS_BEGIN_DECLS char *fics_getsalt(void); - -#ifdef __cplusplus -} -#endif +__FICS_END_DECLS #endif diff --git a/FICS/ficslim.h b/FICS/ficslim.h index 79d819c..40f9b9a 100644 --- a/FICS/ficslim.h +++ b/FICS/ficslim.h @@ -3,15 +3,11 @@ #include <time.h> -#ifdef __cplusplus -extern "C" { -#endif +#include "common.h" +__FICS_BEGIN_DECLS extern const time_t g_time_min; extern const time_t g_time_max; - -#ifdef __cplusplus -} -#endif +__FICS_END_DECLS #endif diff --git a/FICS/ficsmain.c b/FICS/ficsmain.c index d6efd59..4110635 100644 --- a/FICS/ficsmain.c +++ b/FICS/ficsmain.c @@ -26,6 +26,7 @@ and marked usage() '__dead'. Markus Uhlin 24/06/01 Added command-line option 'l' Markus Uhlin 24/08/03 Added command-line option 'd' + Markus Uhlin 24/12/04 Added command-line option 'v' */ #include "stdinclude.h" @@ -48,6 +49,7 @@ #endif #include "ficsmain.h" #include "legal.h" +#include "legal2.h" #include "network.h" #include "playerdb.h" #include "ratings.h" @@ -143,6 +145,10 @@ GetArgs(int argc, char *argv[]) break; case 'l': puts(legalNotice); + puts(legalNotice2); + exit(0); + case 'v': + printf("%s %s\n", VERS_NUM, COMP_DATE); exit(0); } } else { @@ -182,7 +188,7 @@ main_event_loop(void) PRIVATE __dead void usage(char *progname) { - fprintf(stderr, "Usage: %s [-p port] [-C] [-dhl]\n", progname); + fprintf(stderr, "Usage: %s [-p port] [-C] [-dhlv]\n", progname); fprintf(stderr, "\t\t-p port\t\tSpecify port. (Default=%d)\n", DEFAULT_PORT); fprintf(stderr, "\t\t-C\t\tStart with console player connected " @@ -190,6 +196,7 @@ usage(char *progname) fprintf(stderr, "\t\t-d\t\tRun in the background.\n"); fprintf(stderr, "\t\t-h\t\tDisplay this information.\n"); fprintf(stderr, "\t\t-l\t\tDisplay the legal notice and exit.\n"); + fprintf(stderr, "\t\t-v\t\tDisplay version.\n"); exit(EXIT_FAILURE); } diff --git a/FICS/formula.c b/FICS/formula.c index ffbca20..dd5ff23 100644 --- a/FICS/formula.c +++ b/FICS/formula.c @@ -574,10 +574,11 @@ ChooseClauses(player *who, char *formula) return ret; for (i = 0; formula[i] != '\0' && formula[i] != '#'; i++) { - if (formula[i] != 'f' || (i > 0 && isalnum(formula[i - 1])) || - !isdigit(formula[i + 1])) + if ((i > 0 && isalnum(formula[i - 1])) || formula[i] != 'f' || + !isdigit(formula[i + 1]) || + sscanf(&formula[i], "f%d", &which) != 1) continue; - sscanf(&formula[i], "f%d", &which); + ret |= (1 << (which - 1)); } diff --git a/FICS/gamedb.c b/FICS/gamedb.c index 2e65af0..2656447 100644 --- a/FICS/gamedb.c +++ b/FICS/gamedb.c @@ -30,12 +30,27 @@ a crash. Markus Uhlin 24/07/18 Return value checking Markus Uhlin 24/08/03 See previous change + Markus Uhlin 24/11/23 Added width specifications to + multiple fscanf() calls. + Markus Uhlin 24/11/23 Fixed bugs in movesToString() + Markus Uhlin 24/11/25 Null checks + Markus Uhlin 24/12/02 Fixed bugs and ignored function + return values. + Markus Uhlin 25/03/18 Fixed unchecked return values + Markus Uhlin 25/03/25 ReadGameState: fixed truncated + stdio return value. + Markus Uhlin 25/04/01 Fixed call of risky function + Markus Uhlin 25/04/01 ReadV1GameFmt: guard num half + moves. + Markus Uhlin 25/04/06 Fixed Clang Tidy warnings. */ #include "stdinclude.h" #include "common.h" #include <err.h> +#include <errno.h> +#include <limits.h> #include "command.h" #include "config.h" @@ -85,9 +100,11 @@ PRIVATE char gameString[GAME_STRING_LEN]; PRIVATE int get_empty_slot(void) { - for (int i = 0; i < g_num; i++) { - if (garray[i].status == GAME_EMPTY) - return i; + if (garray != NULL) { + for (int i = 0; i < g_num; i++) { + if (garray[i].status == GAME_EMPTY) + return i; + } } g_num++; @@ -551,7 +568,8 @@ movesToString(int g, int pgn) char tmp[160] = { '\0' }; int i, col; int wr, br; - unsigned int curTime; + struct tm *tm_ptr = NULL; + time_t curTime; wr = garray[g].white_rating; br = garray[g].black_rating; @@ -567,11 +585,15 @@ movesToString(int g, int pgn) bstr[garray[g].type], serv_name, serv_loc); - strftime(tmp, sizeof(tmp), - "[Date \"%Y.%m.%d\"]\n" - "[Time \"%H:%M:%S\"]\n", - localtime((time_t *) &curTime)); - mstrlcat(gameString, tmp, sizeof gameString); + + if ((tm_ptr = localtime(&curTime)) != NULL) { + strftime(tmp, sizeof(tmp), + "[Date \"%Y.%m.%d\"]\n" + "[Time \"%H:%M:%S\"]\n", + tm_ptr); + mstrlcat(gameString, tmp, sizeof gameString); + } else + warn("%s: localtime", __func__); msnprintf(tmp, sizeof tmp, "[Round \"-\"]\n" @@ -620,6 +642,10 @@ movesToString(int g, int pgn) mstrlcat(gameString, "\n", sizeof gameString); } else { + /* + * !pgn + */ + msnprintf(gameString, sizeof gameString, "\n%s ", garray[g].white_name); @@ -641,8 +667,12 @@ movesToString(int g, int pgn) mstrlcat(gameString, tmp, sizeof gameString); mstrlcat(gameString, "--- ", sizeof gameString); - mstrlcat(gameString, (char *) (localtime((time_t *) &curTime)), - sizeof gameString); + + if ((tm_ptr = localtime(&curTime)) != NULL) { + strftime(tmp, sizeof tmp, "%Y.%m.%d %H:%M:%S", tm_ptr); + mstrlcat(gameString, tmp, sizeof gameString); + } else + warn("%s: localtime", __func__); if (garray[g].rated) { mstrlcat(gameString, "\nRated ", sizeof gameString); @@ -864,7 +894,8 @@ WriteMoves(FILE *fp, move_t *m) /* Are we using from-file or from-rank in 'algString'? */ - i = strlen(m->algString) - 1; + if ((i = strlen(m->algString)) > 0) + i -= 1; if (m->algString[i] == '+') { check = 1; @@ -904,7 +935,10 @@ ReadMove(FILE *fp, move_t *m) if (fgets(line, sizeof line, fp) == NULL) return -1; - if (sscanf(line, "%d %d %d %d %d %d %d %d %d \"%[^\"]\" \"%[^\"]\" " + _Static_assert(ARRAY_SIZE(m->moveString) > 7, "'moveString' too small"); + _Static_assert(ARRAY_SIZE(m->algString) > 7, "'algString' too small"); + + if (sscanf(line, "%d %d %d %d %d %d %d %d %d \"%7[^\"]\" \"%7[^\"]\" " "%u %u\n", &m->color, &m->fromFile, &m->fromRank, @@ -948,8 +982,8 @@ WriteGameState(FILE *fp, game_state_t *gs) PRIVATE int ReadGameState(FILE *fp, game_state_t *gs, int version) { - char pieceChar; int i, j; + int pieceChar; int wkmoved, wqrmoved, wkrmoved, bkmoved, bqrmoved, bkrmoved; if (version == 0) { @@ -960,11 +994,12 @@ ReadGameState(FILE *fp, game_state_t *gs, int version) } } } else { - getc(fp); /* Skip past a newline. */ + (void) getc(fp); /* Skip past a newline. */ for (i = 0; i < 8; i++) { for (j = 0; j < 8; j++) { - pieceChar = getc(fp); + if ((pieceChar = getc(fp)) == EOF) + return -1; gs->board[i][j] = CharToPiece(pieceChar); } } @@ -1033,10 +1068,16 @@ got_attr_value(int g, char *attr, char *value, FILE *fp, char *file) } else if (!strcmp(attr, "type:")) { garray[g].type = atoi(value); } else if (!strcmp(attr, "halfmoves:")) { - garray[g].numHalfMoves = atoi(value); - - if (garray[g].numHalfMoves == 0) + if ((garray[g].numHalfMoves = atoi(value)) == 0) return 0; + else if (garray[g].numHalfMoves < 0 || + (size_t)garray[g].numHalfMoves > INT_MAX / sizeof(move_t)) { + warnx("%s: num half moves out-of-bounds (%d)", __func__, + garray[g].numHalfMoves); + return -1; + } else { + /* null */; + } garray[g].moveListSize = garray[g].numHalfMoves; garray[g].moveList = reallocarray(NULL, sizeof(move_t), @@ -1241,8 +1282,10 @@ ReadV1GameFmt(game *g, FILE *fp, const char *file, int version) _Static_assert(17 < ARRAY_SIZE(g->black_name), "Unexpected array size"); ret[0] = fscanf(fp, "%17s %17s", g->white_name, g->black_name); - ret[1] = fscanf(fp, "%d %d", &g->white_rating, &g->black_rating); - ret[2] = fscanf(fp, "%d %d %d %d", + ret[1] = fscanf(fp, "%d %d", // NOLINT + &g->white_rating, + &g->black_rating); + ret[2] = fscanf(fp, "%d %d %d %d", // NOLINT &g->wInitTime, &g->wIncrement, &g->bInitTime, @@ -1285,10 +1328,16 @@ ReadV1GameFmt(game *g, FILE *fp, const char *file, int version) ret[0] = fscanf(fp, "%d %d %d %d", &g->private, &g->type, &g->rated, &g->clockStopped); - ret[1] = fscanf(fp, "%d", &g->numHalfMoves); + ret[1] = fscanf(fp, "%d", &g->numHalfMoves); // NOLINT if (ret[0] != 4 || ret[1] != 1) { warnx("%s: fscanf error: %s", __func__, file); return -1; + } else if (g->numHalfMoves < 0 || (size_t)g->numHalfMoves > + INT_MAX / sizeof(move_t)) { + warnx("%s: warning: num half moves out-of-bounds (%d)", + __func__, + g->numHalfMoves); + return -1; } if (ReadV1Moves(g, fp) != 0) { @@ -1311,7 +1360,7 @@ ReadGameAttrs(FILE *fp, char *fname, int g) { char *attr, *value; char line[MAX_GLINE_SIZE] = { '\0' }; - int len; + int len = 0; int version = 0; if (fgets(line, sizeof line, fp) == NULL) { @@ -1330,7 +1379,8 @@ ReadGameAttrs(FILE *fp, char *fname, int g) } else { do { if ((len = strlen(line)) <= 1) { - fgets(line, sizeof line, fp); + if (fgets(line, sizeof line, fp) == NULL) + break; continue; } @@ -1345,7 +1395,8 @@ ReadGameAttrs(FILE *fp, char *fname, int g) if (!*value) { fprintf(stderr, "FICS: Error reading file %s\n", fname); - fgets(line, sizeof line, fp); + if (fgets(line, sizeof line, fp) == NULL) + break; continue; } @@ -1356,7 +1407,8 @@ ReadGameAttrs(FILE *fp, char *fname, int g) if (!*value) { fprintf(stderr, "FICS: Error reading file %s\n", fname); - fgets(line, sizeof line, fp); + if (fgets(line, sizeof line, fp) == NULL) + break; continue; } @@ -1365,7 +1417,8 @@ ReadGameAttrs(FILE *fp, char *fname, int g) if (got_attr_value(g, attr, value, fp, fname)) return -1; - fgets(line, sizeof line, fp); + if (fgets(line, sizeof line, fp) == NULL) + break; } while (!feof(fp)); } @@ -1549,8 +1602,9 @@ game_save(int g) /* * Create link for easier stored game finding */ - if (bp->login[0] != wp->login[0]) - link(fname, lname); + if (bp->login[0] != wp->login[0] && + link(fname, lname) != 0) + warn("%s: link() error", __func__); return 0; } @@ -1594,10 +1648,9 @@ RemoveHistGame(char *file, int maxlines) char Opponent[MAX_LOGIN_NAME + 1] = { '\0' }; char line[MAX_LINE_SIZE] = { '\0' }; int count = 0; - long int When, oppWhen; + long int When = 0, oppWhen = 0; _Static_assert(20 < ARRAY_SIZE(Opponent), "Not within bounds"); - When = oppWhen = 0; if ((fp = fopen(file, "r")) == NULL) { return; @@ -1614,12 +1667,8 @@ RemoveHistGame(char *file, int maxlines) count++; - while (!feof(fp)) { - fgets(line, ARRAY_SIZE(line), fp); - - if (!feof(fp)) - count++; - } + while (fgets(line, ARRAY_SIZE(line), fp) != NULL) + count++; fclose(fp); stolower(Opponent); @@ -1647,22 +1696,37 @@ RemHist(char *who) msnprintf(fName, sizeof fName, "%s/player_data/%c/%s.%s", stats_dir, who[0], who, STATS_GAMES); - fp = fopen(fName, "r"); - - if (fp != NULL) { - while (!feof(fp)) { - fscanf(fp, "%*d %*c %*d %*c %*d %s %*s %*d %*d %*d " - "%*d %*s %*s %ld", Opp, &When); + if ((fp = fopen(fName, "r")) != NULL) { + long int iter_no = 0; + + while (!feof(fp) && !ferror(fp)) { + const int ret = fscanf(fp, "%*d %*c %*d %*c %*d %19s " + "%*s %*d %*d %*d %*d %*s %*s %ld\n", Opp, &When); + if (ret != 2) { + warnx("%s: fscanf() error (%s:%ld)", __func__, + fName, iter_no); +// iter_no++; + break; + } stolower(Opp); oppWhen = OldestHistGame(Opp); if (oppWhen > When || oppWhen <= 0L) { - msnprintf(fName, sizeof fName, "%s/%ld/%ld", - hist_dir, When % 100, When); - unlink(fName); + char histfile[MAX_FILENAME_SIZE] = { '\0' }; + + msnprintf(histfile, sizeof histfile, + "%s/%ld/%ld", hist_dir, (When % 100), When); + if (unlink(histfile) != 0) { + warn("%s: unlink(%s)", __func__, + histfile); + } } + + iter_no++; } + + fclose(fp); } } @@ -1732,11 +1796,10 @@ write_g_out(int g, char *file, int maxlines, int isDraw, char *EndSymbol, type[3] = '\0'; - fp = fopen(file, "r"); - - if (fp) { - while (!feof(fp)) - fgets(tmp, 1024, fp); + if ((fp = fopen(file, "r")) != NULL) { + while (fgets(tmp, sizeof tmp, fp) != NULL) { + /* null */; + } if (sscanf(ptmp, "%d", &count) != 1) warnx("%s: failed to read 'count'", __func__); fclose(fp); @@ -1799,38 +1862,46 @@ write_g_out(int g, char *file, int maxlines, int isDraw, char *EndSymbol, * Find from_spot in journal list - return 0 if corrupted */ PUBLIC int -journal_get_info(int p, char from_spot, char *WhiteName, int *WhiteRating, - char *BlackName, int *BlackRating, char *type, int *t, int *i, char *eco, - char *ending, char *result, char *fname) +journal_get_info(struct JGI_context *ctx, const char *fname) { FILE *fp; char count; if ((fp = fopen(fname, "r")) == NULL) { fprintf(stderr, "Corrupt journal file! %s\n", fname); - pprintf(p, "The journal file is corrupt! See an admin.\n"); + pprintf(ctx->p, "The journal file is corrupt! See an admin.\n"); return 0; } while (!feof(fp)) { - if (fscanf(fp, "%c %s %d %s %d %s %d %d %s %s %s\n", + _Static_assert(ARRAY_SIZE(ctx->WhiteName) > 20, + "'WhiteName' too small"); + _Static_assert(ARRAY_SIZE(ctx->BlackName) > 20, + "'BlackName' too small"); + + _Static_assert(ARRAY_SIZE(ctx->type) > 99, "'type' too small"); + _Static_assert(ARRAY_SIZE(ctx->eco) > 99, "'eco' too small"); + _Static_assert(ARRAY_SIZE(ctx->ending) > 99, "'ending' too small"); + _Static_assert(ARRAY_SIZE(ctx->result) > 99, "'result' too small"); + + if (fscanf(fp, "%c %20s %d %20s %d %99s %d %d %99s %99s %99s\n", &count, - WhiteName, &(*WhiteRating), - BlackName, &(*BlackRating), - type, - &(*t), &(*i), - eco, - ending, - result) != 11) { + ctx->WhiteName, &ctx->WhiteRating, + ctx->BlackName, &ctx->BlackRating, + ctx->type, + &ctx->t, &ctx->i, + ctx->eco, + ctx->ending, + ctx->result) != 11) { fprintf(stderr, "FICS: Error in journal info format. " "%s\n", fname); - pprintf(p, "The journal file is corrupt! Error in " + pprintf(ctx->p, "The journal file is corrupt! Error in " "internal format.\n"); fclose(fp); return 0; } - if (tolower(count) == from_spot) { + if (tolower(count) == ctx->from_spot) { fclose(fp); return 1; } @@ -1881,11 +1952,22 @@ addjournalitem(int p, char count2, char *WhiteName2, int WhiteRating2, ending2, result2); fclose(fp2); - rename(fname2, fname); + xrename(__func__, fname2, fname); return; } else { + _Static_assert(ARRAY_SIZE(WhiteName) > 19, + "'WhiteName' too small"); + _Static_assert(ARRAY_SIZE(BlackName) > 19, + "'BlackName' too small"); + + _Static_assert(ARRAY_SIZE(type) > 99, "'type' too small"); + _Static_assert(ARRAY_SIZE(eco) > 99, "'eco' too small"); + _Static_assert(ARRAY_SIZE(ending) > 99, "'ending' too small"); + _Static_assert(ARRAY_SIZE(result) > 99, "'result' too small"); + while (!feof(fp)) { - if (fscanf(fp, "%c %s %d %s %d %s %d %d %s %s %s\n", + if (fscanf(fp, "%c %19s %d %19s %d %99s %d %d %99s " + "%99s %99s\n", &count, WhiteName, &WhiteRating, BlackName, &BlackRating, @@ -1944,7 +2026,7 @@ addjournalitem(int p, char count2, char *WhiteName2, int WhiteRating2, fclose(fp); fclose(fp2); - rename(fname2, fname); + xrename(__func__, fname2, fname); } PUBLIC int @@ -1970,8 +2052,16 @@ pjournal(int p, int p1, char *fname) pprintf(p, " White Rating Black Rating " "Type ECO End Result\n"); + _Static_assert(ARRAY_SIZE(WhiteName) > 19, "'WhiteName' too small"); + _Static_assert(ARRAY_SIZE(BlackName) > 19, "'BlackName' too small"); + + _Static_assert(ARRAY_SIZE(type) > 99, "'type' too small"); + _Static_assert(ARRAY_SIZE(eco) > 99, "'eco' too small"); + _Static_assert(ARRAY_SIZE(ending) > 99, "'ending' too small"); + _Static_assert(ARRAY_SIZE(result) > 99, "'result' too small"); + while (!feof(fp)) { - if (fscanf(fp, "%c %s %d %s %d %s %d %d %s %s %s\n", + if (fscanf(fp, "%c %19s %d %19s %d %99s %d %d %99s %99s %99s\n", &count, WhiteName, &WhiteRating, BlackName, &BlackRating, @@ -2009,7 +2099,7 @@ pgames(int p, int p1, char *fname) char OppName[MAX_LOGIN_NAME + 1] = { '\0' }; char eco[100] = { '\0' }; char ending[100] = { '\0' }; - char result[2] = { 0,0 }; + char result[2] = { 0,0 }; // XXX: right size? char type[100] = { '\0' }; int MyRating, OppRating; int count; @@ -2025,8 +2115,16 @@ pgames(int p, int p1, char *fname) pprintf(p, " Opponent Type " "ECO End Date\n"); + _Static_assert(ARRAY_SIZE(result) > 1, "'result' too small"); + _Static_assert(ARRAY_SIZE(MyColor) > 1, "'MyColor' too small"); + _Static_assert(ARRAY_SIZE(OppName) > 19, "'OppName' too small"); + _Static_assert(ARRAY_SIZE(type) > 99, "'type' too small"); + _Static_assert(ARRAY_SIZE(eco) > 99, "'eco' too small"); + _Static_assert(ARRAY_SIZE(ending) > 99, "'ending' too small"); + while (!feof(fp)) { - if (fscanf(fp, "%d %s %d %s %d %s %s %d %d %d %d %s %s %ld\n", + if (fscanf(fp, "%d %1s %d %1s %d %19s %99s %d %d %d %d %99s " + "%99s %ld\n", &count, result, &MyRating, MyColor, &OppRating, OppName, type, @@ -2057,9 +2155,9 @@ pgames(int p, int p1, char *fname) PUBLIC void game_write_complete(int g, int isDraw, char *EndSymbol) { - FILE *fp; + FILE *fp = NULL; char fname[MAX_FILENAME_SIZE] = { '\0' }; - int fd; + int fd = -1; int wp = garray[g].white, bp = garray[g].black; time_t now = time(NULL); @@ -2068,20 +2166,21 @@ game_write_complete(int g, int isDraw, char *EndSymbol) hist_dir, (long int)(now % 100), (long int)now); + errno = 0; fd = open(fname, (O_WRONLY | O_CREAT | O_EXCL), 0644); - if (fd == EEXIST) + if (fd == -1 && errno == EEXIST) now++; - } while (fd == EEXIST); + } while (fd == -1 && errno == EEXIST); if (fd >= 0) { - fp = fdopen(fd, "w"); - if (fp != NULL) + if ((fp = fdopen(fd, "w")) != NULL) { WriteGameFile(fp, g); - else { + fclose(fp); + } else { fprintf(stderr, "Trouble writing history file %s", fname); } - fclose(fp); + close(fd); } diff --git a/FICS/gamedb.h b/FICS/gamedb.h index d123028..8dc84d3 100644 --- a/FICS/gamedb.h +++ b/FICS/gamedb.h @@ -27,9 +27,11 @@ #ifndef _GAMEDB_H #define _GAMEDB_H +#include <stdint.h> #include <time.h> #include "board.h" +#include "command.h" extern const char *bstr[7]; extern const char *rstr[2]; @@ -139,15 +141,30 @@ typedef struct _game { move_t *examMoveList; // Extra movelist for examine int examMoveListSize; - unsigned int startTime; // The relative time the game started - unsigned int lastMoveTime; // Last time a move was made - unsigned int lastDecTime; // Last time a players clock was + uint64_t startTime; // The relative time the game started + uint64_t lastMoveTime; // Last time a move was made + uint64_t lastDecTime; // Last time a players clock was // decremented int result; int winner; } game; +struct JGI_context { + int p; + char from_spot; + char WhiteName[MAX_LOGIN_NAME + 1]; + int WhiteRating; + char BlackName[MAX_LOGIN_NAME + 1]; + int BlackRating; + char type[100]; + int t; + int i; + char eco[100]; + char ending[100]; + char result[100]; +}; + extern game *garray; extern int g_num; @@ -171,8 +188,7 @@ extern int game_remove(int); extern int game_save(int); extern int game_zero(int); extern int got_attr_value(int, char *, char *, FILE *, char *); -extern int journal_get_info(int, char, char *, int *, char *, int *, - char *, int *, int *, char *, char *, char *, char *); +extern int journal_get_info(struct JGI_context *, const char *); extern int pgames(int, int, char *); extern int pjournal(int, int, char *); extern void MakeFENpos(int, char *, size_t); diff --git a/FICS/gameproc.c b/FICS/gameproc.c index b4a538e..cd6398e 100644 --- a/FICS/gameproc.c +++ b/FICS/gameproc.c @@ -30,6 +30,7 @@ Markus Uhlin 24/04/13 Added usage of msnprintf(), mstrlcpy() and mstrlcat(). Markus Uhlin 24/05/05 Added usage of reallocarray(). + Markus Uhlin 25/03/09 Calc string length once */ #include "stdinclude.h" @@ -848,18 +849,23 @@ CheckRepetition(int p, int g) char *pos; int flag1 = 1, flag2 = 1; int move_num; + size_t len[3]; if (garray[g].numHalfMoves < 8) // Can't have three repeats any quicker. return 0; + len[0] = strlen(pos1); + len[1] = strlen(pos2); + for (move_num = garray[g].game_state.lastIrreversable; move_num < garray[g].numHalfMoves - 1; move_num++) { pos = GetFENpos(g, move_num); + len[2] = strlen(pos); - if (strlen(pos1) == strlen(pos) && !strcmp(pos1, pos)) + if (len[0] == len[2] && !strcmp(pos1, pos)) flag1++; - if (strlen(pos2) == strlen(pos) && !strcmp(pos2, pos)) + if (len[1] == len[2] && !strcmp(pos2, pos)) flag2++; } @@ -1828,7 +1834,11 @@ com_goboard(int p, param_list param) } on = parray[p].simul_info.onBoard; - g = parray[p].simul_info.boards[on]; + + if ((g = parray[p].simul_info.boards[on]) < 0) { + pprintf(p, "Internal error! Unexpected negative value!\n"); + return COM_OK; + } if (p1 == garray[g].black) { pprintf(p, "You are already at that board!\n"); diff --git a/FICS/iset.h b/FICS/iset.h index f88d9fb..3fa3f6f 100644 --- a/FICS/iset.h +++ b/FICS/iset.h @@ -2,15 +2,10 @@ #define GUARD_ISET_H #include "command.h" /* param_list */ +#include "common.h" -#ifdef __cplusplus -extern "C" { -#endif - +__FICS_BEGIN_DECLS int com_iset(int, param_list); - -#ifdef __cplusplus -} -#endif +__FICS_END_DECLS #endif diff --git a/FICS/legal.c b/FICS/legal.c index ee1d01b..87b5685 100644 --- a/FICS/legal.c +++ b/FICS/legal.c @@ -24,7 +24,7 @@ #include "legal.h" -char legalNotice[] = +const char legalNotice[] = "fics - An internet chess server.\n" "Copyright (C) 1993 Richard V. Nash\n" "\n" diff --git a/FICS/legal.h b/FICS/legal.h index 5219938..a87b8a8 100644 --- a/FICS/legal.h +++ b/FICS/legal.h @@ -26,6 +26,6 @@ #ifndef _LEGAL_H #define _LEGAL_H -extern char legalNotice[]; +extern const char legalNotice[]; #endif /* _LEGAL_H */ diff --git a/FICS/legal2.c b/FICS/legal2.c new file mode 100644 index 0000000..528814a --- /dev/null +++ b/FICS/legal2.c @@ -0,0 +1,16 @@ +#include "legal2.h" + +const char legalNotice2[] = + "Copyright (c) 2023, 2024 Markus Uhlin <maxxe@rpblc.net>\n" + "\n" + "Permission to use, copy, modify, and distribute this software for any\n" + "purpose with or without fee is hereby granted, provided that the above\n" + "copyright notice and this permission notice appear in all copies.\n" + "\n" + "THE SOFTWARE IS PROVIDED \"AS IS\" AND THE AUTHOR DISCLAIMS ALL WARRANTIES\n" + "WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF\n" + "MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR\n" + "ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES\n" + "WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN\n" + "ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF\n" + "OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.\n"; diff --git a/FICS/legal2.h b/FICS/legal2.h new file mode 100644 index 0000000..0ee2173 --- /dev/null +++ b/FICS/legal2.h @@ -0,0 +1,10 @@ +#ifndef FICS_LEGAL2_H_ +#define FICS_LEGAL2_H_ + +#include "common.h" + +__FICS_BEGIN_DECLS +extern const char legalNotice2[]; +__FICS_END_DECLS + +#endif diff --git a/FICS/lists.c b/FICS/lists.c index 5f93baf..e3f2873 100644 --- a/FICS/lists.c +++ b/FICS/lists.c @@ -8,6 +8,7 @@ #include "stdinclude.h" #include "common.h" +#include <err.h> #include <string.h> #include "command.h" @@ -59,7 +60,14 @@ list_find(int p, enum ListWhich l) int personal; personal = ListArray[l].rights == P_PERSONAL; - starter = (personal ? &parray[p].lists : &firstGlobalList); + + if (personal) { + if (p < 0) + return NULL; + starter = &parray[p].lists; + } else { + starter = &firstGlobalList; + } for (tempList = *starter; tempList != NULL; tempList = tempList->next) { if (l == tempList->which) { @@ -89,11 +97,9 @@ list_find(int p, enum ListWhich l) rfree(tempList); return NULL; } - while (!feof(fp)) { - if (fgets(listmember, 100, fp) != NULL) { - listmember[strlen(listmember) - 1] = '\0'; - tempList->member[count++] = xstrdup(listmember); - } + while (fgets(listmember, ARRAY_SIZE(listmember), fp) != NULL) { + listmember[strcspn(listmember, "\n")] = '\0'; + tempList->member[count++] = xstrdup(listmember); } fclose(fp); @@ -280,7 +286,7 @@ list_addsub(int p, char *list, char *who, int addsub) List *gl; char *listname, *member; char *yourthe, *addrem; - int p1, connected, loadme, personal, ch; + int p1 = -1, connected, loadme, personal, ch; if ((gl = list_findpartial(p, list, addsub)) == NULL) return COM_OK; @@ -376,6 +382,10 @@ list_addsub(int p, char *list, char *who, int addsub) addrem, listname); break; case L_COMPUTER: + if (p1 < 0) { + warnx("%s: negative player number", __func__); + break; + } if (parray[p1].b_stats.rating > 0) { UpdateRank(TYPE_BLITZ, member, &parray[p1].b_stats, member); @@ -390,6 +400,10 @@ list_addsub(int p, char *list, char *who, int addsub) } break; case L_ADMIN: + if (p1 < 0) { + warnx("%s: negative player number", __func__); + break; + } if (addsub == 1) { // adding to list parray[p1].adminLevel = 10; pprintf(p, "%s has been given an admin level " diff --git a/FICS/makerank.c b/FICS/makerank.c index 57956ed..4458f31 100644 --- a/FICS/makerank.c +++ b/FICS/makerank.c @@ -24,10 +24,10 @@ static int rtype; static int GetPlayerInfo(char *fileName, ENTRY *e) { - FILE *fp; - char NameWithCase[30]; - char field[20]; - char line[100]; + FILE *fp = NULL; + char NameWithCase[30] = { '\0' }; + char field[20] = { '\0' }; + char line[100] = { '\0' }; int done = 0; e->computer = 0; @@ -38,22 +38,32 @@ GetPlayerInfo(char *fileName, ENTRY *e) } if ((fp = fopen(fileName, "r")) == NULL || - fgets(line, sizeof line - 1, fp) == NULL || - feof(fp)) + fgets(line, sizeof line, fp) == NULL || + feof(fp)) { + if (fp) + fclose(fp); return 0; + } if (!strcmp(line, "v 1\n")) { - fgets(line, 99, fp); - - sscanf(line, "%s", e->name); - - fgets(line, 99, fp); - fgets(line, 99, fp); - fgets(line, 99, fp); + _Static_assert(ARRAY_SIZE(e->name) > 19, "Array too little"); + + if (fgets(line, sizeof line, fp) == NULL || + sscanf(line, "%19s", e->name) != 1) { + warnx("%s: fgets() or sscanf() error", __func__); + fclose(fp); + return 0; + } else if (fgets(line, sizeof line, fp) == NULL || + fgets(line, sizeof line, fp) == NULL || + fgets(line, sizeof line, fp) == NULL) { + warnx("%s: fgets() error", __func__); +// fclose(fp); +// return 0; + } - if (fscanf(fp, "%u %*u %*u %*u %u %*u %*u %*u %*u %u %*u %*u " - "%*u %u %*u %*u %*u %*u %u %*u %*u %*u %u %*u %*u %*u %*u " - "%u %*u %*u %*u %u %*u %*u %*u %*u", + if (fscanf(fp, "%d %*u %*u %*u %d %*u %*u %*u %*u %d %*u %*u " + "%*u %d %*u %*u %*u %*u %d %*u %*u %*u %d %*u %*u %*u %*u " + "%d %*u %*u %*u %d %*u %*u %*u %*u", &(e->r[0].num), &(e->r[0].rating), &(e->r[1].num), @@ -64,13 +74,31 @@ GetPlayerInfo(char *fileName, ENTRY *e) &(e->r[3].rating)) != 8) { fprintf(stderr, "OOPS: couldn't parse player file %s." "\n", fileName); + fclose(fp); + return 0; } + + done = 1; } else { do { - sscanf(line, "%s", field); + _Static_assert(ARRAY_SIZE(field) > 19, + "Unexpected array length"); + _Static_assert(ARRAY_SIZE(NameWithCase) > 29, + "Unexpected array length"); + + if (sscanf(line, "%19s", field) != 1) { + warnx("%s: sscanf() error", __func__); + fclose(fp); + return 0; + } if (!strcmp(field, "Name:")) { - sscanf(line, "%*s %s", NameWithCase); + if (sscanf(line, "%*s %29s", NameWithCase) != 1) { + warnx("%s: expected name with case", + __func__); + fclose(fp); + return 0; + } if (strcasecmp(e->name, NameWithCase)) { printf("TROUBLE: %s's handle is " @@ -82,31 +110,44 @@ GetPlayerInfo(char *fileName, ENTRY *e) "strlcpy() truncated\n", __func__); } } else if (!strcmp(field, "S_NUM:")) { - sscanf(line, "%*s %d", &(e->r[0].num)); + if (sscanf(line, "%*s %d", &(e->r[0].num)) != 1) + warnx("%s: S_NUM error", __func__); } else if (!strcmp(field, "B_NUM:")) { - sscanf(line, "%*s %d", &(e->r[1].num)); + if (sscanf(line, "%*s %d", &(e->r[1].num)) != 1) + warnx("%s: B_NUM error", __func__); } else if (!strcmp(field, "W_NUM:")) { - sscanf(line, "%*s %d", &(e->r[2].num)); + if (sscanf(line, "%*s %d", &(e->r[2].num)) != 1) + warnx("%s: W_NUM error", __func__); } else if (!strcmp(field, "L_NUM:")) { - sscanf(line, "%*s %d", &(e->r[3].num)); + if (sscanf(line, "%*s %d", &(e->r[3].num)) != 1) + warnx("%s: L_NUM error", __func__); } else if (!strcmp(field, "S_RATING:")) { - sscanf(line, "%*s %d", &(e->r[0].rating)); + if (sscanf(line, "%*s %d", + &(e->r[0].rating)) != 1) + warnx("%s: S_RATING error", __func__); } else if (!strcmp(field, "B_RATING:")) { - sscanf(line, "%*s %d", &(e->r[1].rating)); + if (sscanf(line, "%*s %d", + &(e->r[1].rating)) != 1) + warnx("%s: B_RATING error", __func__); } else if (!strcmp(field, "W_RATING:")) { - sscanf(line, "%*s %d", &(e->r[2].rating)); + if (sscanf(line, "%*s %d", + &(e->r[2].rating)) != 1) + warnx("%s: W_RATING error", __func__); } else if (!strcmp(field, "L_RATING:")) { - sscanf(line, "%*s %d", &(e->r[3].rating)); + if (sscanf(line, "%*s %d", + &(e->r[3].rating)) != 1) + warnx("%s: L_RATING error", __func__); } else if (!strcmp(field, "Network:")) { done = 1; } - fgets(line, 99, fp); + if (fgets(line, sizeof line, fp) == NULL) + break; } while (!done && !feof(fp)); } fclose(fp); - return 1; + return (done ? 1 : 0); } static int @@ -117,7 +158,7 @@ LoadEntries(void) char command[90]; char letter1; char pathInput[80]; - int len, n = 0; + int n = 0; int listsize; listsize = 100; @@ -137,13 +178,11 @@ LoadEntries(void) continue; while (1) { - fgets(e.name, MAX_LOGIN_NAME, fpPlayerList); - - if (feof(fpPlayerList)) + if (fgets(e.name, sizeof(e.name), fpPlayerList) == NULL || + feof(fpPlayerList)) break; - len = strlen(e.name); - e.name[len - 1] = '\0'; + e.name[strcspn(e.name, "\n")] = '\0'; if (e.name[0] != letter1) { printf("File %c/%s: wrong directory.\n", @@ -181,9 +220,9 @@ LoadEntries(void) static int SetComputers(int n) { - FILE *fpComp; - char comp[30]; - char line[100]; + FILE *fpComp = NULL; + char comp[30] = { '\0' }; + char line[100] = { '\0' }; int i = 0; if (snprintf(line, sizeof line, "sort -f %s", COMPUTER_FILE) >= @@ -194,12 +233,11 @@ SetComputers(int n) return 0; while (i < n) { - fgets(comp, 29, fpComp); - - if (feof(fpComp)) + if (fgets(comp, sizeof comp, fpComp) == NULL || + feof(fpComp)) break; - comp[strlen(comp) - 1] = '\0'; + comp[strcspn(comp, "\n")] = '\0'; while (i < n && strcasecmp(list[i]->name, comp) < 0) i++; diff --git a/FICS/matchproc.c b/FICS/matchproc.c index adbe5df..1b1eab8 100644 --- a/FICS/matchproc.c +++ b/FICS/matchproc.c @@ -28,6 +28,12 @@ handling. Markus Uhlin 24/03/29 Fixed potentially insecure format strings. + Markus Uhlin 24/12/02 com_accept: check that + the accept number is + within bounds. + Markus Uhlin 25/03/12 Fixed negative array + index read in + accept_match(). */ #include "stdinclude.h" @@ -309,7 +315,13 @@ accept_match(int p, int p1) unobserveAll(p); unobserveAll(p1); - which = player_find_pendfrom(p, p1, PEND_MATCH); + if ((which = player_find_pendfrom(p, p1, PEND_MATCH)) < 0) { + pprintf(p, "%s: player_find_pendfrom: error\n", __func__); + pprintf(p1, "%s accepted your challenge but a fatal error " + "occurred\n", parray[p].name); + return COM_FAILED; + } + pend = &parray[p].p_from_list[which]; wt = pend->param1; winc = pend->param2; @@ -1067,7 +1079,10 @@ com_match(int p, param_list param) .wt = wt, }; - print_bughouse(p, p1, &ctx, colorstr); + if (ctx.white >= 0) + print_bughouse(p, p1, &ctx, colorstr); + else + warnx("%s: cannot print bughouse", __func__); } if (in_list(p, L_COMPUTER, parray[p].name)) { @@ -1206,6 +1221,12 @@ com_accept(int p, param_list param) } } + if (acceptNum < 0 || + acceptNum >= (int)ARRAY_SIZE(parray[0].p_from_list)) { + pprintf(p, "Accept number out-of-bounds!\n"); + return COM_FAILED; + } + from = parray[p].p_from_list[acceptNum].whofrom; switch (parray[p].p_from_list[acceptNum].type) { diff --git a/FICS/movecheck.c b/FICS/movecheck.c index a0c9d30..5f607de 100644 --- a/FICS/movecheck.c +++ b/FICS/movecheck.c @@ -25,6 +25,8 @@ Markus Uhlin 23/12/24 Fixed dead assignment Markus Uhlin 24/05/05 Refactored and reformatted all functions. + Markus Uhlin 25/03/21 Fixed out-of-bounds array access + in has_legal_move(). */ #include "stdinclude.h" @@ -967,8 +969,10 @@ has_legal_move(game_state_t *gs) &numpossible); break; } - if (numpossible >= 500) + if (numpossible >= 500) { fprintf(stderr, "FICS: Possible move overrun\n"); + return 0; + } for (i = 0; i < numpossible; i++) { if (legal_andcheck_move(gs, f, r, possiblef[i], possibler[i])) diff --git a/FICS/network.c b/FICS/network.c index 843bef5..1e97e20 100644 --- a/FICS/network.c +++ b/FICS/network.c @@ -10,6 +10,7 @@ #include <arpa/telnet.h> #include <netinet/in.h> +#include <err.h> #include <errno.h> #include "common.h" @@ -266,7 +267,9 @@ net_send_string(int fd, char *str, int format) if ((which = findConnection(fd)) < 0) return -1; while (*str) { - for (i = 0; str[i] >= ' '; i++) { + const int upbound = (int)strlen(str); + + for (i = 0; i < upbound && str[i] >= ' '; i++) { /* null */; } @@ -302,7 +305,7 @@ net_send_string(int fd, char *str, int format) sendme(which, " ", 8 - (con[which].outPos & 7)); con[which].outPos &= ~7; - if (con[which].outPos += 8 >= LINE_WIDTH) + if ((con[which].outPos += 8) >= LINE_WIDTH) con[which].outPos = 0; break; case '\n': @@ -311,6 +314,7 @@ net_send_string(int fd, char *str, int format) break; case '\033': con[which].outPos -= 3; + // XXX: fallthrough here? default: sendme(which, str, 1); } @@ -331,12 +335,11 @@ net_send_string(int fd, char *str, int format) PUBLIC int readline2(comstr_t *cs, int who) { - int howmany, state, fd, pending; - unsigned char *start, *s, *d; - - static unsigned char ayt[] = "[Responding to AYT: Yes, I'm here.]\n"; - static unsigned char will_sga[] = { IAC, WILL, TELOPT_SGA, '\0' }; - static unsigned char will_tm[] = { IAC, WILL, TELOPT_TM, '\0' }; + int howmany, state, fd, v_pending; + static const uint8_t ayt[] = "[Responding to AYT: Yes, I'm here.]\n"; + static const uint8_t will_sga[] = { IAC, WILL, TELOPT_SGA, '\0' }; + static const uint8_t will_tm[] = { IAC, WILL, TELOPT_TM, '\0' }; + unsigned char *start, *s, *d; state = con[who].state; @@ -347,11 +350,11 @@ readline2(comstr_t *cs, int who) } s = start = con[who].inBuf; - pending = con[who].numPending; + v_pending = con[who].numPending; fd = con[who].fd; - if ((howmany = recv(fd, start + pending, MAX_STRING_LENGTH - 1 - - pending, 0)) == 0) { // error: they've disconnected + if ((howmany = recv(fd, start + v_pending, MAX_STRING_LENGTH - 1 - + v_pending, 0)) == 0) { // error: they've disconnected return -1; } else if (howmany == -1) { if (errno != EWOULDBLOCK) { // some other error @@ -365,9 +368,9 @@ readline2(comstr_t *cs, int who) } if (con[who].processed) - s += pending; + s += v_pending; else - howmany += pending; + howmany += v_pending; d = s; for (; howmany-- > 0; s++) { @@ -402,7 +405,7 @@ readline2(comstr_t *cs, int who) state = 3; // this is cheesy // but we aren't using em } else if (*s == AYT) { - send(fd, (char *)ayt, strlen((char *)ayt), 0); + send(fd, (char *)ayt, sizeof ayt - 1, 0); state = 2; } else if (*s == EL) { // erase line d = start; @@ -435,11 +438,13 @@ readline2(comstr_t *cs, int who) break; case 4: // got IAC DO if (*s == TELOPT_TM) { - send(fd, (char *)will_tm, - strlen((char *)will_tm), 0); + if (send(fd, (char *)will_tm, + sizeof will_tm - 1, 0) == -1) + warn("%s: cannot send", __func__); } else if (*s == TELOPT_SGA) { - send(fd, (char *)will_sga, - strlen((char *)will_sga), 0); + if (send(fd, (char *)will_sga, + sizeof will_sga - 1, 0) == -1) + warn("%s: cannot send", __func__); } state = 2; break; @@ -467,9 +472,9 @@ readline2(comstr_t *cs, int who) } PUBLIC int -net_init(int port) +net_init(int p_port) { - int opt; + int opt, ret; struct linger lingeropt; struct sockaddr_in serv_addr; @@ -501,22 +506,30 @@ net_init(int port) memset(&serv_addr, 0, sizeof serv_addr); serv_addr.sin_family = AF_INET; serv_addr.sin_addr.s_addr = htonl(INADDR_ANY); - serv_addr.sin_port = htons(port); + serv_addr.sin_port = htons(p_port); /* * Attempt to allow rebinding to the port... */ opt = 1; - setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, (char *) &opt, sizeof opt); + ret = setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, (char *) &opt, + sizeof opt); + if (ret == -1) + warn("%s: SO_REUSEADDR", __func__); opt = 1; - setsockopt(sockfd, SOL_SOCKET, SO_KEEPALIVE, (char *) &opt, sizeof opt); + ret = setsockopt(sockfd, SOL_SOCKET, SO_KEEPALIVE, (char *) &opt, + sizeof opt); + if (ret == -1) + warn("%s: SO_KEEPALIVE", __func__); lingeropt.l_onoff = 0; lingeropt.l_linger = 0; - setsockopt(sockfd, SOL_SOCKET, SO_LINGER, (char *) &lingeropt, + ret = setsockopt(sockfd, SOL_SOCKET, SO_LINGER, (char *) &lingeropt, sizeof(lingeropt)); + if (ret == -1) + warn("%s: SO_LINGER", __func__); if (bind(sockfd, (struct sockaddr *) &serv_addr, sizeof serv_addr) < 0) { fprintf(stderr, "FICS: can't bind local address. errno=%d\n", @@ -553,15 +566,23 @@ net_close_connection(int fd) PUBLIC void turn_echo_on(int fd) { - static unsigned char wont_echo[] = { IAC, WONT, TELOPT_ECHO, '\0' }; - send(fd, (char *) wont_echo, strlen((char *) wont_echo), 0); + int ret; + static unsigned char wont_echo[] = {IAC, WONT, TELOPT_ECHO, '\0'}; + + ret = send(fd, (char *)wont_echo, sizeof wont_echo - 1, 0); + if (ret == -1) + warn("%s: cannot send", __func__); } PUBLIC void turn_echo_off(int fd) { - static unsigned char will_echo[] = { IAC, WILL, TELOPT_ECHO, '\0' }; - send(fd, (char *) will_echo, strlen((char *) will_echo), 0); + int ret; + static unsigned char will_echo[] = {IAC, WILL, TELOPT_ECHO, '\0'}; + + ret = send(fd, (char *)will_echo, sizeof will_echo - 1, 0); + if (ret == -1) + warn("%s: cannot send", __func__); } PUBLIC unsigned int diff --git a/FICS/obsproc.c b/FICS/obsproc.c index 4dd7b2d..2353c60 100644 --- a/FICS/obsproc.c +++ b/FICS/obsproc.c @@ -28,6 +28,11 @@ Markus Uhlin 24/04/29 Fixed compiler warnings Markus Uhlin 24/07/07 Fixed unhandled return values of fscanf(). + Markus Uhlin 24/12/02 Improved old_mail_moves() + Markus Uhlin 25/01/18 Fixed -Wshadow + Markus Uhlin 25/03/15 Fixed possible buffer overflow + in FindHistory2(). + Markus Uhlin 25/04/06 Fixed Clang Tidy warnings. */ #include "stdinclude.h" @@ -86,19 +91,22 @@ GameNumFromParam(int p, int *p1, parameter *param) PRIVATE int gamesortfunc(const void *i, const void *j) { + const int x = *(int *)i; + const int y = *(int *)j; + /* * examine mode games moved to top of "games" output */ - return (GetRating(&parray[garray[*(int *)i].white], - garray[*(int *)i].type) + - GetRating(&parray[garray[*(int *)i].black], - garray[*(int *)i].type) - - (garray[*(int *)i].status == GAME_EXAMINE ? 10000 : 0) - - GetRating(&parray[garray[*(int *)j].white], - garray[*(int *)j].type) - - GetRating(&parray[garray[*(int *)j].black], - garray[*(int *)j].type) + - (garray[*(int *)j].status == GAME_EXAMINE ? 10000 : 0)); + return (GetRating(&parray[garray[x].white], + garray[x].type) + + GetRating(&parray[garray[x].black], + garray[x].type) - + (garray[x].status == GAME_EXAMINE ? 10000 : 0) - + GetRating(&parray[garray[y].white], + garray[y].type) - + GetRating(&parray[garray[y].black], + garray[y].type) + + (garray[y].status == GAME_EXAMINE ? 10000 : 0)); } PUBLIC int @@ -145,6 +153,9 @@ com_games(int p, param_list param) wp = garray[i].white; bp = garray[i].black; + UNUSED_VAR(wp); + UNUSED_VAR(bp); + if ((!selected) && s && strncasecmp(s, garray[i].white_name, slen) && @@ -373,9 +384,8 @@ com_allobservers(int p, param_list param) start = 0; end = g_num; } else if ((obgame >= g_num) || - ((obgame < g_num) && - ((garray[obgame].status != GAME_ACTIVE) && - (garray[obgame].status != GAME_EXAMINE)))) { + (garray[obgame].status != GAME_ACTIVE && + garray[obgame].status != GAME_EXAMINE)) { pprintf(p, "There is no such game.\n"); return COM_OK; } else { @@ -672,11 +682,11 @@ com_mailmoves(int p, param_list param) PRIVATE int old_mail_moves(int p, int mail, param_list param) { - FILE *fp; + FILE *fp = NULL; char fname[MAX_FILENAME_SIZE] = { '\0' }; char tmp[2048] = { '\0' }; char *ptmp = tmp; - int count; + int count = 0; int p1, connected; if (mail && (!parray[p].registered)) { @@ -705,10 +715,16 @@ old_mail_moves(int p, int mail, param_list param) return COM_OK; } - while (!feof(fp)) - fgets(tmp, 1024, fp); + while (fgets(tmp, sizeof tmp, fp) != NULL) { + /* null */; + } + + if (sscanf(ptmp, "%d", &count) != 1) { + warnx("%s: sscanf() error", __func__); + fclose(fp); + return COM_FAILED; + } - sscanf(ptmp, "%d", &count); fclose(fp); pprintf(p, "Last game for %s was history game %d.\n", parray[p1].name, count); @@ -947,7 +963,7 @@ ExamineAdjourned(int p, int p1, int p2) } PRIVATE char * -FindHistory(int p, int p1, int game) +FindHistory(int p, int p1, int p_game) { FILE *fpHist; int index = 0; @@ -967,12 +983,17 @@ FindHistory(int p, int p1, int game) ret = fscanf(fpHist, "%d %*c %*d %*c %*d %*s %*s %*d %*d %*d " "%*d %*s %*s %ld", &index, &when); - if (ret != 2) - warn("%s: %s: corrupt", __func__, &fileName[0]); - } while (!feof(fpHist) && index != game); + if (ret != 2) { + warnx("%s: %s: corrupt", __func__, fileName); + fclose(fpHist); + return NULL; + } + } while (!feof(fpHist) && + !ferror(fpHist) && + index != p_game); - if (feof(fpHist)) { - pprintf(p, "There is no history game %d for %s.\n", game, + if (feof(fpHist) || ferror(fpHist)) { + pprintf(p, "There is no history game %d for %s.\n", p_game, parray[p1].name); fclose(fpHist); return NULL; @@ -986,9 +1007,10 @@ FindHistory(int p, int p1, int game) } PRIVATE char * -FindHistory2(int p, int p1, int game, char *End) +FindHistory2(int p, int p1, int p_game, char *End, const size_t End_size) { FILE *fpHist; + char fmt[80] = { '\0' }; int index = 0; long int when = 0; static char fileName[MAX_FILENAME_SIZE]; @@ -1001,17 +1023,21 @@ FindHistory2(int p, int p1, int game, char *End) return NULL; } - do { - int ret; + msnprintf(fmt, sizeof fmt, "%%d %%*c %%*d %%*c %%*d %%*s %%*s %%*d " + "%%*d %%*d %%*d %%*s %%%zus %%ld\n", (End_size - 1)); - ret = fscanf(fpHist, "%d %*c %*d %*c %*d %*s %*s %*d %*d %*d " - "%*d %*s %s %ld", &index, End, &when); - if (ret != 3) - warn("%s: %s: corrupt", __func__, &fileName[0]); - } while (!feof(fpHist) && index != game); + do { + if (fscanf(fpHist, fmt, &index, End, &when) != 3) { + warnx("%s: %s: corrupt", __func__, fileName); + fclose(fpHist); + return NULL; + } + } while (!feof(fpHist) && + !ferror(fpHist) && + index != p_game); - if (feof(fpHist)) { - pprintf(p, "There is no history game %d for %s.\n", game, + if (feof(fpHist) || ferror(fpHist)) { + pprintf(p, "There is no history game %d for %s.\n", p_game, parray[p1].name); fclose(fpHist); return NULL; @@ -1025,15 +1051,15 @@ FindHistory2(int p, int p1, int game, char *End) } PRIVATE void -ExamineHistory(int p, int p1, int game) +ExamineHistory(int p, int p1, int p_game) { FILE *fpGame; char *fileName; - if ((fileName = FindHistory(p, p1, game)) != NULL) { + if ((fileName = FindHistory(p, p1, p_game)) != NULL) { if ((fpGame = fopen(fileName, "r")) == NULL) { pprintf(p, "History game %d not available for %s.\n", - game, parray[p1].name); + p_game, parray[p1].name); } else { ExamineStored(fpGame, p, fileName); fclose(fpGame); @@ -1744,21 +1770,13 @@ com_journal(int p, param_list param) PRIVATE void jsave_journalentry(int p, char save_spot, int p1, char from_spot, char *to_file) { - FILE *Game; - char *name_from = parray[p1].login; - char *name_to = parray[p].login; - char BlackName[MAX_LOGIN_NAME + 1]; - char WhiteName[MAX_LOGIN_NAME + 1]; - char command[MAX_FILENAME_SIZE * 2 + 3]; - char eco[100]; - char ending[100]; - char fname[MAX_FILENAME_SIZE]; - char fname2[MAX_FILENAME_SIZE]; - char result[100]; - char type[100]; - int BlackRating; - int WhiteRating; - int i, t; + FILE *Game; + char *name_from = parray[p1].login; + char *name_to = parray[p].login; + char command[MAX_FILENAME_SIZE * 2 + 3]; + char fname[MAX_FILENAME_SIZE]; + char fname2[MAX_FILENAME_SIZE]; + struct JGI_context ctx; msnprintf(fname, sizeof fname, "%s/%c/%s.%c", journal_dir, name_from[0], name_from, from_spot); @@ -1788,14 +1806,34 @@ jsave_journalentry(int p, char save_spot, int p1, char from_spot, char *to_file) msnprintf(fname, sizeof fname, "%s/player_data/%c/%s.%s", stats_dir, name_to[0], name_to, STATS_JOURNAL); - if (!journal_get_info(p, from_spot, WhiteName, &WhiteRating, - BlackName, &BlackRating, type, &t, &i, eco, ending, result, fname)) + /* + * Init context + */ + ctx.p = p; + ctx.from_spot = from_spot; + ctx.WhiteRating = 0; + ctx.BlackRating = 0; + ctx.t = 0; + ctx.i = 0; + memset(ctx.WhiteName, 0, sizeof(ctx.WhiteName)); + memset(ctx.BlackName, 0, sizeof(ctx.BlackName)); + memset(ctx.type, 0, sizeof(ctx.type)); + memset(ctx.eco, 0, sizeof(ctx.eco)); + memset(ctx.ending, 0, sizeof(ctx.ending)); + memset(ctx.result, 0, sizeof(ctx.result)); + + if (!journal_get_info(&ctx, fname)) return; addjournalitem(p, toupper(save_spot), - WhiteName, WhiteRating, - BlackName, BlackRating, - type, t, i, eco, ending, result, to_file); + ctx.WhiteName, ctx.WhiteRating, + ctx.BlackName, ctx.BlackRating, + ctx.type, + ctx.t, ctx.i, + ctx.eco, + ctx.ending, + ctx.result, + to_file); pprintf(p, "Journal entry %s %c saved in slot %c in journal.\n", parray[p1].name, toupper(from_spot), toupper(save_spot)); } @@ -1807,14 +1845,15 @@ jsave_history(int p, char save_spot, int p1, int from, char *to_file) char *EndSymbol; char *HistoryFname; char *name_to = parray[p].login; - char End[100]; - char command[MAX_FILENAME_SIZE * 2 + 3]; + char End[100] = { '\0' }; + char command[MAX_FILENAME_SIZE * 2 + 3] = { '\0' }; char filename[MAX_FILENAME_SIZE + 1] = { '\0' }; // XXX - char jfname[MAX_FILENAME_SIZE]; + char jfname[MAX_FILENAME_SIZE] = { '\0' }; char type[4]; int g; - if ((HistoryFname = FindHistory2(p, p1, from, End)) != NULL) { + if ((HistoryFname = FindHistory2(p, p1, from, End, sizeof End)) != + NULL) { if ((Game = fopen(HistoryFname, "r")) == NULL) { pprintf(p, "History game %d not available for %s.\n", from, diff --git a/FICS/playerdb.c b/FICS/playerdb.c index ab5f3eb..c85ec0c 100644 --- a/FICS/playerdb.c +++ b/FICS/playerdb.c @@ -32,12 +32,27 @@ Markus Uhlin 24/08/04 Fixed multiple possible buffer overflows. Markus Uhlin 24/08/13 Handled function return values + Markus Uhlin 24/11/24 Fixed incorrect format strings + Markus Uhlin 24/12/02 Made many improvements + Markus Uhlin 24/12/04 Added player number checks + Markus Uhlin 25/02/11 Calc string length once + Markus Uhlin 25/03/22 Fixed overflowed return value in + player_search(). + Markus Uhlin 25/03/23 Fixed overflowed array index + read/write. + Markus Uhlin 25/03/29 player_remove_request: + fixed overflowed array index + read/write. + Markus Uhlin 25/04/02 add_to_list: added an upper + limit for the list size. + Markus Uhlin 25/04/06 Fixed Clang Tidy warnings. */ #include "stdinclude.h" #include "common.h" #include <err.h> +#include <errno.h> #include <stdint.h> #include "command.h" @@ -61,6 +76,31 @@ PUBLIC player parray[PARRAY_SIZE]; PUBLIC int p_num = 0; +/* + * Checks if a player number is within bounds. + */ +PUBLIC bool +player_num_ok_chk(const int num) +{ + return (num >= 0 && num <= p_num && + num < (int)ARRAY_SIZE(parray)); +} + +PUBLIC void +xrename(const char *fn, const char *name1, const char *name2) +{ + if (fn == NULL || name1 == NULL || name2 == NULL) { + errno = EINVAL; + warn("%s", __func__); + return; + } + + errno = 0; + + if (rename(name1, name2) != 0) + warn("%s: '%s' -> '%s'", fn, name1, name2); +} + PRIVATE int get_empty_slot(void) { @@ -204,6 +244,7 @@ player_zero(int p) parray[p].rated = 0; parray[p].registered = 0; parray[p].ropen = 1; + parray[p].seek = 0; parray[p].simul_info.numBoards = 0; parray[p].socket = -1; parray[p].sopen = 0; @@ -314,6 +355,11 @@ player_remove(int p) { int i; + if (!player_num_ok_chk(p)) { + warnx("%s: invalid player number %d", __func__, p); + return -1; + } + player_decline_offers(p, -1, -1); player_withdraw_offers(p, -1, -1); @@ -366,10 +412,14 @@ add_to_list(FILE *fp, enum ListWhich lw, int *size, int p) #define SCAN_STR "%1023s" - if (*size <= 0) + if (*size <= 0 || *size > MAX_GLOBAL_LIST_SIZE) { +// warnx("%s: illegal list size (%d)", __func__, *size); return -2; + } + while ((*size)-- > 0 && fscanf(fp, SCAN_STR, buf) == 1) list_add(p, lw, buf); + return (*size <= 0 ? 0 : -1); } @@ -392,7 +442,7 @@ ReadV1PlayerFmt(int p, player *pp, FILE *fp, char *file, int version) /* * Name */ - if (fgets(tmp2, sizeof tmp2, fp) != NULL && + if (fgets(tmp2, sizeof tmp2, fp) != NULL && // NOLINT strcmp(tmp2, "NONE\n") != 0) { tmp2[strcspn(tmp2, "\n")] = '\0'; pp->name = xstrdup(tmp2); @@ -403,7 +453,7 @@ ReadV1PlayerFmt(int p, player *pp, FILE *fp, char *file, int version) /* * Full name */ - if (fgets(tmp2, sizeof tmp2, fp) != NULL && + if (fgets(tmp2, sizeof tmp2, fp) != NULL && // NOLINT strcmp(tmp2, "NONE\n") != 0) { tmp2[strcspn(tmp2, "\n")] = '\0'; pp->fullName = xstrdup(tmp2); @@ -414,7 +464,7 @@ ReadV1PlayerFmt(int p, player *pp, FILE *fp, char *file, int version) /* * Password */ - if (fgets(tmp2, sizeof tmp2, fp) != NULL && + if (fgets(tmp2, sizeof tmp2, fp) != NULL && // NOLINT strcmp(tmp2, "NONE\n") != 0) { tmp2[strcspn(tmp2, "\n")] = '\0'; pp->passwd = xstrdup(tmp2); @@ -425,7 +475,7 @@ ReadV1PlayerFmt(int p, player *pp, FILE *fp, char *file, int version) /* * Email */ - if (fgets(tmp2, sizeof tmp2, fp) != NULL && + if (fgets(tmp2, sizeof tmp2, fp) != NULL && // NOLINT strcmp(tmp2, "NONE\n") != 0) { tmp2[strcspn(tmp2, "\n")] = '\0'; pp->emailAddress = xstrdup(tmp2); @@ -433,9 +483,11 @@ ReadV1PlayerFmt(int p, player *pp, FILE *fp, char *file, int version) pp->emailAddress = NULL; } - if (fscanf(fp, "%u %u %u %u %u %u %jd %u %jd %u %u %u %u %u %u %jd %u %jd " - "%u %u %u %u %u %u %jd %u %jd %u %u %u %u %u %u %jd %u %jd %u %u %u %u " - "%u %u %jd %u %jd %d\n", + if (feof(fp) || + ferror(fp) || + fscanf(fp, "%d %d %d %d %d %d %jd %d %jd %d %d %d %d %d %d %jd %d %jd " + "%d %d %d %d %d %d %jd %d %jd %d %d %d %d %d %d %jd %d %jd %d %d %d %d " + "%d %d %jd %d %jd %u\n", &pp->s_stats.num, &pp->s_stats.win, &pp->s_stats.los, &pp->s_stats.dra, &pp->s_stats.rating, &ss, <ime_tmp[0], &pp->s_stats.best, &wb_tmp[0], @@ -535,9 +587,29 @@ ReadV1PlayerFmt(int p, player *pp, FILE *fp, char *file, int version) pp->timeOfReg = array[0]; pp->totalTime = array[1]; + if (pp->num_plan >= MAX_PLAN) { + warnx("Player %s is corrupt\nToo many plans (%d)", + parray[p].name, + pp->num_plan); + return; + } else if (pp->num_formula >= MAX_FORMULA) { + warnx("Player %s is corrupt\nToo many formulas (%d)", + parray[p].name, + pp->num_formula); + return; + } else if (pp->numAlias >= MAX_ALIASES) { + warnx("Player %s is corrupt\nToo many aliases (%d)", + parray[p].name, + pp->numAlias); + return; + } + if (pp->num_plan > 0) { for (i = 0; i < pp->num_plan; i++) { - fgets(tmp2, sizeof tmp2, fp); + if (fgets(tmp2, sizeof tmp2, fp) == NULL) { + warnx("%s: bad plan: feof %s", __func__, file); + return; + } if (!(len = strlen(tmp2))) { fprintf(stderr, "FICS: Error bad plan in " @@ -545,7 +617,8 @@ ReadV1PlayerFmt(int p, player *pp, FILE *fp, char *file, int version) i--; pp->num_plan--; } else { - tmp2[len - 1] = '\0'; // Get rid of '\n'. + // Get rid of '\n'. + tmp2[strcspn(tmp2, "\n")] = '\0'; pp->planLines[i] = (len > 1 ? xstrdup(tmp2) : NULL); @@ -555,7 +628,11 @@ ReadV1PlayerFmt(int p, player *pp, FILE *fp, char *file, int version) if (pp->num_formula > 0) { for (i = 0; i < pp->num_formula; i++) { - fgets(tmp2, sizeof tmp2, fp); + if (fgets(tmp2, sizeof tmp2, fp) == NULL) { + warnx("%s: bad formula: feof %s", __func__, + file); + return; + } if (!(len = strlen(tmp2))) { fprintf(stderr, "FICS: Error bad formula in " @@ -563,7 +640,8 @@ ReadV1PlayerFmt(int p, player *pp, FILE *fp, char *file, int version) i--; pp->num_formula--; } else { - tmp2[len - 1] = '\0'; // Get rid of '\n'. + // Get rid of '\n'. + tmp2[strcspn(tmp2, "\n")] = '\0'; pp->formulaLines[i] = (len > 1 ? xstrdup(tmp2) : NULL); @@ -571,8 +649,12 @@ ReadV1PlayerFmt(int p, player *pp, FILE *fp, char *file, int version) } } - fgets(tmp2, sizeof tmp2, fp); - tmp2[strlen(tmp2) - 1] = '\0'; + if (fgets(tmp2, sizeof tmp2, fp) == NULL) { + warnx("%s: fgets() error", __func__); + return; + } + + tmp2[strcspn(tmp2, "\n")] = '\0'; if (!strcmp(tmp2, "NONE")) pp->formula = NULL; @@ -581,16 +663,18 @@ ReadV1PlayerFmt(int p, player *pp, FILE *fp, char *file, int version) if (pp->numAlias > 0) { for (i = 0; i < pp->numAlias; i++) { - fgets(tmp2, sizeof tmp2, fp); + if (fgets(tmp2, sizeof tmp2, fp) == NULL) { + warnx("%s: bad alias: feof %s", __func__, file); + return; + } - if (!(len = strlen(tmp2))) { + if (!strlen(tmp2)) { // XXX fprintf(stderr, "FICS: Error bad alias in " "file %s\n", file); i--; pp->numAlias--; } else { - tmp2[len - 1] = '\0'; // Get rid of '\n'. - tmp = tmp2; + tmp2[strcspn(tmp2, "\n")] = '\0'; tmp = eatword(tmp2); *tmp = '\0'; tmp++; @@ -775,11 +859,18 @@ got_attr_value_player(int p, char *attr, char *value, FILE *fp, char *file) * num_plan */ - parray[p].num_plan = atoi(value); - - if (parray[p].num_plan > 0) { + if ((parray[p].num_plan = atoi(value)) >= MAX_PLAN) { + warnx("%s: %s: too many plans (%d)", __func__, file, + parray[p].num_plan); + return -1; + } else if (parray[p].num_plan > 0) { for (i = 0; i < parray[p].num_plan; i++) { - fgets(tmp, sizeof tmp, fp); + + if (fgets(tmp, sizeof tmp, fp) == NULL) { + warnx("%s: bad plan: feof %s", + __func__, file); + return -1; + } if (!(len = strlen(tmp))) { fprintf(stderr, "FICS: Error bad plan " @@ -787,22 +878,32 @@ got_attr_value_player(int p, char *attr, char *value, FILE *fp, char *file) i--; parray[p].num_plan--; } else { - tmp[len - 1] = '\0'; // Get rid of '\n'. + // Get rid of '\n'. + tmp[strcspn(tmp, "\n")] = '\0'; + parray[p].planLines[i] = (len > 1 ? xstrdup(tmp) : NULL); } } + } else { + /* null */; } } else if (!strcmp(attr, "num_formula:")) { /* * num_formula */ - parray[p].num_formula = atoi(value); - - if (parray[p].num_formula > 0) { + if ((parray[p].num_formula = atoi(value)) >= MAX_FORMULA) { + warnx("%s: %s: too many formulas (%d)", __func__, file, + parray[p].num_formula); + return -1; + } else if (parray[p].num_formula > 0) { for (i = 0; i < parray[p].num_formula; i++) { - fgets(tmp, sizeof tmp, fp); + if (fgets(tmp, sizeof tmp, fp) == NULL) { + warnx("%s: bad formula: feof %s", + __func__, file); + return -1; + } if (!(len = strlen(tmp))) { fprintf(stderr, "FICS: Error bad " @@ -810,11 +911,15 @@ got_attr_value_player(int p, char *attr, char *value, FILE *fp, char *file) i--; parray[p].num_formula--; } else { - tmp[len - 1] = '\0'; // Get rid of '\n'. + // Get rid of '\n'. + tmp[strcspn(tmp, "\n")] = '\0'; + parray[p].formulaLines[i] = (len > 1 ? xstrdup(tmp) : NULL); } } + } else { + /* null */; } } else if (!strcmp(attr, "formula:")) { /* @@ -827,19 +932,25 @@ got_attr_value_player(int p, char *attr, char *value, FILE *fp, char *file) * num_alias */ - parray[p].numAlias = atoi(value); - - if (parray[p].numAlias > 0) { + if ((parray[p].numAlias = atoi(value)) >= MAX_ALIASES) { + warnx("%s: %s: too many aliases (%d)", __func__, file, + parray[p].numAlias); + return -1; + } else if (parray[p].numAlias > 0) { for (i = 0; i < parray[p].numAlias; i++) { - fgets(tmp, sizeof tmp, fp); + if (fgets(tmp, sizeof tmp, fp) == NULL) { + warnx("%s: bad alias: feof %s", + __func__, file); + return -1; + } - if (!(len = strlen(tmp))) { + if (!strlen(tmp)) { // XXX fprintf(stderr, "FICS: Error bad alias " "in file %s\n", file); i--; parray[p].numAlias--; } else { - tmp[len - 1] = '\0'; // Get rid of '\n'. + tmp[strcspn(tmp, "\n")] = '\0'; tmp1 = tmp; tmp1 = eatword(tmp1); *tmp1 = '\0'; @@ -852,68 +963,104 @@ got_attr_value_player(int p, char *attr, char *value, FILE *fp, char *file) xstrdup(tmp1); } } + } else { + /* null */; } } else if (!strcmp(attr, "num_censor:")) { /* * num_censor */ - i = atoi(value); + if ((i = atoi(value)) < 0) { + warnx("%s: num censor negative", __func__); + return -1; + } else if (i > MAX_CENSOR) { + warnx("%s: num censor too large", __func__); + return -1; + } while (i--) { - fgets(tmp, sizeof tmp, fp); + if (fgets(tmp, sizeof tmp, fp) == NULL) { + warnx("%s: bad censor: feof %s", + __func__, file); + return -1; + } if (!(len = strlen(tmp)) || len == 1) { // blank lines // do occur! fprintf(stderr, "FICS: Error bad censor in " "file %s\n", file); } else { - tmp[len - 1] = '\0'; // Get rid of '\n'. + tmp[strcspn(tmp, "\n")] = '\0'; list_add(p, L_CENSOR, tmp); } } } else if (!strcmp(attr, "num_notify:")) { - i = atoi(value); + if ((i = atoi(value)) < 0) { + warnx("%s: num notify negative", __func__); + return -1; + } else if (i > MAX_NOTIFY) { + warnx("%s: num notify too large", __func__); + return -1; + } while (i--) { - fgets(tmp, sizeof tmp, fp); + if (fgets(tmp, sizeof tmp, fp) == NULL) { + warnx("%s: bad notify: feof %s", + __func__, file); + return -1; + } if (!(len = strlen(tmp)) || len == 1) { // blank lines // do occur! fprintf(stderr, "FICS: Error bad notify in " "file %s\n", file); } else { - tmp[len - 1] = '\0'; // Get rid of '\n'. + tmp[strcspn(tmp, "\n")] = '\0'; list_add(p, L_NOTIFY, tmp); } } } else if (!strcmp(attr, "num_noplay:")) { - i = atoi(value); + if ((i = atoi(value)) < 0) { + warnx("%s: num noplay negative", __func__); + return -1; + } while (i--) { - fgets(tmp, sizeof tmp, fp); + if (fgets(tmp, sizeof tmp, fp) == NULL) { + warnx("%s: bad noplay: feof %s", + __func__, file); + return -1; + } if (!(len = strlen(tmp)) || len == 1) { // blank lines // do occur! fprintf(stderr, "FICS: Error bad noplay in " "file %s\n", file); } else { - tmp[len - 1] = '\0'; // Get rid of '\n'. + tmp[strcspn(tmp, "\n")] = '\0'; list_add(p, L_NOPLAY, tmp); } } } else if (!strcmp(attr, "num_gnotify:")) { - i = atoi(value); + if ((i = atoi(value)) < 0) { + warnx("%s: num gnotify negative", __func__); + return -1; + } while (i--) { - fgets(tmp, sizeof tmp, fp); + if (fgets(tmp, sizeof tmp, fp) == NULL) { + warnx("%s: bad gnotify: feof %s", + __func__, file); + return -1; + } if (!(len = strlen(tmp)) || len == 1) { // blank lines // do occur! fprintf(stderr, "FICS: Error bad gnotify in " "file %s\n", file); } else { - tmp[len - 1] = '\0'; // Get rid of '\n'. + tmp[strcspn(tmp, "\n")] = '\0'; list_add(p, L_GNOTIFY, tmp); } } @@ -928,11 +1075,11 @@ got_attr_value_player(int p, char *attr, char *value, FILE *fp, char *file) PUBLIC int player_read(int p, char *name) { - FILE *fp; + FILE *fp = NULL; char *attr, *value; char fname[MAX_FILENAME_SIZE] = { '\0' }; char line[MAX_LINE_SIZE] = { '\0' }; - int len; + int len = 0; int version = 0; parray[p].login = stolower(xstrdup(name)); @@ -948,10 +1095,14 @@ player_read(int p, char *name) parray[p].registered = 1; // Lets load the file - fgets(line, MAX_LINE_SIZE, fp); // Ok, so which version file? + if (fgets(line, sizeof line, fp) == NULL) { // Ok, so which version + warnx("%s: fgets() error", __func__); // file? + fclose(fp); + return -1; + } if (line[0] == 'v') - sscanf(line, "%*c %d", &version); + (void)sscanf(line, "%*c %d", &version); if (version > 0) // Quick method: ReadV1PlayerFmt(p, &parray[p], fp, fname, version); else { // Do it the old SLOW way @@ -980,7 +1131,9 @@ player_read(int p, char *name) value = eatwhite(value); stolower(attr); got_attr_value_player(p, attr, value, fp, fname); - fgets(line, MAX_LINE_SIZE, fp); + + if (fgets(line, sizeof line, fp) == NULL) + break; } while (!feof(fp)); } @@ -1029,7 +1182,7 @@ player_markdeleted(int p) parray[p].login[0], parray[p].login); snprintf(fname2, sizeof fname2, "%s/%c/%s.delete", player_dir, parray[p].login[0], parray[p].login); - rename(fname, fname2); + xrename(__func__, fname, fname2); if ((fp = fopen(fname2, "a")) != NULL) { // Touch the file fprintf(fp, "\n"); @@ -1052,9 +1205,9 @@ WritePlayerFile(FILE *fp, int p) fprintf(fp, "%s\n", (pp->passwd ? pp->passwd : "NONE")); fprintf(fp, "%s\n", (pp->emailAddress ? pp->emailAddress : "NONE")); - fprintf(fp, "%u %u %u %u %u %u %jd %u %jd %u %u %u %u %u %u %jd %u %jd %u " - "%u %u %u %u %u %jd %u %jd %u %u %u %u %u %u %jd %u %jd %u %u %u %u %u " - "%u %jd %u %jd %d\n", + fprintf(fp, "%d %d %d %d %d %d %jd %d %jd %d %d %d %d %d %d %jd %d %jd %d " + "%d %d %d %d %d %jd %d %jd %d %d %d %d %d %d %jd %d %jd %d %d %d %d %d " + "%d %jd %d %jd %u\n", pp->s_stats.num, pp->s_stats.win, pp->s_stats.los, pp->s_stats.dra, pp->s_stats.rating, (int)(pp->s_stats.sterr * 10.0), @@ -1138,6 +1291,11 @@ player_save(int p) FILE *fp; char fname[MAX_FILENAME_SIZE]; + if (!player_num_ok_chk(p)) { + warnx("%s: invalid player number %d", __func__, p); + return -1; + } + if (!parray[p].registered) // Player must not be registered return -1; @@ -1203,10 +1361,13 @@ player_find_part_login(char *name) { int found = -1; int i; + size_t namelen; if ((i = player_find_bylogin(name)) >= 0) return i; + namelen = strlen(name); + for (i = 0; i < p_num; i++) { if (parray[i].status == PLAYER_EMPTY || parray[i].status == PLAYER_LOGIN || @@ -1216,7 +1377,7 @@ player_find_part_login(char *name) if (!parray[i].login) continue; - if (!strncmp(parray[i].login, name, strlen(name))) { + if (!strncmp(parray[i].login, name, namelen)) { if (found >= 0) /* Ambiguous */ return -2; found = i; @@ -1687,17 +1848,26 @@ player_new_pendto(int p) PUBLIC int player_remove_pendto(int p, int p1, int type) { - int w; + bool removed = false; + int w; if ((w = player_find_pendto(p, p1, type)) < 0) return -1; - for (; w < (parray[p].num_to - 1); w++) + for (; w < (parray[p].num_to - 1); w++) { + if (w + 1 >= (int)ARRAY_SIZE(parray[0].p_to_list)) { + warnx("%s: overflowed array index write", __func__); + break; + } + parray[p].p_to_list[w] = parray[p].p_to_list[w + 1]; + removed = true; + } - parray[p].num_to = (parray[p].num_to - 1); + UNUSED_VAR(removed); + parray[p].num_to -= 1; - return 0; + return (0); } PUBLIC int @@ -1741,17 +1911,26 @@ player_new_pendfrom(int p) PUBLIC int player_remove_pendfrom(int p, int p1, int type) { - int w; + bool removed = false; + int w; if ((w = player_find_pendfrom(p, p1, type)) < 0) return -1; - for (; w < (parray[p].num_from - 1); w++) + for (; w < (parray[p].num_from - 1); w++) { + if (w + 1 >= (int)ARRAY_SIZE(parray[0].p_from_list)) { + warnx("%s: overflowed array index write", __func__); + break; + } + parray[p].p_from_list[w] = parray[p].p_from_list[w + 1]; + removed = true; + } - parray[p].num_from = (parray[p].num_from - 1); + UNUSED_VAR(removed); + parray[p].num_from -= 1; - return 0; + return (0); } PUBLIC int @@ -1786,28 +1965,44 @@ player_add_request(int p, int p1, int type, int param) PUBLIC int player_remove_request(int p, int p1, int type) { - int to = 0, from = 0; + bool removed; + int to = 0, from = 0; - while (to != -1) { - if ((to = player_find_pendto(p, p1, type)) != -1) { - for (; to < parray[p].num_to - 1; to++) { - parray[p].p_to_list[to] = - parray[p].p_to_list[to + 1]; + while (to != -1 && (to = player_find_pendto(p, p1, type)) != -1) { + removed = false; + + for (; to < parray[p].num_to - 1; to++) { + if (to + 1 >= (int)ARRAY_SIZE(parray[0].p_to_list)) { + warnx("%s: overflowed array index read/write", + __func__); + break; } - parray[p].num_to = (parray[p].num_to - 1); + parray[p].p_to_list[to] = parray[p].p_to_list[to + 1]; + removed = true; } + + UNUSED_VAR(removed); + parray[p].num_to -= 1; } - while (from != -1) { - if ((from = player_find_pendfrom(p1, p, type)) != -1) { - for (; from < parray[p1].num_from - 1; from++) { - parray[p1].p_from_list[from] = - parray[p1].p_from_list[from + 1]; + while (from != -1 && (from = player_find_pendfrom(p1, p, type)) != -1) { + removed = false; + + for (; from < parray[p1].num_from - 1; from++) { + if (from + 1 >= (int)ARRAY_SIZE(parray[0].p_from_list)) { + warnx("%s: overflowed array index read/write", + __func__); + break; } - parray[p1].num_from = (parray[p1].num_from - 1); + parray[p1].p_from_list[from] = + parray[p1].p_from_list[from + 1]; + removed = true; } + + UNUSED_VAR(removed); + parray[p1].num_from -= 1; } if ((type == PEND_ALL || type == PEND_MATCH) && parray[p].partner >= 0) @@ -2175,7 +2370,6 @@ player_goto_next_board(int p) on = parray[p].simul_info.onBoard; start = on; - g = -1; do { on++; @@ -2204,7 +2398,6 @@ player_goto_prev_board(int p) on = parray[p].simul_info.onBoard; start = on; - g = -1; do { --on; @@ -2413,7 +2606,7 @@ player_add_message(int top, int fromp, char *message) pprintf(fromp, "(and emailed) "); } - pprintf(fromp, "to %s: \n %s\n", parray[top].name, message); + pprintf(fromp, "to %s:\n %s\n", parray[top].name, message); return 0; } @@ -2495,12 +2688,7 @@ LoadMsgs(int p, int which, textlist **Head) if ((fp = fopen(fName, "r")) == NULL) return -1; - while (!feof(fp)) { - fgets(line, sizeof line, fp); - - if (feof(fp)) - break; - + while (fgets(line, sizeof line, fp) != NULL) { if (SaveThisMsg(which, line)) { SaveTextListEntry(Cur, line, ++n); Cur = &(*Cur)->next; @@ -2536,9 +2724,7 @@ LoadMsgRange(int p, int start, int end, textlist **Head) } for (n = 1; n <= end || end <= 0; n++) { - fgets(line, sizeof line, fp); - - if (feof(fp)) + if (fgets(line, sizeof line, fp) == NULL) break; if ((start < 0 && (n < -start || n > -end)) || (start >= 0 && n >= start)) { @@ -2650,7 +2836,7 @@ player_show_messages(int p) PUBLIC int ShowMsgsBySender(int p, param_list param) { - int nFrom, nTo; + int nFrom = -1, nTo = -1; int p1, connected; textlist *Head; @@ -2663,8 +2849,6 @@ ShowMsgsBySender(int p, param_list param) return -1; /* no need to disconnect */ } - nFrom = nTo = -1; - if (p != p1) { if ((nTo = LoadMsgs(p1, p + 1, &Head)) <= 0) { pprintf(p, "%s has no messages from you.\n", @@ -2753,8 +2937,11 @@ player_search(int p, char *name) int p1, count; // Exact match with connected player? - if ((p1 = player_find_bylogin(name)) >= 0) + if ((p1 = player_find_bylogin(name)) >= 0) { + if (p1 + 1 >= (int)ARRAY_SIZE(parray)) + return 0; return (p1 + 1); + } // Exact match with registered player? snprintf(pdir, sizeof pdir, "%s/%c", player_dir, name[0]); @@ -2810,7 +2997,7 @@ player_kill(char *name) name); snprintf(fname2, sizeof fname2, "%s/%c/.rem.%s", player_dir, name[0], name); - rename(fname, fname2); + xrename(__func__, fname, fname2); RemHist(name); @@ -2818,25 +3005,25 @@ player_kill(char *name) stats_dir, name[0], name); snprintf(fname2, sizeof fname2, "%s/player_data/%c/.rem.%s.games", stats_dir, name[0], name); - rename(fname, fname2); + xrename(__func__, fname, fname2); snprintf(fname, sizeof fname, "%s/player_data/%c/%s.comments", stats_dir, name[0], name); snprintf(fname2, sizeof fname2, "%s/player_data/%c/.rem.%s.comments", stats_dir, name[0], name); - rename(fname, fname2); + xrename(__func__, fname, fname2); snprintf(fname, sizeof fname, "%s/player_data/%c/%s.logons", stats_dir, name[0], name); snprintf(fname2, sizeof fname2, "%s/player_data/%c/.rem.%s.logons", stats_dir, name[0], name); - rename(fname, fname2); + xrename(__func__, fname, fname2); snprintf(fname, sizeof fname, "%s/player_data/%c/%s.messages", stats_dir, name[0], name); snprintf(fname2, sizeof fname2, "%s/player_data/%c/.rem.%s.messages", stats_dir, name[0], name); - rename(fname, fname2); + xrename(__func__, fname, fname2); return 0; } @@ -2851,31 +3038,31 @@ player_rename(char *name, char *newname) name); snprintf(fname2, sizeof fname2, "%s/%c/%s", player_dir, newname[0], newname); - rename(fname, fname2); + xrename(__func__, fname, fname2); snprintf(fname, sizeof fname, "%s/player_data/%c/%s.games", stats_dir, name[0], name); snprintf(fname2, sizeof fname2, "%s/player_data/%c/%s.games", stats_dir, newname[0], newname); - rename(fname, fname2); + xrename(__func__, fname, fname2); snprintf(fname, sizeof fname, "%s/player_data/%c/%s.comments", stats_dir, name[0], name); snprintf(fname2, sizeof fname2, "%s/player_data/%c/%s.comments", stats_dir, newname[0], newname); - rename(fname, fname2); + xrename(__func__, fname, fname2); snprintf(fname, sizeof fname, "%s/player_data/%c/%s.logons", stats_dir, name[0], name); snprintf(fname2, sizeof fname2, "%s/player_data/%c/%s.logons", stats_dir, newname[0], newname); - rename(fname, fname2); + xrename(__func__, fname, fname2); snprintf(fname, sizeof fname, "%s/player_data/%c/%s.messages", stats_dir, name[0], name); snprintf(fname2, sizeof fname2, "%s/player_data/%c/%s.messages", stats_dir, newname[0], newname); - rename(fname, fname2); + xrename(__func__, fname, fname2); return 0; } @@ -2890,31 +3077,31 @@ player_raise(char *name) name[0], name); snprintf(fname2, sizeof fname2, "%s/%c/.rem.%s", player_dir, name[0], name); - rename(fname2, fname); + xrename(__func__, fname2, fname); snprintf(fname, sizeof fname, "%s/player_data/%c/%s.games", stats_dir, name[0], name); snprintf(fname2, sizeof fname2, "%s/player_data/%c/.rem.%s.games", stats_dir, name[0], name); - rename(fname2, fname); + xrename(__func__, fname2, fname); snprintf(fname, sizeof fname, "%s/player_data/%c/%s.comments", stats_dir, name[0], name); snprintf(fname2, sizeof fname2, "%s/player_data/%c/.rem.%s.comments", stats_dir, name[0], name); - rename(fname2, fname); + xrename(__func__, fname2, fname); snprintf(fname, sizeof fname, "%s/player_data/%c/%s.logons", stats_dir, name[0], name); snprintf(fname2, sizeof fname2, "%s/player_data/%c/.rem.%s.logons", stats_dir, name[0], name); - rename(fname2, fname); + xrename(__func__, fname2, fname); snprintf(fname, sizeof fname, "%s/player_data/%c/%s.messages", stats_dir, name[0], name); snprintf(fname2, sizeof fname2, "%s/player_data/%c/.rem.%s.messages", stats_dir, name[0], name); - rename(fname2, fname); + xrename(__func__, fname2, fname); return 0; } @@ -2929,31 +3116,31 @@ player_reincarn(char *name, char *newname) newname[0], newname); snprintf(fname2, sizeof fname2, "%s/%c/.rem.%s", player_dir, name[0], name); - rename(fname2, fname); + xrename(__func__, fname2, fname); snprintf(fname, sizeof fname, "%s/player_data/%c/%s.games", stats_dir, newname[0], newname); snprintf(fname2, sizeof fname2, "%s/player_data/%c/.rem.%s.games", stats_dir, name[0], name); - rename(fname2, fname); + xrename(__func__, fname2, fname); snprintf(fname, sizeof fname, "%s/player_data/%c/%s.comments", stats_dir, newname[0], newname); snprintf(fname2, sizeof fname2, "%s/player_data/%c/.rem.%s.comments", stats_dir, name[0], name); - rename(fname2, fname); + xrename(__func__, fname2, fname); snprintf(fname, sizeof fname, "%s/player_data/%c/%s.logons", stats_dir, newname[0], newname); snprintf(fname2, sizeof fname2, "%s/player_data/%c/.rem.%s.logons", stats_dir, name[0], name); - rename(fname2, fname); + xrename(__func__, fname2, fname); snprintf(fname, sizeof fname, "%s/player_data/%c/%s.messages", stats_dir, newname[0], newname); snprintf(fname2, sizeof fname2, "%s/player_data/%c/.rem.%s.messages", stats_dir, name[0], name); - rename(fname2, fname); + xrename(__func__, fname2, fname); return 0; } @@ -2995,11 +3182,13 @@ player_add_comment(int p_by, int p_to, char *comment) PUBLIC int player_show_comments(int p, int p1) { - char fname[MAX_FILENAME_SIZE]; + char fname[MAX_FILENAME_SIZE] = { '\0' }; snprintf(fname, sizeof fname, "%s/player_data/%c/%s.%s", stats_dir, parray[p1].login[0], parray[p1].login, "comments"); - psend_file(p, NULL, fname); + + if (psend_file(p, NULL, fname) == -1) + warnx("%s: psend_file() error", __func__); return 0; } diff --git a/FICS/playerdb.h b/FICS/playerdb.h index 403533e..1ccae61 100644 --- a/FICS/playerdb.h +++ b/FICS/playerdb.h @@ -29,6 +29,8 @@ #ifndef _PLAYERDB_H #define _PLAYERDB_H +#include <stdbool.h> + #define PLAYER_VERSION 1 #define MAX_ALIASES 30 @@ -127,6 +129,7 @@ typedef struct _player { int side; // Only valid if game is >= 0 int partner; int registered; + int seek; // new int socket; int sopen; int status; @@ -200,6 +203,9 @@ typedef struct _textlist { extern player parray[PARRAY_SIZE]; extern int p_num; +extern bool player_num_ok_chk(const int); +extern void xrename(const char *, const char *, const char *); + extern int ClearMsgsBySender(int, param_list); extern int ClrMsgRange(int, int, int); extern int ShowMsgRange(int, int, int); diff --git a/FICS/ratings.c b/FICS/ratings.c index 3cd8d85..e445c51 100644 --- a/FICS/ratings.c +++ b/FICS/ratings.c @@ -28,6 +28,11 @@ Markus Uhlin 24/05/20 Fixed clang warnings Markus Uhlin 24/07/07 Return value checking of the fscanf() calls. + Markus Uhlin 24/11/27 Added sscanf() width spec and + fixed ignored retvals. + Markus Uhlin 24/11/28 Added null checks + Markus Uhlin 25/03/16 Fixed use of 32-bit 'time_t'. + Markus Uhlin 25/04/06 Fixed Clang Tidy warnings. */ #include "stdinclude.h" @@ -35,6 +40,8 @@ #include <err.h> #include <errno.h> +#include <limits.h> +#include <stdint.h> #include "command.h" #include "comproc.h" @@ -342,49 +349,52 @@ load_ratings(void) return; } - for (int i = 0; i < MAXHIST; i++) { - int ret, errno_save; + for (int i = 0; i < MAXHIST && !feof(fp) && !ferror(fp); i++) { + int ret; sHist[i] = bHist[i] = wHist[i] = lHist[i] = 0; - errno = 0; ret = fscanf(fp, "%d %d %d %d", &sHist[i], &bHist[i], &wHist[i], &lHist[i]); - errno_save = errno; if (ret != 4) { - if (feof(fp) || ferror(fp)) - break; - errno = errno_save; - warn("%s: too few items assigned (iteration: %d)", - __func__, i); + warnx("%s: %s: too few items assigned (iteration: %d)", + __func__, fname, i); + fclose(fp); + return; } } + if (ferror(fp)) { + warnx("%s: %s: the error indicator is set", __func__, fname); + fclose(fp); + return; + } + fclose(fp); - if (Rs_count) { - Ratings_S_StdDev = sqrt(Rs_S / Rs_count); + if (Rs_count != 0) { + Ratings_S_StdDev = sqrt(Rs_S / Rs_count); // NOLINT Ratings_S_Average = (Rs_total / (double)Rs_count); } else { Ratings_S_StdDev = 0; Ratings_S_Average = 0; } - if (Rb_count) { - Ratings_B_StdDev = sqrt(Rb_S / Rb_count); + if (Rb_count != 0) { + Ratings_B_StdDev = sqrt(Rb_S / Rb_count); // NOLINT Ratings_B_Average = (Rb_total / (double)Rb_count); } else { Ratings_B_StdDev = 0; Ratings_B_Average = 0; } - if (Rw_count) { - Ratings_W_StdDev = sqrt(Rw_S / Rw_count); + if (Rw_count != 0) { + Ratings_W_StdDev = sqrt(Rw_S / Rw_count); // NOLINT Ratings_W_Average = (Rw_total / (double)Rw_count); } else { Ratings_W_StdDev = 0; Ratings_W_Average = 0; } - if (Rl_count) { - Ratings_L_StdDev = sqrt(Rl_S / Rl_count); + if (Rl_count != 0) { + Ratings_L_StdDev = sqrt(Rl_S / Rl_count); // NOLINT Ratings_L_Average = (Rl_total / (double)Rl_count); } else { Ratings_L_StdDev = 0; @@ -751,7 +761,7 @@ GE(int r, int rr, double ss, double *fss) } PRIVATE double -current_sterr(double s, int t) +current_sterr(double s, int64_t t) { if (t < 0) t = 0; // this shouldn't happen @@ -766,15 +776,16 @@ current_sterr(double s, int t) * ics.onenet.net, if not elsewhere. */ PUBLIC void -rating_sterr_delta(int p1, int p2, int type, int gtime, int result, +rating_sterr_delta(int p1, int p2, int type, time_t gtime, int result, int *deltarating, double *newsterr) { double E, fs2, denominator, GK, w; // Parts of fancy formulas double delta, sigma; // Results to return double s1, s2; - int t1, r1, t2, r2; // Initial sterrs and ratings + int r1, r2; // Initial sterrs and ratings statistics *p1_stats; statistics *p2_stats; + time_t t1, t2; if (type == TYPE_BLITZ) { p1_stats = &parray[p1].b_stats; @@ -863,12 +874,12 @@ PUBLIC int rating_update(int g) { double wSigma, bSigma; - int gtime; int inprogress = (g == parray[garray[g].black].game); int wDelta, bDelta; int wRes, bRes; statistics *b_stats; statistics *w_stats; + time_t gtime; /* * If this is adjudication of stored game - be quiet about @@ -1297,6 +1308,18 @@ com_statistics(int p, param_list param) return COM_OK; } +/* + * Return the difference of 'a - b' + */ +PUBLIC int +int_diff(const char *fn, const int a, const int b) +{ + if ((b > 0 && a < INT_MIN + b) || + (b < 0 && a > INT_MAX + b)) + errx(1, "%s: integer overflow (%d - %d)", fn, a, b); + return (a - b); +} + PUBLIC int com_fixrank(int p, param_list param) { @@ -1339,8 +1362,16 @@ DisplayRank(int p, param_list param, int showComputers) DisplayTargetRank(p, parray[p].name, show, showComputers); return COM_OK; } else if (isdigit(param[0].val.word[0])) { + int ret; + + start = 0; end = -1; - sscanf(param[0].val.word, "%d-%d", &start, &end); + ret = sscanf(param[0].val.word, "%d-%d", &start, &end); + + if (ret != 2) { +// warnx("%s: sscanf() == %d", __func__, ret); + return COM_FAILED; + } if (end > 0 && (param[1].type != TYPE_NULL)) show = ShowFromString(param[1].val.word); @@ -1463,9 +1494,15 @@ UpdateRank(int type, char *addName, statistics *sNew, char *delName) return; } - while (fgets(line, MAX_RANK_LINE - 1, fp)) { - sscanf(line, "%s %d %d %d", login, &sCur.rating, &sCur.num, - &comp); + while (fgets(line, sizeof line, fp) != NULL) { + _Static_assert(ARRAY_SIZE(login) > 19, "'login' too small"); + + if (sscanf(line, "%19s %d %d %d", login, + &sCur.rating, &sCur.num, &comp) != 4) { + warnx("%s: %s: sscanf() error -- too few items", + __func__, RankFile); + continue; + } if (delName != NULL && !strcasecmp(delName, login)) { // Kill name. @@ -1533,9 +1570,15 @@ GetRank(FILE *fp, char *target, int countComp) int nGames, is_computer; int playerFound = 0; - while (fgets(line, MAX_RANK_LINE - 1, fp) && + while (fgets(line, sizeof line, fp) != NULL && !playerFound) { - sscanf(line, "%s %*d %d %d", login, &nGames, &is_computer); + _Static_assert(ARRAY_SIZE(login) > 19, "'login' too small"); + + if (sscanf(line, "%19s %*d %d %d", login, &nGames, &is_computer) + != 1) { +// warnx("%s: sscanf() error", __func__); + continue; + } if ((playerFound = !strcasecmp(login, target)) || CountRankLine(countComp, login, nGames, is_computer)) @@ -1556,18 +1599,33 @@ PositionFilePtr(FILE *fp, int count, int *last, int *nTied, int showComp) return; rating = nGames = is_computer = 0; + errno = 0; rewind(fp); + if (errno) { + warn("%s: rewind", __func__); + return; + } for (int i = 1; i < count; i++) { do { - fgets(line, MAX_RANK_LINE - 1, fp); + _Static_assert(ARRAY_SIZE(login) > 19, + "'login' too small"); - if (feof(fp)) + if (feof(fp) || ferror(fp) || + fgets(line, sizeof line, fp) == NULL) break; - sscanf(line, "%s %d %d %d", login, &rating, &nGames, - &is_computer); + else if (sscanf(line, "%19s %d %d %d", login, &rating, + &nGames, &is_computer) != 4) { + warnx("%s: sscanf() error", __func__); + break; + } } while (!CountRankLine(showComp, login, nGames, is_computer)); + if (ferror(fp)) { + warnx("%s: the error indicator is set", __func__); + return; + } + if (rating != *last) { *nTied = 1; *last = rating; @@ -1586,19 +1644,26 @@ ShowRankEntry(int p, FILE *fp, int count, int comp, char *target, // XXX rating = 0; - findable = (count > 0 && !feof(fp)); + findable = (count > 0 && !feof(fp) && !ferror(fp)); nGames = 0; is_comp = 0; if (findable) { do { - fgets(newLine, MAX_RANK_LINE - 1, fp); - - if (feof(fp)) { + if (fgets(newLine, sizeof newLine, fp) == NULL || + feof(fp) || + ferror(fp)) { findable = 0; } else if (newLine[0] != '\0') { - sscanf(newLine, "%s %d %d %d", login, &rating, - &nGames, &is_comp); + _Static_assert(ARRAY_SIZE(login) > 19, + "Assertion has failed"); + + if (sscanf(newLine, "%19s %d %d %d", login, + &rating, &nGames, &is_comp) != 4) { + warnx("%s: sscanf() error", __func__); + findable = 0; + break; + } } else { login[0] = '\0'; } @@ -1675,21 +1740,21 @@ ShowRankLines(int p, FILE *fb, FILE *fs, FILE *fw, int bCount, int sCount, if (n <= 0) return 0; - if (CheckFlag(show, SHOW_BLITZ)) { + if (fb != NULL && CheckFlag(show, SHOW_BLITZ)) { PositionFilePtr(fb, bCount, &lastBlitz, &nTiedBlitz, showComp); if (feof(fb)) ClearFlag(show, SHOW_BLITZ); } - if (CheckFlag(show, SHOW_STANDARD)) { + if (fs != NULL && CheckFlag(show, SHOW_STANDARD)) { PositionFilePtr(fs, sCount, &lastStd, &nTiedStd, showComp); if (feof(fs)) ClearFlag(show, SHOW_STANDARD); } - if (CheckFlag(show, SHOW_WILD)) { + if (fw != NULL && CheckFlag(show, SHOW_WILD)) { PositionFilePtr(fw, wCount, &lastWild, &nTiedWild, showComp); if (feof(fw)) @@ -1702,15 +1767,15 @@ ShowRankLines(int p, FILE *fb, FILE *fs, FILE *fw, int bCount, int sCount, DisplayRankHead(p, show); for (int i = 0; i < n && show; i++) { - if (CheckFlag(show, SHOW_BLITZ)) { + if (fb != NULL && CheckFlag(show, SHOW_BLITZ)) { bCount += ShowRankEntry(p, fb, bCount, showComp, target, &lastBlitz, &nTiedBlitz); } - if (CheckFlag(show, SHOW_STANDARD)) { + if (fs != NULL && CheckFlag(show, SHOW_STANDARD)) { sCount += ShowRankEntry(p, fs, sCount, showComp, target, &lastStd, &nTiedStd); } - if (CheckFlag(show, SHOW_WILD)) { + if (fw != NULL && CheckFlag(show, SHOW_WILD)) { wCount += ShowRankEntry(p, fw, wCount, showComp, target, &lastWild, &nTiedWild); } @@ -1776,9 +1841,9 @@ DisplayTargetRank(int p, char *target, int show, int showComp) numAbove = CountAbove(numToShow, blitzRank, stdRank, wildRank, show); - blitzCount = (blitzRank - numAbove); - stdCount = (stdRank - numAbove); - wildCount = (wildRank - numAbove); + blitzCount = int_diff(__func__, blitzRank, numAbove); + stdCount = int_diff(__func__, stdRank, numAbove); + wildCount = int_diff(__func__, wildRank, numAbove); ShowRankLines(p, fb, fs, fw, blitzCount, stdCount, wildCount, numToShow, showComp, show, target); diff --git a/FICS/ratings.h b/FICS/ratings.h index 50b4f56..7f2a521 100644 --- a/FICS/ratings.h +++ b/FICS/ratings.h @@ -26,6 +26,8 @@ #ifndef _RATINGS_H #define _RATINGS_H +#include "common.h" + #define STATS_VERSION 2 #define RESULT_WIN 0 @@ -49,6 +51,7 @@ typedef struct _rateStruct { int rating; } rateStruct; +__FICS_BEGIN_DECLS extern int Best(int, param_list, int); extern int DisplayRank(int, param_list, int); extern int DisplayRankedPlayers(int, int, int, int, int); @@ -61,6 +64,7 @@ extern int com_hbest(int, param_list); extern int com_hrank(int, param_list); extern int com_rank(int, param_list); extern int com_statistics(int, param_list); +extern int int_diff(const char *, const int, const int); extern int is_active(int); extern int rating_delta(int, int, int, int, int); extern int rating_update(int); @@ -70,7 +74,8 @@ extern void rating_add(int, int); extern void rating_init(void); extern void rating_recalc(void); extern void rating_remove(int, int); -extern void rating_sterr_delta(int, int, int, int, int, int *, double *); +extern void rating_sterr_delta(int, int, int, time_t, int, int *, double *); extern void save_ratings(void); +__FICS_END_DECLS #endif /* _RATINGS_H */ diff --git a/FICS/sought.cpp b/FICS/sought.cpp new file mode 100644 index 0000000..b4dd282 --- /dev/null +++ b/FICS/sought.cpp @@ -0,0 +1,41 @@ +// SPDX-FileCopyrightText: 2024 Markus Uhlin <maxxe@rpblc.net> +// SPDX-License-Identifier: ISC + +#include "stdinclude.h" +#include "common.h" + +#include "sought.h" + +/* + * Usage: sought [all] + * + * The "sought" command can be used in two ways: (a) typing "sought + * all" will display all current ads including your own; (b) typing + * "sought" alone will display only those current ads for which you + * are eligible based on any formula you might have (default). An + * example output is as follows: + * + * 0 1900 Hawk blitz 5 0 rated 1800-2000 f + * 1 1700 Friar wild7 2 12 unrated [white] 0-9999 + * 4 1500 loon standard 5 0 unrated 0-9999 m + * + * The various columns have this information: + * + * Ad index number + * Player's rating + * Player's handle + * Type of chess match + * Time at start + * Increment per move + * Rated/unrated + * Color (if specified) + * Rating range + * Auto start/manual start and whether formula will be checked + */ +PUBLIC int +com_sought(int p, param_list param) +{ + UNUSED_PARAM(p); + UNUSED_PARAM(param); + return COM_OK; +} diff --git a/FICS/sought.h b/FICS/sought.h new file mode 100644 index 0000000..873435c --- /dev/null +++ b/FICS/sought.h @@ -0,0 +1,11 @@ +#ifndef COM_SOUGHT_H +#define COM_SOUGHT_H + +#include "command.h" /* param_list */ +#include "common.h" + +__FICS_BEGIN_DECLS +int com_sought(int, param_list); +__FICS_END_DECLS + +#endif diff --git a/FICS/talkproc.c b/FICS/talkproc.c index 85da66d..601f43b 100644 --- a/FICS/talkproc.c +++ b/FICS/talkproc.c @@ -33,6 +33,9 @@ Markus Uhlin 24/01/04 Fixed pprintf() calls Markus Uhlin 24/04/14 Refactored and reformatted ALL functions. + Markus Uhlin 25/01/18 Fixed -Wshadow + Markus Uhlin 25/04/04 tell: fixed constant expression + result */ #include "stdinclude.h" @@ -295,8 +298,8 @@ tell(int p, int p1, char *msg, int why, int ch) } if (player_censored(p1, p) && parray[p].adminLevel == 0) { - if (why != TELL_KIBITZ || - why != TELL_WHISPER || + if (why != TELL_KIBITZ && + why != TELL_WHISPER && why != TELL_CHANNEL) { pprintf(p, "Player \"%s\" is censoring you.\n", parray[p1].name); @@ -830,7 +833,7 @@ com_messages(int p, param_list param) } if (param[0].type == TYPE_NULL) { - player_show_messages (p); + player_show_messages(p); } else if (param[0].type == TYPE_WORD) { if (param[1].type != TYPE_NULL) return com_sendmessage(p, param); @@ -968,10 +971,10 @@ com_qtell(int p, param_list param) return COM_OK; } - mstrlcpy(buffer1, ":\0", sizeof buffer1); - mstrlcpy(buffer2, ":\0", sizeof buffer2); - mstrlcpy(buffer3, ":\0", sizeof buffer3); - mstrlcpy(buffer4, ":\0", sizeof buffer4); + mstrlcpy(buffer1, ":", sizeof buffer1); + mstrlcpy(buffer2, ":", sizeof buffer2); + mstrlcpy(buffer3, ":", sizeof buffer3); + mstrlcpy(buffer4, ":", sizeof buffer4); msnprintf(tmp, sizeof tmp, "%s", param[1].val.string); @@ -1034,7 +1037,6 @@ com_qtell(int p, param_list param) pprintf(p, "*qtell %s 0*\n", parray[p1].name); } else { int ch = param[0].val.integer; - int p1; if (ch == 0) { pprintf(p, "*qtell %d 1*\n", param[0].val.integer); diff --git a/FICS/utils.c b/FICS/utils.c index 0784711..fd0a8e3 100644 --- a/FICS/utils.c +++ b/FICS/utils.c @@ -32,6 +32,13 @@ Markus Uhlin 24/07/07 Added parameter 'size' to psprintf_highlight(). Markus Uhlin 24/08/11 Improved fix_time(). + Markus Uhlin 24/12/01 Usage of sizeof and fixed + ignored fgets() retvals. + Markus Uhlin 24/12/02 Fixed a possible array overrun + in truncate_file(). + Markus Uhlin 25/03/09 truncate_file: + fixed null ptr dereference. + Markus Uhlin 25/04/06 Fixed Clang Tidy warnings. */ #include "stdinclude.h" @@ -200,8 +207,8 @@ PUBLIC int mail_file_to_address(char *addr, char *subj, char *fname) { FILE *fp1, *fp2; - char com[1000]; - char tmp[MAX_LINE_SIZE]; + char com[1000] = { '\0' }; + char tmp[MAX_LINE_SIZE] = { '\0' }; /* maybe unused */ (void) fp2; @@ -216,15 +223,15 @@ mail_file_to_address(char *addr, char *subj, char *fname) if ((fp1 = popen(com, "w")) == NULL) return -1; #ifdef SENDMAILPROG - fprintf(fp1, "To: %s\nSubject: %s\n", addr, subj); - if ((fp2 = fopen(fname, "r")) == NULL) + if ((fp2 = fopen(fname, "r")) == NULL) { + pclose(fp1); return -1; - while (!feof(fp2)) { - fgets(tmp, MAX_LINE_SIZE - 1, fp2); - if (!feof(fp2)) { - fputs(tmp, fp1); - } } + + fprintf(fp1, "To: %s\nSubject: %s\n", addr, subj); + + while (fgets(tmp, sizeof tmp, fp2) != NULL && !feof(fp2)) + fputs(tmp, fp1); fclose(fp2); #endif pclose(fp1); @@ -250,7 +257,7 @@ mail_file_to_user(int p, char *subj, char *fname) PUBLIC int pcommand(int p, char *comstr, ...) { - char tmp[MAX_LINE_SIZE]; + char tmp[MAX_LINE_SIZE] = { '\0' }; int current_socket = parray[p].socket; int retval; va_list ap; @@ -269,10 +276,10 @@ pcommand(int p, char *comstr, ...) return retval; } -PUBLIC int +PUBLIC void pprintf(int p, const char *format, ...) { - char tmp[10 * MAX_LINE_SIZE]; + char tmp[10 * MAX_LINE_SIZE] = { '\0' }; int retval; va_list ap; @@ -280,8 +287,8 @@ pprintf(int p, const char *format, ...) retval = vsnprintf(tmp, sizeof tmp, format, ap); va_end(ap); + UNUSED_VAR(retval); net_send_string(parray[p].socket, tmp, 1); - return retval; } PRIVATE void @@ -387,7 +394,7 @@ pprintf_noformat(int p, char *format, ...) } PUBLIC int -psend_raw_file(int p, char *dir, char *file) +psend_raw_file(int p, const char *dir, const char *file) { FILE *fp; char fname[MAX_FILENAME_SIZE] = { '\0' }; @@ -402,9 +409,18 @@ psend_raw_file(int p, char *dir, char *file) if ((fp = fopen(fname, "r")) == NULL) return -1; - while ((num = fread(tmp, sizeof(char), MAX_LINE_SIZE - 1, fp)) > 0) { - tmp[num] = '\0'; - net_send_string(parray[p].socket, tmp, 1); + while (!feof(fp) && !ferror(fp)) { + if ((num = fread(tmp, sizeof(char), MAX_LINE_SIZE - 1, + fp)) > 0) { + tmp[num] = '\0'; + net_send_string(parray[p].socket, tmp, 1); + } + } + + if (ferror(fp)) { + warnx("%s: %s: the error indicator is set", __func__, fname); + fclose(fp); + return -1; } fclose(fp); @@ -412,7 +428,7 @@ psend_raw_file(int p, char *dir, char *file) } PUBLIC int -psend_file(int p, char *dir, char *file) +psend_file(int p, const char *dir, const char *file) { FILE *fp; char fname[MAX_FILENAME_SIZE] = { '\0' }; @@ -432,14 +448,19 @@ psend_file(int p, char *dir, char *file) if ((fp = fopen(fname, "r")) == NULL) return -1; - while (!feof(fp) && --lcount > 0) { - fgets(tmp, MAX_LINE_SIZE - 1, fp); - - if (!feof(fp)) - net_send_string(parray[p].socket, tmp, 1); + while (--lcount > 0) { + if (fgets(tmp, sizeof tmp, fp) == NULL) + break; + net_send_string(parray[p].socket, tmp, 1); } if (!feof(fp)) { + if (ferror(fp)) { + warnx("%s: %s: the error indicator is set", __func__, + fname); + fclose(fp); + return -1; + } parray[p].last_file = xstrdup(fname); parray[p].last_file_byte = ftell(fp); pprintf(p, "Type [next] to see next page.\n"); @@ -473,12 +494,8 @@ psend_logoutfile(int p, char *dir, char *file) if ((fp = fopen(fname, "r")) == NULL) return -1; - while (!feof(fp)) { - fgets(tmp, MAX_LINE_SIZE - 1, fp); - - if (!feof(fp)) - net_send_string(parray[p].socket, tmp, 1); - } + while (fgets(tmp, sizeof tmp, fp) != NULL && !feof(fp)) + net_send_string(parray[p].socket, tmp, 1); fclose(fp); return 0; @@ -488,7 +505,7 @@ PUBLIC int pmore_file(int p) { FILE *fp; - char tmp[MAX_LINE_SIZE]; + char tmp[MAX_LINE_SIZE] = { '\0' }; int lcount = (parray[p].d_height - 1); if (!parray[p].last_file) { @@ -499,20 +516,30 @@ pmore_file(int p) if ((fp = fopen(parray[p].last_file, "r")) == NULL) { pprintf(p, "File not found!\n"); return -1; + } else if (fseek(fp, parray[p].last_file_byte, SEEK_SET) == -1) { + pprintf(p, "Unable to set the file position indicator.\n"); + fclose(fp); + return -1; } - fseek(fp, parray[p].last_file_byte, SEEK_SET); - - while (!feof(fp) && --lcount > 0) { - fgets(tmp, MAX_LINE_SIZE, fp); - - if (!feof(fp)) - net_send_string(parray[p].socket, tmp, 1); + while (--lcount > 0) { + if (fgets(tmp, sizeof tmp, fp) == NULL) + break; + net_send_string(parray[p].socket, tmp, 1); } if (!feof(fp)) { - parray[p].last_file_byte = ftell(fp); - pprintf(p, "Type [next] to see next page.\n"); + if (ferror(fp)) { + warnx("%s: %s: the error indicator is set", __func__, + parray[p].last_file); + fclose(fp); + return -1; + } else if ((parray[p].last_file_byte = ftell(fp)) == -1) { + warn("%s: %s: ftell", __func__, parray[p].last_file); + fclose(fp); + return -1; + } else + pprintf(p, "Type [next] to see next page.\n"); } else { rfree(parray[p].last_file); parray[p].last_file = NULL; @@ -707,9 +734,9 @@ fix_time(char *old_time) char month[5] = { '\0' }; static char new_time[20]; - _Static_assert(4 < ARRAY_SIZE(day), "Array too small"); - _Static_assert(4 < ARRAY_SIZE(month), "Array too small"); - _Static_assert(4 < ARRAY_SIZE(date), "Array too small"); + _Static_assert(4 < ARRAY_SIZE(day), "'day' too small"); + _Static_assert(4 < ARRAY_SIZE(month), "'month' too small"); + _Static_assert(4 < ARRAY_SIZE(date), "'date' too small"); if (sscanf(old_time, "%4s %4s %4s", day, month, date) != 3) warnx("%s: sscanf: too few items (%s)", __func__, old_time); @@ -762,8 +789,11 @@ tenth_secs(void) * seconds, because vek didn't read utils.c when he programmed new * ratings. 1 sec since 1970 fits into a 32 bit int OK. */ -PUBLIC int -untenths(unsigned int tenths) +/* + * 2024-11-23 maxxe: changed the return type to 'time_t' + */ +PUBLIC time_t +untenths(uint64_t tenths) { return (tenths / 10 + 331939277 + 0xffffffff / 10 + 1); } @@ -792,13 +822,8 @@ truncate_file(char *file, int lines) if ((fp = fopen(file, "r")) == NULL) return 1; - while (!feof(fp)) { - fgets(tBuf[bptr], MAX_LINE_SIZE, fp); - - if (feof(fp)) - break; - - if (tBuf[bptr][strlen(tBuf[bptr]) - 1] != '\n') { + while (fgets(tBuf[bptr], MAX_LINE_SIZE, fp) != NULL) { + if (strchr(tBuf[bptr], '\n') == NULL) { // Line too long fclose(fp); return -1; @@ -813,7 +838,10 @@ truncate_file(char *file, int lines) fclose(fp); if (trunc) { - fp = fopen(file, "w"); + if ((fp = fopen(file, "w")) == NULL) { + warn("%s: fopen", __func__); + return 1; + } for (i = 0; i < lines; i++) { fputs(tBuf[bptr], fp); @@ -842,10 +870,8 @@ lines_file(char *file) if ((fp = fopen(file, "r")) == NULL) return 0; - while (!feof(fp)) { - if (fgets(tmp, MAX_LINE_SIZE, fp)) - lcount++; - } + while (fgets(tmp, sizeof tmp, fp) != NULL && !feof(fp)) + lcount++; fclose(fp); return lcount; @@ -963,10 +989,11 @@ ratstrii(int rat, int reg) * Fill 't_buffer' with anything matching "want*" in file tree */ PRIVATE void -t_sft(char *want, struct t_tree *t) +t_sft(const char *want, struct t_tree *t) { if (t) { - int cmp = strncmp(want, t->name, strlen(want)); + const char *v_want = (want ? want : ""); + int cmp = strncmp(v_want, t->name, strlen(v_want)); if (cmp <= 0) // If 'want' <= this one, look left t_sft(want, t->left); @@ -1049,7 +1076,6 @@ PUBLIC int search_directory(char *dir, char *filter, char **buffer, int buffersize) { int cmp; - static char nullify = '\0'; static struct t_dirs *ramdirs = NULL; struct stat statbuf; struct t_dirs** i; @@ -1058,9 +1084,6 @@ search_directory(char *dir, char *filter, char **buffer, int buffersize) t_buffersize = buffersize; if (!stat(dir, &statbuf)) { - if (filter == NULL) // NULL becomes pointer to null string - filter = &nullify; - i = &ramdirs; while (*i) { // Find dir in dir tree diff --git a/FICS/utils.h b/FICS/utils.h index 3bcdd4d..8f53882 100644 --- a/FICS/utils.h +++ b/FICS/utils.h @@ -25,11 +25,13 @@ Markus Uhlin 23/12/17 Added argument lists Markus Uhlin 23/12/28 Completed adding argument lists Markus Uhlin 24/01/04 Added usage of PRINTFLIKE() + Markus Uhlin 24/11/20 Usage of begin/end decls */ #ifndef _UTILS_H #define _UTILS_H +#include <stdint.h> #include <stdio.h> #include "common.h" /* PRINTFLIKE() */ @@ -55,10 +57,7 @@ #define ClearFlag(VAR, FLAG) (VAR &= ~(FLAG)) #define CheckFlag(VAR, FLAG) (VAR & (FLAG)) -#ifdef __cplusplus -extern "C" { -#endif - +__FICS_BEGIN_DECLS extern char *dotQuad(unsigned int); extern char *eattailwhite(char *); extern char *eatwhite(char *); @@ -91,28 +90,25 @@ extern int mail_string_to_address(char *, char *, char *); extern int mail_string_to_user(int, char *, char *); extern int pcommand(int, char *, ...) PRINTFLIKE(2); extern int pmore_file(int); -extern int pprintf(int, const char *, ...) PRINTFLIKE(2); +extern void pprintf(int, const char *, ...) PRINTFLIKE(2); extern int pprintf_highlight(int, char *, ...) PRINTFLIKE(2); extern int pprintf_noformat(int, char *, ...) PRINTFLIKE(2); extern int pprintf_prompt(int, char *, ...) PRINTFLIKE(2); extern int printablestring(char *); extern int psend_command(int, char *, char *); -extern int psend_file(int, char *, char *); +extern int psend_file(int, const char *, const char *); extern int psend_logoutfile(int, char *, char *); -extern int psend_raw_file(int, char *, char *); +extern int psend_raw_file(int, const char *, const char *); extern int psprintf_highlight(int, char *, size_t, char *, ...) PRINTFLIKE(4); extern int safechar(int); extern int safestring(char *); extern int search_directory(char *, char *, char **, int); extern int truncate_file(char *, int); -extern int untenths(unsigned int); +extern time_t untenths(uint64_t); extern unsigned int tenth_secs(void); //extern void pprintf_dohightlight(int); //extern void sprintf_dohightlight(int, char *); - -#ifdef __cplusplus -} -#endif +__FICS_END_DECLS #endif /* _UTILS_H */ diff --git a/FICS/variable.c b/FICS/variable.c index 8837c96..e004eca 100644 --- a/FICS/variable.c +++ b/FICS/variable.c @@ -28,11 +28,14 @@ Markus Uhlin 23/12/27 Fixed the includes Markus Uhlin 24/04/01 Reformatted all functions Markus Uhlin 24/07/08 Added var 'interface' + Markus Uhlin 24/11/19 Improved Language() + Markus Uhlin 24/11/24 Added var 'seek'. */ #include "stdinclude.h" #include "common.h" +#include <err.h> #include <stdbool.h> #include "board.h" @@ -122,6 +125,33 @@ set_ropen(int p, char *var, char *val) return VAR_OK; } +/* + * If your 'seek' variable is non-zero (whether a number, 'true' or + * 'on'), you will be informed of "seek" ads when they are made. zero + * ('false' or 'off'), you will not receive these announcements. For + * example, to receive ads, type "set seek 1". To stop receiving ads, + * type "set seek 0". + */ +PRIVATE int +set_seek(int p, char *var, char *val) +{ + int v = set_boolean_var(&parray[p].seek, val); + + if (v < 0) + return VAR_BADVAL; + + pprintf(p, "seek set to %d.\n", parray[p].seek); + + if (v > 0) { + pprintf(p, "You will be informed of seek ads when they are " + "made.\n"); + } else { + pprintf(p, "You are no longer receiving seek ads.\n"); + } + + return VAR_OK; +} + PRIVATE int set_rated(int p, char *var, char *val) { @@ -464,15 +494,22 @@ set_width(int p, char *var, char *val) return VAR_OK; } -PUBLIC char * -Language(int i) -{ // XXX +PUBLIC const char * +Language(unsigned int i) +{ static char *Lang[NUM_LANGS] = { "English", "Spanish", "French", "Danish" }; + + _Static_assert(ARRAY_SIZE(Lang) == 4, "Unexpected array size"); + + if (i >= ARRAY_SIZE(Lang)) { + warnx("%s: invalid arg %u (too large)", __func__, i); + return Lang[0]; + } return Lang[i]; } @@ -877,6 +914,7 @@ PUBLIC var_list variables[] = { {"prompt", set_prompt}, {"rated", set_rated}, {"ropen", set_ropen}, + {"seek", set_seek}, {"shout", set_shout}, {"simopen", set_sopen}, {"style", set_style}, diff --git a/FICS/variable.h b/FICS/variable.h index 3c13eae..ced7878 100644 --- a/FICS/variable.h +++ b/FICS/variable.h @@ -21,11 +21,14 @@ name email yy/mm/dd Change Richard Nash 93/10/22 Created Markus Uhlin 24/04/01 Revised + Markus Uhlin 24/11/24 Added begin/end decls */ #ifndef _VARIABLE_H #define _VARIABLE_H +#include "common.h" + #define VAR_OK 0 #define VAR_NOSUCH 1 #define VAR_BADVAL 2 @@ -42,9 +45,11 @@ typedef struct _var_list { int (*var_func)(int, char *, char *); } var_list; +__FICS_BEGIN_DECLS extern var_list variables[]; -extern char *Language(int); -extern int var_set(int, char *, char *, int *); +extern const char *Language(unsigned int); +extern int var_set(int, char *, char *, int *); +__FICS_END_DECLS #endif /* _VARIABLE_H */ diff --git a/FICS/vers.c b/FICS/vers.c index 3022be4..c6dd99c 100644 --- a/FICS/vers.c +++ b/FICS/vers.c @@ -1,5 +1,5 @@ #include "vers.h" -char SGS_VERS[] = ""; -char VERS_NUM[] = "rpblc-1.4.3"; -char COMP_DATE[] = __DATE__; +const char SGS_VERS[] = ""; +const char VERS_NUM[] = "rpblc-1.4.5"; +const char COMP_DATE[] = __DATE__; diff --git a/FICS/vers.h b/FICS/vers.h index df005ac..874c835 100644 --- a/FICS/vers.h +++ b/FICS/vers.h @@ -19,13 +19,14 @@ /* Revision history: name email yy/mm/dd Change Richard Nash 93/10/22 Created + Markus Uhlin 24/11/23 Made vars const */ #ifndef _VERS_H #define _VERS_H -extern char SGS_VERS[]; -extern char VERS_NUM[]; -extern char COMP_DATE[]; +extern const char SGS_VERS[]; +extern const char VERS_NUM[]; +extern const char COMP_DATE[]; #endif /* _VERS_H */ diff --git a/GNUmakefile b/GNUmakefile index f94b0a4..cf29782 100644 --- a/GNUmakefile +++ b/GNUmakefile @@ -14,7 +14,8 @@ include FICS/build.mk # common rules include common.mk -.PHONY: clean install-init install +.PHONY: clean install-init install tidy include $(TARGETS_DIR)clean.mk include $(TARGETS_DIR)install.mk +include $(TARGETS_DIR)tidy.mk @@ -1,5 +1,7 @@ # README # + + ## About ## This is a fork of FICS(Free Internet Chess Server) version 1.6.2 made @@ -12,6 +14,26 @@ The main goal of the fork is to modernize the codebase, improve the security and fix bugs. New features, for example, other chess variants will be added in a later stage. +### Public chess server ### + +[IRCNow](https://ircnow.org/) +provides a +[public chess server](https://wiki.ircnow.org/index.php?n=Chess.Chess) +for everyone to use! + +To connect to the server by using +[XBoard](https://www.gnu.org/software/xboard/), try: + + $ xboard -ics -icshost rpblc.net + +### IPv6 ### + +IPv6 connections are at the moment not supported. + +## SAST Tools ## + +[PVS-Studio](https://pvs-studio.com/en/pvs-studio/?utm_source=website&utm_medium=github&utm_campaign=open_source) - static analyzer for C, C++, C#, and Java code. + ## Installation ## Begin the installation with creating a new user, dedicated for running @@ -29,6 +51,19 @@ repository: $ git clone https://github.com/uhlin/fics.git $ cd fics +### Checkout ### + +If you want you can checkout a specific version. For example: + + $ git checkout 1.4.4 + +(This is a good idea since you most likely don't want to run a +development version.) + +To see all tags, type: + + $ git tag + Edit `FICS/config.h` with a text editor and save the file. $ emacs FICS/config.h @@ -45,6 +80,10 @@ running `make install`. Done! +**NOTE**: +Running `make install` multiple times is totally fine and does no harm +when a new version of FICS is available. + ### Make variables ### If you want you can customize the `FICS_HOME` and `PREFIX` make diff --git a/include/colors.h b/include/colors.h new file mode 100644 index 0000000..6902138 --- /dev/null +++ b/include/colors.h @@ -0,0 +1,81 @@ +/* Copyright (c) 2019 Markus Uhlin <markus.uhlin@icloud.com> + All rights reserved. + + Permission to use, copy, modify, and distribute this software for any + purpose with or without fee is hereby granted, provided that the above + copyright notice and this permission notice appear in all copies. + + THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + PERFORMANCE OF THIS SOFTWARE. */ + +#ifndef _COLORS_H_ +#define _COLORS_H_ + +/* + * Reset all attributes to their defaults + */ +#define NORMAL "\x1b[0m" + +#define UNDERLINE_ON "\x1b[4m" +#define UNDERLINE_OFF "\x1b[24m" + +#define BLINK_ON "\x1b[5m" +#define BLINK_OFF "\x1b[25m" + +/* + * Reverse video + */ +#define REVVID_ON "\x1b[7m" +#define REVVID_OFF "\x1b[27m" + +#define BLACK "\x1b[30m" +#define RED "\x1b[31m" +#define GREEN "\x1b[32m" +#define BROWN "\x1b[33m" +#define BLUE "\x1b[34m" +#define MAGENTA "\x1b[35m" +#define CYAN "\x1b[36m" +#define WHITE "\x1b[37m" + +#define BOLDBLACK "\x1b[1;30m" +#define BOLDRED "\x1b[1;31m" +#define BOLDGREEN "\x1b[1;32m" +#define BOLDBROWN "\x1b[1;33m" +#define BOLDBLUE "\x1b[1;34m" +#define BOLDMAGENTA "\x1b[1;35m" +#define BOLDCYAN "\x1b[1;36m" +#define BOLDWHITE "\x1b[1;37m" + +#define UNDERSCORE_ON "\x1b[38m" +#define UNDERSCORE_OFF "\x1b[39m" + +#define BGBLACK "\x1b[40m" +#define BGRED "\x1b[41m" +#define BGGREEN "\x1b[42m" +#define BGBROWN "\x1b[43m" +#define BGBLUE "\x1b[44m" +#define BGMAGENTA "\x1b[45m" +#define BGCYAN "\x1b[46m" +#define BGWHITE "\x1b[47m" +#define BGDEFAULT "\x1b[49m" + +#if defined(_WIN32) && defined(ENABLE_VIRTUAL_TERMINAL_PROCESSING) +static void +VirtualTerminalProcessing(void) +{ + HANDLE output_handle = GetStdHandle(STD_OUTPUT_HANDLE); + DWORD modes = 0; + + GetConsoleMode(output_handle, &modes); + modes |= ENABLE_VIRTUAL_TERMINAL_PROCESSING; + SetConsoleMode(output_handle, modes); +} +#endif + +#endif diff --git a/maketargets/install.mk b/maketargets/install.mk index 1f43793..db07e25 100644 --- a/maketargets/install.mk +++ b/maketargets/install.mk @@ -25,6 +25,8 @@ install-init: install -m 0644 $(ROOT)data/boards/std.board \ $(DESTDIR)$(FICS_HOME)/data/boards install -d $(DESTDIR)$(FICS_HOME)/data/boards/wild + install -m 0644 $(ROOT)data/boards/standard/standard \ + $(DESTDIR)$(FICS_HOME)/data/boards/wild/bughouse install -d $(DESTDIR)$(FICS_HOME)/data/book install -d $(DESTDIR)$(FICS_HOME)/data/com_help install -m 0755 $(ROOT)data/com_help/makelinks.sh \ diff --git a/maketargets/tidy.mk b/maketargets/tidy.mk new file mode 100644 index 0000000..e52abe2 --- /dev/null +++ b/maketargets/tidy.mk @@ -0,0 +1,9 @@ +# The 'tidy' target + +TIDY = clang-tidy +TIDYFLAGS = -checks=-clang-analyzer-security.insecureAPI.strcpy,-clang-analyzer-optin.performance.Padding,-clang-analyzer-security.insecureAPI.DeprecatedOrUnsafeBufferHandling -quiet +FICS_CLANG_TIDYFLAGS ?= + +tidy: $(INCLUDE_DIR)ficspaths.h + $(TIDY) $(SRCS) $(TIDYFLAGS) $(FICS_CLANG_TIDYFLAGS) -- \ + -I $(INCLUDE_DIR) $(CPPFLAGS) @@ -7,6 +7,8 @@ PREFIX ?= /home/chess CC ?= cc CFLAGS = -O2 -Wall -g -pipe -std=c11 \ + -Wformat-security \ + -Wshadow \ -Wsign-compare \ -Wstrict-prototypes @@ -14,7 +16,7 @@ CXX ?= c++ CXXFLAGS = -O2 -Wall -g -pipe -std=c++17 # C preprocessor flags -CPPFLAGS = -D_DEFAULT_SOURCE=1 +CPPFLAGS = -D_DEFAULT_SOURCE=1 -D_FORTIFY_SOURCE=3 LDFLAGS = LDLIBS = -lbsd -lcrypt diff --git a/tests/fscanf1.c b/tests/fscanf1.c new file mode 100644 index 0000000..366ea89 --- /dev/null +++ b/tests/fscanf1.c @@ -0,0 +1,36 @@ +#include <err.h> +#include <stdio.h> + +#include "maxxes-utils.h" + +int +main(void) +{ + FILE *fp; + char End[100] = { '\0' }; + char fmt[80] = { '\0' }; + const size_t End_size = sizeof End; + int index = 0; + long int when = 0; + + if ((fp = fopen("txt/stats-games.txt", "r")) == NULL) + err(1, "fopen"); + + msnprintf(fmt, sizeof fmt, "%%d %%*c %%*d %%*c %%*d %%*s %%*s %%*d " + "%%*d %%*d %%*d %%*s %%%zus %%ld\n", (End_size - 1)); + puts(fmt); + + do { + if (fscanf(fp, fmt, &index, End, &when) != 3) + warnx("items assigned mismatch"); + else { + printf("index:\t%d\nEnd:\t%s\nwhen:\t%ld\n---\n", + index, + End, + when); + } + } while (!feof(fp) && !ferror(fp)); + + fclose(fp); + return 0; +} diff --git a/tests/txt/stats-games.txt b/tests/txt/stats-games.txt new file mode 100644 index 0000000..f87a46b --- /dev/null +++ b/tests/txt/stats-games.txt @@ -0,0 +1,10 @@ +17 - 1425 B 1921 jrmu br 1200 120 1200 120 A00 Mat 1720893733 +18 = 0 W 0 kiliro puu 1200 120 1200 120 A00 Agr 1721235741 +19 = 1449 B 1692 kiliro br 1200 120 1200 120 *** Agr 1721235780 +20 = 0 W 0 kiliro puu 1200 120 1200 120 A00 Agr 1721237855 +21 - 1443 B 1948 jrmu br 1200 120 1200 120 A00 Mat 1721241134 +22 - 1443 W 0 bobbel bu 1200 120 1200 120 A00 Mat 1721252601 +23 - 1443 W 0 maxxetwo bu 1200 120 1200 120 A00 Mat 1722731908 +24 - 1443 W 0 maxxetwo bu 1200 120 1200 120 A00 Mat 1723503792 +25 = 1443 B 0 maxxetwo bu 1200 120 1200 120 A00 Agr 1733280040 +26 - 1443 W 0 maxxetwo bu 1200 120 1200 120 A00 Mat 1733280686 |