/* lists.c -- new global lists code * * Added by Shaney, 29 May 1995. * Revised by maxxe, 15 Dec 2023. * This file is part of FICS. */ #include "stdinclude.h" #include "common.h" #include #include "command.h" #include "comproc.h" #include "gamedb.h" #include "lists.h" #include "maxxes-utils.h" #include "multicol.h" #include "playerdb.h" #include "ratings.h" #include "rmalloc.h" #include "talkproc.h" #include "utils.h" PRIVATE List *firstGlobalList = NULL; PRIVATE ListTable ListArray[] = { {P_HEAD, "admin"}, {P_GOD, "removedcom"}, {P_ADMIN, "filter"}, {P_ADMIN, "ban"}, {P_ADMIN, "abuser"}, {P_ADMIN, "muzzle"}, {P_ADMIN, "cmuzzle"}, {P_PUBLIC, "fm"}, {P_PUBLIC, "im"}, {P_PUBLIC, "gm"}, {P_PUBLIC, "blind"}, {P_PUBLIC, "teams"}, {P_PUBLIC, "computer"}, {P_PUBLIC, "td"}, {P_PERSONAL, "censor"}, {P_PERSONAL, "gnotify"}, {P_PERSONAL, "noplay"}, {P_PERSONAL, "notify"}, {P_PERSONAL, "channel"}, {0, NULL}, }; /* * find a list. * loads from disk if not in memory. */ PRIVATE List * list_find(int p, enum ListWhich l) { List *prev = NULL, *tempList, **starter; int count = 0; int personal; personal = ListArray[l].rights == P_PERSONAL; starter = (personal ? &parray[p].lists : &firstGlobalList); for (tempList = *starter; tempList != NULL; tempList = tempList->next) { if (l == tempList->which) { if (prev != NULL) { prev->next = tempList->next; tempList->next = *starter; *starter = tempList; } return tempList; } prev = tempList; } if ((tempList = rmalloc(sizeof(List))) == NULL) return NULL; if (!personal) { // now we have to load the list FILE *fp; char filename[MAX_FILENAME_SIZE]; char listmember[100]; sprintf(filename, "%s/%s", lists_dir, ListArray[l].name); if ((fp = fopen(filename, "r")) == NULL) { rfree(tempList); return NULL; } while (!feof(fp)) { if (fgets(listmember, 100, fp) != NULL) { listmember[strlen(listmember) - 1] = '\0'; tempList->member[count++] = xstrdup(listmember); } } fclose(fp); } tempList->which = l; tempList->numMembers = count; tempList->next = *starter; *starter = tempList; return tempList; } /* * Add item to list */ PUBLIC int list_add(int p, enum ListWhich l, char *s) { List *gl; if ((gl = list_find(p, l)) != NULL) { if (gl->numMembers < MAX_GLOBAL_LIST_SIZE) { gl->member[gl->numMembers] = xstrdup(s); gl->numMembers++; return 0; } else { return 1; } } else { return 1; } } /* * Remove item from list. */ PUBLIC int list_sub(int p, enum ListWhich l, char *s) { List *gl; if ((gl = list_find(p, l)) != NULL) { int i, found = -1; for (i = 0; i < gl->numMembers; i++) { if (!strcasecmp(s, gl->member[i])) { found = i; break; } } if (found == -1) return 1; rfree(gl->member[found]); for (i = found; i < (gl->numMembers - 1); i++) gl->member[i] = gl->member[i + 1]; gl->numMembers--; return 0; } return 1; } /* pretty cheesy: print each member of a list, 1 per line */ PUBLIC void list_print(FILE *fp, int p, enum ListWhich l) { List *gl; if ((gl = list_find(p, l)) != NULL) { for (int i = 0; i < gl->numMembers; i++) fprintf(fp, "%s\n", gl->member[i]); } } /* * Return size of a list. */ PUBLIC int list_size(int p, enum ListWhich l) { List *gl; if ((gl = list_find(p, l)) != NULL) return (gl->numMembers); return 0; } /* * Find list by name * (doesn't have to be the whole name) */ PRIVATE List * list_findpartial(int p, char *which, int gonnado) { List *gl; int foundit, slen; foundit = -1; slen = strlen(which); for (int i = 0; ListArray[i].name != NULL; i++) { if (!strncasecmp(ListArray[i].name, which, slen)) { if (foundit == -1) foundit = i; else return NULL; // ambiguous } } if (foundit != -1) { int rights = ListArray[foundit].rights; int youlose = 0; switch (rights) { // check rights case P_HEAD: if (gonnado && !player_ishead(p)) youlose = 1; break; case P_GOD: if ((gonnado && parray[p].adminLevel < ADMIN_GOD) || (!gonnado && parray[p].adminLevel < ADMIN_ADMIN)) youlose = 1; break; case P_ADMIN: if (parray[p].adminLevel < ADMIN_ADMIN) youlose = 1; break; case P_PUBLIC: if (gonnado && (parray[p].adminLevel < ADMIN_ADMIN)) youlose = 1; break; } if (youlose) { pprintf(p, "\"%s\" is not an appropriate list name or " "you have insufficient rights.\n", which); return NULL; } gl = list_find(p, foundit); } else { pprintf(p, "\"%s\" does not match any list name.\n", which); return NULL; } return gl; } /* * See if something is in a list */ PUBLIC int in_list(int p, enum ListWhich which, char *member) { List *gl; int filterList = (which == L_FILTER); if ((gl = list_find(p, which)) == NULL || member == NULL) return 0; for (int i = 0; i < gl->numMembers; i++) { if (filterList) { if (!strncasecmp(member, gl->member[i], strlen(gl->member[i]))) return 1; } else { if (!strcasecmp(member, gl->member[i])) return 1; } } return 0; } /* * Add or subtract something to/from a list. */ PUBLIC int list_addsub(int p, char *list, char *who, int addsub) { List *gl; char *listname, *member; char *yourthe, *addrem; int p1, connected, loadme, personal, ch; if ((gl = list_findpartial(p, list, addsub)) == NULL) return COM_OK; personal = (ListArray[gl->which].rights == P_PERSONAL); loadme = (gl->which != L_FILTER && gl->which != L_REMOVEDCOM && gl->which != L_CHANNEL); listname = ListArray[gl->which].name; yourthe = (personal ? "your" : "the"); addrem = (addsub == 1 ? "added to" : "removed from"); if (loadme) { if (!FindPlayer(p, who, &p1, &connected)) { if (addsub == 1) return COM_OK; member = who; // allow sub removed/renamed player loadme = 0; } else member = parray[p1].name; } else { member = who; } if (addsub == 1) { // add to list if (gl->which == L_CHANNEL) { if (sscanf(who, "%d", &ch) == 1) { if (!in_list(p, L_ADMIN, parray[p].name) && ch == 0) { pprintf(p, "Only admins may join " "channel 0.\n"); return COM_OK; } if (ch > (MAX_CHANNELS - 1)) { pprintf(p, "The maximum channel " "number is %d.\n", (MAX_CHANNELS - 1)); return COM_OK; } } else { pprintf(p, "Your channel to add must be a " "number between 0 and %d.\n", (MAX_CHANNELS - 1)); return COM_OK; } } if (in_list(p, gl->which, member)) { pprintf(p, "[%s] is already on %s %s list.\n", member, yourthe, listname); if (loadme && !connected) player_remove(p1); return COM_OK; } if (list_add(p, gl->which, member)) { pprintf(p, "Sorry, %s %s list is full.\n", yourthe, listname); if (loadme && !connected) player_remove(p1); return COM_OK; } } else if (addsub == 2) { // subtract from list if (!in_list(p, gl->which, member)) { pprintf(p, "[%s] is not in %s %s list.\n", member, yourthe, listname); if (loadme && !connected) player_remove(p1); return COM_OK; } list_sub(p, gl->which, member); } pprintf(p, "[%s] %s %s %s list.\n", member, addrem, yourthe, listname); if (!personal) { FILE *fp; char filename[MAX_FILENAME_SIZE]; switch (gl->which) { case L_MUZZLE: case L_CMUZZLE: case L_ABUSER: case L_BAN: pprintf(p, "Please leave a comment to explain why %s " "was %s the %s list.\n", member, addrem, listname); pcommand(p, "addcomment %s %s %s list.\n", member, addrem, listname); break; case L_COMPUTER: if (parray[p1].b_stats.rating > 0) { UpdateRank(TYPE_BLITZ, member, &parray[p1].b_stats, member); } if (parray[p1].s_stats.rating > 0) { UpdateRank(TYPE_STAND, member, &parray[p1].s_stats, member); } if (parray[p1].w_stats.rating > 0) { UpdateRank(TYPE_WILD, member, &parray[p1].w_stats, member); } break; case L_ADMIN: if (addsub == 1) { // adding to list parray[p1].adminLevel = 10; pprintf(p, "%s has been given an admin level " "of 10 - change with asetadmin.\n", member); } else { parray[p1].adminLevel = 0; } break; case L_FILTER: pprintf(p, "Please leave a message for filter to " "explain why site %s was %s filter list.\n", member, addrem); break; case L_REMOVEDCOM: pprintf(p, "Please leave a message on anews to explain " "why %s was %s removedcom list.\n", member, addrem); break; default: break; } if (loadme && connected) { pprintf_prompt(p1, "You have been %s the %s list " "by %s.\n", addrem, listname, parray[p].name); } sprintf(filename, "%s/%s", lists_dir, listname); if ((fp = fopen(filename, "w")) == NULL) { fprintf(stderr, "Couldn't save %s list.\n", listname); } else { for (int i = 0; i < gl->numMembers; i++) fprintf(fp, "%s\n", gl->member[i]); fclose(fp); } } if (loadme || gl->which == L_ADMIN) player_save(p1); if (loadme && !connected) player_remove(p1); return COM_OK; } PUBLIC int com_addlist(int p, param_list param) { return list_addsub(p, param[0].val.word, param[1].val.word, 1); } PUBLIC int com_sublist(int p, param_list param) { return list_addsub(p, param[0].val.word, param[1].val.word, 2); } PUBLIC int com_showlist(int p, param_list param) { List *gl; char *rightnames[] = { "EDIT HEAD, READ ADMINS", "EDIT GODS, READ ADMINS", "READ/WRITE ADMINS", "PUBLIC", "PERSONAL" }; int i, rights; if (param[0].type == 0) { // Show all lists pprintf(p, "Lists:\n\n"); for (i = 0; ListArray[i].name != NULL; i++) { if ((rights = ListArray[i].rights) > P_ADMIN || parray[p].adminLevel >= ADMIN_ADMIN) { pprintf(p, "%-20s is %s\n", ListArray[i].name, rightnames[rights]); } } } else { // find match in index if ((gl = list_findpartial(p, param[0].val.word, 0)) == NULL) return COM_OK; { // display the list multicol *m = multicol_start(gl->numMembers); pprintf(p, "-- %s list: %d %s --", ListArray[gl->which].name, gl->numMembers, ((!strcmp(ListArray[gl->which].name, "filter")) ? "ips" : (!strcmp(ListArray[gl->which].name, "removedcom")) ? "commands" : (!strcmp(ListArray[gl->which].name, "channel")) ? "channels" : "names")); for (i = 0; i < gl->numMembers; i++) multicol_store_sorted(m, gl->member[i]); multicol_pprint(m, p, 78, 2); multicol_end(m); } } return COM_OK; } PUBLIC int list_channels(int p, int p1) { List *gl; if ((gl = list_findpartial(p1, "channel", 0)) == NULL) return 1; if (gl->numMembers == 0) return 1; { multicol *m = multicol_start(gl->numMembers); for (int i = 0; i < gl->numMembers; i++) multicol_store_sorted(m, gl->member[i]); multicol_pprint(m, p, 78, 1); multicol_end(m); } return 0; } /* * Free the memory used by a list. */ PUBLIC void list_free(List *gl) { List *temp; while (gl) { for (int i = 0; i < gl->numMembers; i++) strfree(gl->member[i]); temp = gl->next; rfree(gl); gl = temp; } } PUBLIC int titled_player(int p, char *name) { if (in_list(p, L_FM, name) || in_list(p, L_IM, name) || in_list(p, L_GM, name)) return 1; return 0; }