Check-in Lasker-2.2.3 tar ball from samba.org
[capablanca.git] / lasker-2.2.3 / src / lists.c
1 /*
2    This program is free software; you can redistribute it and/or modify
3    it under the terms of the GNU General Public License as published by
4    the Free Software Foundation; either version 2 of the License, or
5    (at your option) any later version.
6    
7    This program is distributed in the hope that it will be useful,
8    but WITHOUT ANY WARRANTY; without even the implied warranty of
9    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
10    GNU General Public License for more details.
11    
12    You should have received a copy of the GNU General Public License
13    along with this program; if not, write to the Free Software
14    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
15 */
16
17 /* lists.c
18  *  New global lists code
19  *
20  *
21  *  Added by Shaney, 29 May 1995  :)
22  *
23 */
24
25 #include "includes.h"
26
27 static struct List *firstGlobalList = NULL;
28
29 static ListTable ListArray[] =
30 {{P_HEAD, "admin"},
31  {P_GOD, "removedcom"},
32  {P_ADMIN, "filter"},
33  {P_ADMIN, "ban"},
34  {P_ADMIN, "abuser"},
35  {P_ADMIN, "muzzle"},
36  {P_ADMIN, "cmuzzle"},
37  {P_ADMIN, "c1muzzle"}, /* possible FICS trouble spots */
38  {P_ADMIN, "c24muzzle"}, /* would prefer two param addlist - DAV */
39  {P_ADMIN, "c46muzzle"}, /* is a temp solution */
40  {P_ADMIN, "c49muzzle"},
41  {P_ADMIN, "c50muzzle"},
42  {P_ADMIN, "c51muzzle"},
43  {P_PUBLIC, "fm"},
44  {P_PUBLIC, "im"},
45  {P_PUBLIC, "gm"},
46  {P_PUBLIC, "wgm"},
47  {P_PUBLIC, "blind"},
48  {P_PUBLIC, "teams"},
49  {P_PUBLIC, "computer"},
50  {P_PUBLIC, "td"},
51  {P_PERSONAL, "censor"},
52  {P_PERSONAL, "gnotify"},
53  {P_PERSONAL, "noplay"},
54  {P_PERSONAL, "notify"},
55  {P_PERSONAL, "channel"},
56  {P_PERSONAL, "follow"},
57  {P_PERSONAL, "remote"},
58  {0, NULL}};
59
60 /* free up memory used by lists */
61 void lists_close(void)
62 {
63         struct List *l;
64
65         for (l=firstGlobalList; l; ) {
66                 struct List *next = l->next;
67                 int i;
68                 for (i=0;i<l->numMembers;i++) {
69                         FREE(l->m_member[i]);
70                 }
71                 FREE(l->m_member);
72                 l->numMembers = 0;
73                 free(l);
74                 l = next;
75         }
76
77         firstGlobalList = NULL;
78 }
79
80
81 /* find a list.  does not load from disk */
82 static struct List *list_find1(int p, enum ListWhich l)
83 {
84         struct player *pp = &player_globals.parray[p];
85         struct List *prev, *tempList, **starter;
86         int personal;
87         
88         personal = ListArray[l].rights == P_PERSONAL;
89         starter = personal ? &(pp->lists) : &firstGlobalList;
90
91         for (prev = NULL, tempList = *starter; tempList != NULL; tempList = tempList->next) {
92                 if (l == tempList->which) {
93                         if (prev != NULL) {
94                                 prev->next = tempList->next;
95                                 tempList->next = *starter;
96                                 *starter = tempList;
97                         }
98                         return tempList;
99                 }
100                 if (tempList->which >= L_LASTLIST) {
101                         /* THIS SHOULD NEVER HAPPEN!  But has been in personal list: bugs! */
102                         d_printf( "fics: ERROR!! in lists code\n");
103                         abort();
104                 }
105                 prev = tempList;
106         }
107
108         return NULL;
109 }
110
111 /* find a list.  loads from disk if not in memory. */
112 static struct List *list_find(int p, enum ListWhich l)
113 {
114         struct List *lst;
115         FILE *fp;
116         char listmember[100];
117         int count=0;
118
119         lst = list_find1(p, l);
120         if (lst) {
121                 return lst;
122         }
123
124         /* create the base list */
125         list_add(p, l, NULL);
126
127         if (ListArray[l].rights == P_PERSONAL) {
128                 list_find1(p, l);
129         }
130
131         fp = fopen_p("%s/%s", "r", LISTS_DIR, ListArray[l].name);
132         if (!fp) {
133                 return NULL;
134         }
135         while (!feof(fp)) {
136                 if (fgets(listmember, sizeof(listmember), fp) != NULL) {
137                         if (list_add(p, l, listmember) == 0) {
138                                 count++;
139                         }
140                 }
141         }
142         fclose(fp);
143         
144         /* we've added some, retry */
145         if (count) {
146                 return list_find1(p, l);
147         }
148         
149         return NULL;
150 }
151
152
153 /* add item to list */
154 int list_add(int p, enum ListWhich l, const char *s)
155 {
156         struct player *pp = &player_globals.parray[p];
157         struct List *gl = list_find1(p, l);
158
159         if (!gl) {
160                 gl = calloc(1, sizeof(*gl));
161                 gl->which = l;
162                 if (ListArray[l].rights == P_PERSONAL) {
163                         gl->next = pp->lists;
164                         pp->lists = gl;
165                 } else {
166                         gl->next = firstGlobalList;
167                         firstGlobalList = gl;
168                 }
169         }
170
171         if (!s) return 0;
172
173         if (ListArray[l].rights == P_PERSONAL &&
174             gl->numMembers >= config_get_int("MAX_USER_LIST_SIZE", DEFAULT_MAX_USER_LIST_SIZE)) {
175                 return 1;
176         }
177
178         while (isspace(*s)) s++;
179             
180         gl->m_member = (char **)realloc(gl->m_member, sizeof(char *) * (gl->numMembers+1));
181         gl->m_member[gl->numMembers] = strndup(s, strcspn(s, " \t\r\n"));
182         gl->numMembers++;
183
184         return 0;
185 }
186
187 /* remove item from list */
188 static int list_sub(int p, enum ListWhich l, char *s)
189 {
190         struct List *gl = list_find(p, l);
191         int i;
192
193         if (!gl) {
194                 return 1;
195         }
196         for (i = 0; i < gl->numMembers; i++) {
197                 if (!strcasecmp(s, gl->m_member[i])) {
198                         break;
199                 }
200         }
201         if (i == gl->numMembers) {
202                 return 1;
203         }
204
205         FREE(gl->m_member[i]);
206
207         for (; i < (gl->numMembers - 1); i++) {
208                 gl->m_member[i] = gl->m_member[i+1];
209         }
210
211         gl->numMembers--;
212         gl->m_member = (char **)realloc(gl->m_member, sizeof(char *) * gl->numMembers);
213         return 0;
214 }
215
216 /* find list by name, doesn't have to be the whole name */
217 struct List *list_findpartial(int p, char *which, int gonnado)
218 {
219   struct player *pp = &player_globals.parray[p];
220   struct List *gl;
221   int i, foundit, slen;
222
223   slen = strlen(which);
224   for (i = 0, foundit = -1; ListArray[i].name != NULL; i++) {
225     if (!strncasecmp(ListArray[i].name, which, slen)) {
226       if (foundit == -1)
227         foundit = i;
228       else
229         return NULL;            /* ambiguous */
230     }
231   }
232
233   if (foundit != -1) {
234     int rights = ListArray[foundit].rights;
235     int youlose = 0;
236
237     switch (rights) {           /* check rights */
238     case P_HEAD:
239       if (gonnado && !player_ishead(p))
240         youlose = 1;
241       break;
242     case P_GOD:
243       if ((gonnado && (pp->adminLevel < ADMIN_GOD)) ||
244           (!gonnado && (pp->adminLevel < ADMIN_ADMIN)))
245         youlose = 1;
246       break;
247     case P_ADMIN:
248       if (pp->adminLevel < ADMIN_ADMIN)
249         youlose = 1;
250       break;
251     case P_PUBLIC:
252       if (gonnado && (pp->adminLevel < ADMIN_ADMIN))
253         youlose = 1;
254       break;
255     }
256     if (youlose) {
257       pprintf(p, "\"%s\" is not an appropriate list name or you have insufficient rights.\n", which);
258       return NULL;
259     }
260     gl = list_find(p, foundit);
261   } else {
262     pprintf(p, "\"%s\" does not match any list name.\n", which);
263     return NULL;
264   }
265   return gl;
266 }
267
268 /* see if something is in a list */
269 int in_list(int p, enum ListWhich which, char *member)
270 {
271         struct List *gl;
272         int i;
273         int filterList = (which == L_FILTER);
274
275         gl = list_find(p, which);
276         if ((gl == NULL) || (member == NULL))
277                 return 0;
278         for (i = 0; i < gl->numMembers; i++) {
279                 if (filterList) {
280                         if (!strncasecmp(member, gl->m_member[i], strlen(gl->m_member[i])))
281                                 return 1;
282                 } else {
283                         if (!strcasecmp(member, gl->m_member[i]))
284                                 return 1;
285                 }
286         }
287         return 0;
288 }
289
290 /* add or subtract something to/from a list */
291 int list_addsub(int p, char* list, char* who, int addsub)
292 {
293   struct player *pp = &player_globals.parray[p];
294   int p1, connected, loadme, personal, ch;
295   char *listname, *member, junkChar;
296   struct List *gl;
297   char *yourthe, *addrem;
298
299   gl = list_findpartial(p, list, addsub);
300   if (!gl)
301     return COM_OK;
302
303   personal = ListArray[gl->which].rights == P_PERSONAL;
304   loadme = (gl->which != L_FILTER) && (gl->which != L_REMOVEDCOM) && (gl->which != L_CHANNEL);
305   listname = ListArray[gl->which].name;
306   yourthe = personal ? "your" : "the";
307   addrem = (addsub == 1) ? "added to" : "removed from";
308
309   if (loadme) {
310     if (!FindPlayer(p, who, &p1, &connected)) {
311       if (addsub == 1)
312         return COM_OK;
313       member = who;             /* allow sub removed/renamed player */
314       loadme = 0;
315     } else
316       member = player_globals.parray[p1].name;
317   } else {
318     member = who;
319   }
320
321   if (addsub == 1) {            /* add to list */
322
323    if (gl->which == L_CHANNEL) {
324
325      if (sscanf (who,"%d%c",&ch, &junkChar) == 1 && ch >= 0 && ch < 255) {
326        if ((ch == 0) && (!in_list(p,L_ADMIN,pp->name))) {
327          pprintf(p, "Only admins may join channel 0.\n");
328          return COM_OK;
329        }
330      } else {
331          pprintf (p,"The channel to add must be a number between 0 and %d.\n",MAX_CHANNELS - 1);
332          return COM_OK;
333         }
334   }      
335   if (in_list(p, gl->which, member)) {
336      pprintf(p, "[%s] is already on %s %s list.\n", member, yourthe, listname);
337      if (loadme && !connected)
338        player_remove(p1);
339      return COM_OK;
340     }
341     if (list_add(p, gl->which, member)) {
342       pprintf(p, "Sorry, %s %s list is full.\n", yourthe, listname);
343       if (loadme && !connected)
344         player_remove(p1);
345       return COM_OK;
346     }
347   } else if (addsub == 2) {     /* subtract from list */
348     if (!in_list(p, gl->which, member)) {
349       pprintf(p, "[%s] is not in %s %s list.\n", member, yourthe, listname);
350       if (loadme && !connected)
351         player_remove(p1);
352       return COM_OK;
353     }
354     list_sub(p, gl->which, member);
355   }
356   pprintf(p, "[%s] %s %s %s list.\n", member, addrem, yourthe, listname);
357
358   if (!personal) {
359     FILE *fp;
360     char filename[MAX_FILENAME_SIZE];
361
362     switch (gl->which) {
363     case L_MUZZLE:
364     case L_CMUZZLE:
365     case L_C1MUZZLE:
366     case L_C24MUZZLE:
367     case L_C46MUZZLE:
368     case L_C49MUZZLE:
369     case L_C50MUZZLE:
370     case L_C51MUZZLE:
371     case L_ABUSER:
372     case L_BAN:
373       pprintf(p, "Please leave a comment to explain why %s was %s the %s list.\n", member, addrem, listname);
374       pcommand(p, "addcomment %s %s %s list.\n", member, addrem, listname);
375       break;
376     case L_COMPUTER:
377       if (player_globals.parray[p1].b_stats.rating > 0)
378         UpdateRank(TYPE_BLITZ, member, &player_globals.parray[p1].b_stats, member);
379       if (player_globals.parray[p1].s_stats.rating > 0)
380         UpdateRank(TYPE_STAND, member, &player_globals.parray[p1].s_stats, member);
381       if (player_globals.parray[p1].w_stats.rating > 0)
382         UpdateRank(TYPE_WILD, member, &player_globals.parray[p1].w_stats, member);
383       break;
384     case L_ADMIN:
385       if (addsub == 1) {        /* adding to list */
386         player_globals.parray[p1].adminLevel = 10;
387         pprintf(p, "%s has been given an admin level of 10 - change with asetadmin.\n", member);
388       } else {
389         player_globals.parray[p1].adminLevel = 0;
390       }
391       break;
392     case L_FILTER:
393     case L_REMOVEDCOM:
394     default:
395       break;
396     }
397
398     if (loadme && connected)
399         pprintf_prompt(p1, "You have been %s the %s list by %s.\n",
400                                     addrem, listname, pp->name);
401
402     sprintf(filename, "%s/%s", LISTS_DIR, listname);
403     fp = fopen_s(filename, "w");
404     if (fp == NULL) {
405       d_printf( "Couldn't save %s list.\n", listname);
406     } else {
407       int i;
408       for (i = 0; i < gl->numMembers; i++)
409         fprintf(fp, "%s\n", gl->m_member[i]);
410       fclose(fp);
411     }
412   }
413   if (loadme || (gl->which == L_ADMIN)) {
414     player_save(p1);
415   }
416   if (loadme && !connected) {
417     player_remove(p1);
418   }
419   return COM_OK;
420 }
421
422 int com_addlist(int p, param_list param)
423 {
424         return list_addsub(p, param[0].val.word, param[1].val.word, 1);
425 }
426
427 int com_sublist(int p,param_list param)
428 {
429         return list_addsub(p, param[0].val.word, param[1].val.word, 2);
430 }
431
432 /* toggle being in a list */
433 int com_togglelist(int p,param_list param)
434 {
435         char *list = param[0].val.word;
436         char *val = param[1].val.word;
437         struct List *gl = list_findpartial(p, list, 0);
438         if (!gl) {
439                 pprintf(p, "'%s' does not match any list name.\n", list);
440                 return COM_FAILED;
441         }
442         if (in_list(p, gl->which, val)) {
443                 return com_sublist(p, param);
444         }
445         return com_addlist(p, param);
446 }
447
448 int com_showlist(int p, param_list param)
449 {
450   struct player *pp = &player_globals.parray[p];
451   struct List *gl;
452   int i, rights;
453
454   char *rightnames[] = {"EDIT HEAD, READ ADMINS", "EDIT GODS, READ ADMINS", "READ/WRITE ADMINS", "PUBLIC", "PERSONAL"};
455
456   if (param[0].type == 0) {     /* Show all lists */
457     pprintf(p, "Lists:\n\n");
458     for (i = 0; ListArray[i].name != NULL; i++) {
459       rights = ListArray[i].rights;
460       if ((rights > P_ADMIN) || (pp->adminLevel >= ADMIN_ADMIN))
461         pprintf(p, "%-20s is %s\n", ListArray[i].name, rightnames[rights]);
462     }
463   } else {                      /* find match in index */
464     gl = list_findpartial(p, param[0].val.word, 0);
465     if (!gl) {
466       return COM_OK;
467     }
468     rights = ListArray[gl->which].rights;
469     /* display the list */
470     {
471       multicol *m = multicol_start(gl->numMembers);
472
473       pprintf(p, "-- %s list: %d %s --", ListArray[gl->which].name,
474       gl->numMembers,
475       ((!(strcmp(ListArray[gl->which].name,"filter"))) ? "ips" : (!(strcmp(ListArray[gl->which].name,"removedcom"))) ? "commands" : (!(strcmp(ListArray[gl->which].name,"channel"))) ? "channels" : "names"));
476       for (i = 0; i < gl->numMembers; i++)
477         multicol_store_sorted(m, gl->m_member[i]);
478       multicol_pprint(m, p, pp->d_width, 2);
479       multicol_end(m);
480     }
481   }
482   return COM_OK;
483 }
484
485 int list_channels(int p,int p1)
486 {
487   struct player *pp = &player_globals.parray[p];
488   struct List *gl;
489   int i, rights;
490
491     gl = list_findpartial(p1, "channel", 0);
492     if (!gl) {
493       return 1;
494     }
495     rights = ListArray[gl->which].rights;
496     /* display the list */
497     if (gl->numMembers == 0)
498       return 1;
499     {
500       multicol *m = multicol_start(gl->numMembers);
501
502       for (i = 0; i < gl->numMembers; i++)
503         multicol_store_sorted(m, gl->m_member[i]);
504       multicol_pprint(m, p, pp->d_width, 1);
505       multicol_end(m);
506     }
507   return 0;
508 }
509
510 /* free the memory used by a list */
511 void list_free(struct List * gl)
512 {
513         int i;
514         struct List *temp;
515
516         while (gl) {
517                 for (i = 0; i < gl->numMembers; i++) {
518                         FREE(gl->m_member[i]);
519                 }
520                 FREE(gl->m_member);
521                 temp = gl->next;
522                 free(gl);
523                 gl = temp;
524         }
525 }
526
527 /* check lists for validity - pure paranoia */
528 void lists_validate(int p)
529 {
530         struct player *pp = &player_globals.parray[p];
531         struct List *gl;
532
533         for (gl=pp->lists; gl; gl=gl->next) {
534                 if (gl->numMembers && !gl->m_member) {
535                         gl->numMembers = 0;
536                 }
537         }
538 }
539
540
541 int titled_player(int p,char* name)
542 {
543         if ((in_list(p, L_FM, name)) || 
544             (in_list(p, L_IM, name)) || 
545             (in_list(p, L_GM, name)) || 
546             (in_list(p, L_WGM, name))) {
547                 return 1;
548         }
549         return 0;
550 }
551