Check-in Lasker-2.2.3 tar ball from samba.org
[capablanca.git] / lasker-2.2.3 / src / adminproc.c
1 /*
2    Copyright (c) 1993 Richard V. Nash.
3    Copyright (c) 2000 Dan Papasian
4    Copyright (C) Andrew Tridgell 2002
5    
6    This program is free software; you can redistribute it and/or modify
7    it under the terms of the GNU General Public License as published by
8    the Free Software Foundation; either version 2 of the License, or
9    (at your option) any later version.
10    
11    This program is distributed in the hope that it will be useful,
12    but WITHOUT ANY WARRANTY; without even the implied warranty of
13    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14    GNU General Public License for more details.
15    
16    You should have received a copy of the GNU General Public License
17    along with this program; if not, write to the Free Software
18    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
19 */
20
21 /**
22  *
23  *     adminproc.c - All administrative commands and related functions
24  *
25  */
26
27 #include "includes.h"
28
29 #define PASSLEN 4
30
31
32 /*
33   check that a player has sufficient rights for an operation
34 */
35 int check_admin(int p, unsigned level)
36 {
37         struct player *pp = &player_globals.parray[p];
38         /* gods and head admins always get what they want */
39         if (pp->adminLevel >= ADMIN_GOD || 
40             player_ishead(p)) {
41                 return 1;
42         }
43         return pp->adminLevel >= level;
44 }
45
46 /*
47   check that player p1 can do an admin operation on player p2
48 */
49 static int check_admin2(int p1, int p2)
50 {
51         return check_admin(p1, player_globals.parray[p2].adminLevel+1);
52 }
53
54 /*
55  * adjudicate
56  *
57  * Usage: adjudicate white_player black_player result
58  *
59  *   Adjudicates a saved (stored) game between white_player and black_player.
60  *   The result is one of: abort, draw, white, black.  "Abort" cancels the game
61  *   (no win, loss or draw), "white" gives white_player the win, "black" gives
62  *   black_player the win, and "draw" gives a draw.
63  */
64 int com_adjudicate(int p, param_list param)
65 {
66   int wp, wconnected, bp, bconnected, g, inprogress, confused = 0;
67
68   if (!FindPlayer(p, param[0].val.word, &wp, &wconnected))
69     return COM_OK;
70   if (!FindPlayer(p, param[1].val.word, &bp, &bconnected)) {
71     if (!wconnected) 
72      player_remove(wp);
73     return COM_OK;
74   }
75
76   inprogress = ((player_globals.parray[wp].game >=0) &&(player_globals.parray[wp].opponent == bp));
77
78   if (inprogress) {
79     g = player_globals.parray[wp].game;
80   } else {
81     g = game_new();
82     if (game_read(g, wp, bp) < 0) {
83       confused = 1;
84       pprintf(p, "There is no stored game %s vs. %s\n", player_globals.parray[wp].name, player_globals.parray[bp].name);
85     } else {
86       game_globals.garray[g].white = wp;
87       game_globals.garray[g].black = bp;
88     }
89   }
90   if (!confused) {
91     if (strstr("abort", param[2].val.word) != NULL) {
92       game_ended(g, WHITE, END_ADJABORT);
93
94       pcommand(p, "message %s Your game \"%s vs. %s\" has been aborted.",
95                player_globals.parray[wp].name, player_globals.parray[wp].name, player_globals.parray[bp].name);
96
97       pcommand(p, "message %s Your game \"%s vs. %s\" has been aborted.",
98                player_globals.parray[bp].name, player_globals.parray[wp].name, player_globals.parray[bp].name);
99     } else if (strstr("draw", param[2].val.word) != NULL) {
100       game_ended(g, WHITE, END_ADJDRAW);
101
102       pcommand(p, "message %s Your game \"%s vs. %s\" has been adjudicated "
103                "as a draw", player_globals.parray[wp].name, player_globals.parray[wp].name, player_globals.parray[bp].name);
104
105       pcommand(p, "message %s Your game \"%s vs. %s\" has been adjudicated "
106                "as a draw", player_globals.parray[bp].name, player_globals.parray[wp].name, player_globals.parray[bp].name);
107     } else if (strstr("white", param[2].val.word) != NULL) {
108       game_ended(g, WHITE, END_ADJWIN);
109
110       pcommand(p, "message %s Your game \"%s vs. %s\" has been adjudicated "
111                "as a win", player_globals.parray[wp].name, player_globals.parray[wp].name, player_globals.parray[bp].name);
112
113       pcommand(p, "message %s Your game \"%s vs. %s\" has been adjudicated "
114                "as a loss", player_globals.parray[bp].name, player_globals.parray[wp].name, player_globals.parray[bp].name);
115     } else if (strstr("black", param[2].val.word) != NULL) {
116       game_ended(g, BLACK, END_ADJWIN);
117       pcommand(p, "message %s Your game \"%s vs. %s\" has been adjudicated "
118                "as a loss", player_globals.parray[wp].name, player_globals.parray[wp].name, player_globals.parray[bp].name);
119
120       pcommand(p, "message %s Your game \"%s vs. %s\" has been adjudicated "
121                "as a win", player_globals.parray[bp].name, player_globals.parray[wp].name, player_globals.parray[bp].name);
122     } else {
123       confused = 1;
124       pprintf(p, "Result must be one of: abort draw white black\n");
125     }
126   }
127   if (!confused) {
128     pprintf(p, "Game adjudicated.\n");
129     if (!inprogress) {
130       game_delete(wp, bp);
131     } else {
132       return (COM_OK);
133     }
134   }
135   game_remove(g);
136   if (!wconnected)
137     player_remove(wp);
138   if (!bconnected)
139     player_remove(bp);
140   return COM_OK;
141 }
142
143
144 /*
145  * remplayer
146  *
147  * Usage:  remplayer name
148  *
149  *   Removes an account.  A copy of its files are saved under .rem.* which can
150  *   be found in the appropriate directory (useful in case of an accident).
151  *
152  *   The account's details, messages, games and logons are all saved as
153  *   'zombie' files.  These zombie accounts are not listed in handles or
154  *   totals.
155  */
156 int com_remplayer(int p, param_list param)
157 {
158         char *player = param[0].val.word;
159         char playerlower[MAX_LOGIN_NAME];
160         int p1, lookup;
161
162         strcpy(playerlower, player);
163         stolower(playerlower);
164         p1 = player_new();
165         lookup = player_read(p1, playerlower);
166         if (!lookup) {
167                 if (!check_admin2(p, p1)) {
168                         pprintf(p, "You can't remove an admin with a level higher than or equal to yourself.\n");
169                         player_remove(p1);
170                         return COM_OK;
171                 }
172         }
173         player_remove(p1);
174         if (lookup) {
175                 pprintf(p, "No player by the name %s is registered.\n", player);
176                 return COM_OK;
177         }
178         if (player_find_bylogin(playerlower) >= 0) {
179                 pprintf(p, "A player by that name is logged in.\n");
180                 return COM_OK;
181         }
182         if (!player_kill(playerlower)) {
183                 pprintf(p, "Player %s removed.\n", player);
184                 UpdateRank(TYPE_BLITZ, NULL, NULL, player);
185                 UpdateRank(TYPE_STAND, NULL, NULL, player);
186                 UpdateRank(TYPE_WILD, NULL, NULL, player);
187         } else {
188                 pprintf(p, "Remplayer failed.\n");
189         }
190         return COM_OK;
191 }
192
193 /*
194  * raisedead
195  *
196  * Usage:  raisedead oldname [newname]
197  *
198  *   Restores an account that has been previously removed using "remplayer".
199  *   The zombie files from which it came are removed.  Under most
200  *   circumstances, you restore the account to the same handle it had
201  *   before (oldname).  However, in some circumstances you may need to
202  *   restore the account to a different handle, in which case you include
203  *   "newname" as the new handle.  After "raisedead", you may need to use the
204  *   "asetpasswd" command to get the player started again as a registered
205  *   user, especially if the account had been locked
206  *   by setting the password to *.
207  */
208 int com_raisedead(int p, param_list param)
209 {
210   char *player = param[0].val.word;
211   char *newplayer = (param[1].type == TYPE_NULL  ?  player  :  param[1].val.word);
212   char playerlower[MAX_LOGIN_NAME], newplayerlower[MAX_LOGIN_NAME];
213   char plrFile[MAX_FILENAME_SIZE];
214
215   int p2, lookup;
216
217   strcpy(playerlower, player);
218   stolower(playerlower);
219   strcpy(newplayerlower, newplayer);
220   stolower(newplayerlower);
221
222   /* First make sure we have a player to raise. */
223   sprintf (plrFile, "%s/%c/.rem.%s", PLAYER_DIR, playerlower[0], playerlower);
224   if (!file_exists (plrFile)) {
225     pprintf(p, "No deleted player %s.\n", player);
226     return COM_OK;
227   }
228
229   /* Now check for registered player. */
230   sprintf (plrFile, "%s/%c/%s", PLAYER_DIR, newplayerlower[0], newplayerlower);
231   if (file_exists (plrFile)) {
232     pprintf(p, "A player named %s is already registered.\n", newplayerlower);
233     pprintf(p, "Obtain a new handle for the dead person.\n");
234     pprintf(p, "Then use raisedead [oldname] [newname].\n");
235     return COM_OK;
236   }
237
238   /* Don't raise over a logged in user. */
239   if (player_find_bylogin(newplayerlower) >= 0) {
240     pprintf(p, "A player named %s is logged in.\n", newplayerlower);
241     pprintf(p, "Can't raise until that person leaves.\n");
242     return COM_OK;
243   }
244
245   /* OK, ready to go. */
246   if (!player_reincarn(playerlower, newplayerlower)) {
247     if (param[1].type == TYPE_WORD)
248       pprintf(p, "Player %s reincarnated to %s.\n", player, newplayer);
249     else
250       pprintf(p, "Player %s raised.\n", player);
251     p2 = player_new();
252     if (!(lookup = player_read(p2, newplayerlower))) {
253       if (param[1].type == TYPE_WORD) {
254         free(player_globals.parray[p2].name);
255         player_globals.parray[p2].name = strdup(newplayer);
256         player_save(p2);
257       }
258       if (player_globals.parray[p2].s_stats.rating > 0)
259         UpdateRank(TYPE_STAND, newplayer, &player_globals.parray[p2].s_stats, newplayer);
260       if (player_globals.parray[p2].b_stats.rating > 0)
261         UpdateRank(TYPE_BLITZ, newplayer, &player_globals.parray[p2].b_stats, newplayer);
262       if (player_globals.parray[p2].w_stats.rating > 0)
263         UpdateRank(TYPE_WILD, newplayer, &player_globals.parray[p2].w_stats, newplayer);
264     }
265     player_remove(p2);
266   } else {
267     pprintf(p, "Raisedead failed.\n");
268   }
269   return COM_OK;
270 #if 0
271  if (param[1].type == TYPE_NULL) {
272     if (!player_raise(playerlower)) {
273       pprintf(p, "Player %s raised from dead.\n", player);
274
275       p1 = player_new();
276       if (!(lookup = player_read(p1, playerlower))) {
277         if (player_globals.parray[p1].s_stats.rating > 0)
278           UpdateRank(TYPE_STAND, player, &player_globals.parray[p1].s_stats, player);
279         if (player_globals.parray[p1].b_stats.rating > 0)
280           UpdateRank(TYPE_BLITZ, player, &player_globals.parray[p1].b_stats, player);
281         if (player_globals.parray[p1].w_stats.rating > 0)
282           UpdateRank(TYPE_WILD, player, &player_globals.parray[p1].w_stats, player);
283       }
284       player_remove(p1);
285     } else {
286       pprintf(p, "Raisedead failed.\n");
287     }
288     return COM_OK;
289   } else {
290     if (player_find_bylogin(newplayerlower) >= 0) {
291       pprintf(p, "A player by the requested name is logged in.\n");
292       pprintf(p, "Can't reincarnate until they leave.\n");
293       return COM_OK;
294     }
295     p2 = player_new();
296     lookup = player_read(p2, newplayerlower);
297     player_remove(p2);
298     if (!lookup) {
299       pprintf(p, "A player by the name %s is already registered.\n", player);
300       pprintf(p, "Obtain another new handle for the dead person.\n");
301       return COM_OK;
302     }
303     if (!player_reincarn(playerlower, newplayerlower)) {
304       pprintf(p, "Player %s reincarnated to %s.\n", player, newplayer);
305       p2 = player_new();
306       if (!(lookup = player_read(p2, newplayerlower))) {
307         free(player_globals.parray[p2].name);
308         player_globals.parray[p2].name = strdup(newplayer);
309         player_save(p2);
310         if (player_globals.parray[p2].s_stats.rating > 0)
311           UpdateRank(TYPE_STAND, newplayer, &player_globals.parray[p2].s_stats, newplayer);
312         if (player_globals.parray[p2].b_stats.rating > 0)
313           UpdateRank(TYPE_BLITZ, newplayer, &player_globals.parray[p2].b_stats, newplayer);
314         if (player_globals.parray[p2].w_stats.rating > 0)
315           UpdateRank(TYPE_WILD, newplayer, &player_globals.parray[p2].w_stats, newplayer);
316       }
317       player_remove(p2);
318     } else {
319       pprintf(p, "Raisedead failed.\n");
320     }
321   }
322   return COM_OK;
323 #endif
324 }
325
326 /*
327  * addplayer
328  *
329  * Usage: addplayer playername emailaddress realname
330  *
331  *   Adds a local player to the server with the handle of "playername".  For
332  *   example:
333  *
334  *      addplayer Hawk u940456@daimi.aau.dk Henrik Gram
335  */
336 int com_addplayer(int p, param_list param)
337 {
338   char text[2048];
339   char *newplayer = param[0].val.word;
340   char *newname = param[2].val.string;
341   char *newemail = param[1].val.word;
342   char password[PASSLEN + 1];
343   char newplayerlower[MAX_LOGIN_NAME];
344   char salt[3];
345   int p1, lookup;
346   int i;
347
348   if (strlen(newplayer) >= MAX_LOGIN_NAME) {
349     pprintf(p, "Player name is too long\n");
350     return COM_OK;
351   }
352   if (strlen(newplayer) < 3) {
353     pprintf(p, "Player name is too short\n");
354     return COM_OK;
355   }
356   if (!alphastring(newplayer)) {
357     pprintf(p, "Illegal characters in player name. Only A-Za-z allowed.\n");
358     return COM_OK;
359   }
360   strcpy(newplayerlower, newplayer);
361   stolower(newplayerlower);
362   p1 = player_new();
363   lookup = player_read(p1, newplayerlower);
364   if (!lookup) {
365    pprintf(p, "A player by the name %s is already registered.\n", newplayerlower);
366    player_remove(p1); 
367    return COM_OK;
368   }
369   player_globals.parray[p1].name = strdup(newplayer);
370   player_globals.parray[p1].login = strdup(newplayerlower);
371   player_globals.parray[p1].fullName = strdup(newname);
372   player_globals.parray[p1].emailAddress = strdup(newemail);
373   if (strcmp(newemail, "none")) {
374     for (i = 0; i < PASSLEN; i++) {
375       password[i] = 'a' + random() % 26;
376     }
377     password[i] = '\0';
378     salt[0] = 'a' + random() % 26;
379     salt[1] = 'a' + random() % 26;
380     salt[2] = '\0';
381     player_globals.parray[p1].passwd = strdup(chessd_crypt(password, salt));
382   } else {
383     password[0] = '\0';
384     player_globals.parray[p1].passwd = strdup(password);
385   }
386   PFlagON(p1, PFLAG_REG);
387 /*  player_globals.parray[p1].network_player = 0; */
388   PFlagON(p1, PFLAG_RATED);
389   player_add_comment(p, p1, "Player added by addplayer.");
390   player_save(p1);
391   player_remove(p1);
392   pprintf(p, "Added: >%s< >%s< >%s< >%s<\n", newplayer, newname, newemail, password);
393   if (strcmp(newemail, "none")) {
394 /*
395     sprintf(text, "\nYou have been added as a local player.\n\nLogin Name: "
396             "%s\nFull Name: %s\nEmail Address: %s\nInitial Password: "
397             "%s\n\nIf any of this information is incorrect, please "
398             "contact the administrator\nto get it corrected.\n\n"
399         "Please write down your password.\n\nRegards,\n\nThe FICS admins\n",
400             newplayer, newname, newemail, password);
401 */
402
403    sprintf(text, "\nYour player account has been created.\n\n"
404    "Login Name: %s\nFull Name: %s\nEmail Address: %s\nInitial Password: %s\n\n"
405    "If any of this information is incorrect, please contact the administrator\n"
406    "to get it corrected.\n\n"
407    "You may change your password with the password command on the the server.\n"
408    "\nPlease be advised that if this is an unauthorized duplicate account for\n"
409    "you, by using it you take the risk of being banned from accessing this\n"
410    "chess server.\n\n"        
411    "Regards,\n\nThe FICS admins\n",
412         newplayer, newname, newemail, password);
413
414     mail_string_to_address(newemail, "FICS Account Created", text);
415     if ((p1 = player_find_part_login(newplayer)) >= 0) {
416       pprintf_prompt(p1, "\n\nYou are now registered! Confirmation together with\npassword is sent to your email address.\n\n");
417       player_read(p1, newplayer);
418       return COM_OK;
419     }
420     return COM_OK;
421   } else {
422     if ((p1 = player_find_part_login(newplayer)) >= 0) {
423       pprintf_prompt(p1, "\n\nYou are now registered! Your have NO password!\n\n");
424       player_read(p1, newplayer);
425       return COM_OK;
426     }
427   }
428   return COM_OK;
429 }
430
431 int com_pose(int p, param_list param)
432 {
433         int p1;
434
435         if ((p1 = player_find_part_login(param[0].val.word)) < 0) {
436                 pprintf(p, "%s is not logged in.\n", param[0].val.word);
437                 return COM_OK;
438         }
439         if (!check_admin2(p, p1)) {
440                 pprintf(p, "You can only pose as players below your adminlevel.\n");
441                 return COM_OK;
442         }
443         pprintf(p, "Command issued as %s\n", player_globals.parray[p1].name);
444         pcommand(p1, "%s\n", param[1].val.string);
445         return COM_OK;
446 }
447
448 /*
449  * asetv
450  *
451  * Usage: asetv user instructions
452  *
453  *   This command executes "set" instructions as if they had been made by the
454  *   user indicated.  For example, "asetv DAV shout 0" would set DAV's shout
455  *   variable to 0.
456  */
457 int com_asetv(int p, param_list param)
458 {
459         int p1;
460         
461         if ((p1 = player_find_part_login(param[0].val.word)) < 0) {
462                 pprintf(p, "%s is not logged in.\n", param[0].val.word);
463                 return COM_OK;
464         }
465         if (!check_admin2(p, p1)) {
466                 pprintf(p, "You can only aset players below your adminlevel.\n");
467                 return COM_OK;
468         }
469         pprintf(p, "Command issued as %s\n", player_globals.parray[p1].name);
470         pcommand(p1, "set %s\n", param[1].val.string);
471         return COM_OK;
472 }
473
474 /*
475  * announce
476  *
477  * Usage: announce message
478  *
479  *   Broadcasts your message to all logged on users.  Announcements reach all
480  *   users and cannot be censored in any way (such as by "set shout 0").
481  */
482 int com_announce(int p, param_list param)
483 {
484         struct player *pp = &player_globals.parray[p];
485         int p1;
486         int count = 0;
487         
488         if (!printablestring(param[0].val.string)) {
489                 pprintf(p, "Your message contains some unprintable character(s).\n");
490                 return COM_OK;
491         }
492         for (p1 = 0; p1 < player_globals.p_num; p1++) {
493                 if (p1 == p)
494                         continue;
495                 if (player_globals.parray[p1].status != PLAYER_PROMPT)
496                         continue;
497                 count++;
498                 pprintf_prompt(p1, "\n\n    **ANNOUNCEMENT** from %s: %s\n\n", pp->name, param[0].val.string);
499         }
500         pprintf(p, "\n(%d) **ANNOUNCEMENT** from %s: %s\n\n", count, pp->name, param[0].val.string);
501         return COM_OK;
502 }
503
504 /*
505  * annunreg
506  *
507  * Usage:  annunreg message
508  *
509  *   Broadcasts your message to all logged on unregistered users, and admins,
510  *   too.  Announcements reach all unregistered users and admins and cannot be
511  *   censored in any way (such as by "set shout 0").
512  */
513 int com_annunreg(int p, param_list param)
514 {
515         struct player *pp = &player_globals.parray[p];
516         int p1;
517         int count = 0;
518
519         if (!printablestring(param[0].val.string)) {
520                 pprintf(p, "Your message contains some unprintable character(s).\n");
521                 return COM_OK;
522         }
523         for (p1 = 0; p1 < player_globals.p_num; p1++) {
524                 if (p1 == p) continue;
525                 if (player_globals.parray[p1].status != PLAYER_PROMPT) continue;
526                 if (CheckPFlag(p1, PFLAG_REG)
527                     && !check_admin(p1, ADMIN_ADMIN))
528                         continue;
529                 count++;
530                 pprintf_prompt(p1, "\n\n    **UNREG ANNOUNCEMENT** from %s: %s\n\n", 
531                                pp->name, param[0].val.string);
532         }
533         pprintf(p, "\n(%d) **UNREG ANNOUNCEMENT** from %s: %s\n\n",
534                 count, pp->name, param[0].val.string);
535         return COM_OK;
536 }
537
538 /*
539  * asetpasswd
540  *
541  * Usage: asetpasswd player {password,*}
542  *
543  *   This command sets the password of the player to the password given.
544  *   If '*' is specified then the player's account is locked, and no password
545  *   will work until a new one is set by asetpasswd.
546  *
547  *   If the player is connected, he is told of the new password and the name
548  *   of the admin who changed it, or likewise of his account status.  An
549  *   email message is mailed to the player's email address as well.
550  */
551 int com_asetpasswd(int p, param_list param)
552 {
553   struct player *pp = &player_globals.parray[p];
554   int p1, connected;
555   char subject[400], text[10100];
556   char salt[3];
557
558   if (!FindPlayer(p, param[0].val.word, &p1, &connected))
559     return COM_OK;
560
561   if (!check_admin2(p, p1)) {
562     pprintf(p, "You can only set password for players below your adminlevel.\n");
563     if (!connected)
564       player_remove(p1);
565     return COM_OK;
566   }
567   if (!CheckPFlag(p1, PFLAG_REG)) {
568     pprintf(p, "You cannot set the password of an unregistered player!\n");
569     return COM_OK;
570   }
571   if (player_globals.parray[p1].passwd)
572     free(player_globals.parray[p1].passwd);
573   if (param[1].val.word[0] == '*') {
574     player_globals.parray[p1].passwd = strdup(param[1].val.word);
575     pprintf(p, "Account %s locked!\n", player_globals.parray[p1].name);
576     sprintf(text, "Password of %s is now useless.  Your account at our"
577                   " FICS has been locked.\n", player_globals.parray[p1].name);
578       pprintf(p, "Please leave a comment to explain why %s's account"
579                  " was locked.\n", player_globals.parray[p1].name);
580       pcommand(p, "addcomment %s Account locked.\n", player_globals.parray[p1].name);
581   } else {
582     salt[0] = 'a' + random() % 26;
583     salt[1] = 'a' + random() % 26;
584     salt[2] = '\0';
585     player_globals.parray[p1].passwd = strdup(chessd_crypt(param[1].val.word, salt));
586     sprintf(text, "Password of %s changed to \"%s\".\n", player_globals.parray[p1].name, param[1].val.word);
587     pprintf(p, "%s", text);
588   }
589   if (param[1].val.word[0] == '*') {
590     sprintf(subject, "CHESSD: %s has locked your account.", pp->name);
591     if (connected)
592       pprintf_prompt(p1, "\n%s\n", subject);
593   } else {
594     sprintf(subject, "CHESSD: %s has changed your password.", pp->name);
595     if (connected)
596       pprintf_prompt(p1, "\n%s\n", subject);
597   }
598   mail_string_to_address(player_globals.parray[p1].emailAddress, subject, text);
599   player_save(p1);
600   if (!connected)
601     player_remove(p1);
602   return COM_OK;
603 }
604
605 /*
606  * asetemail
607  *
608  * Usage: asetemail player [address]
609  *
610  *   Sets the email address of the player to the address given.  If the
611  *   address is omited, then the player's email address is cleared.  The
612  *   person's email address is revealed to them when they use the "finger"
613  *   command, but no other users -- except admins -- will have another
614  *   player's email address displayed.
615  */
616 int com_asetemail(int p, param_list param)
617 {
618   struct player *pp = &player_globals.parray[p];
619   int p1, connected;
620   char *oldemail;
621
622   if (!FindPlayer(p, param[0].val.word, &p1, &connected))
623     return COM_OK;
624
625   if (!check_admin2(p, p1)) {
626     pprintf(p, "You can only set email addr for players below your adminlevel.\n");
627     if (!connected)
628       player_remove(p1);
629     return COM_OK;
630   }
631   if (player_globals.parray[p1].emailAddress) {
632     oldemail = strdup(player_globals.parray[p1].emailAddress);
633     free(player_globals.parray[p1].emailAddress);
634   } else {
635     oldemail = strdup("");
636   }
637     
638   if (param[1].type == TYPE_NULL) {
639     player_globals.parray[p1].emailAddress = NULL;
640     pprintf(p, "Email address for %s removed\n", player_globals.parray[p1].name);
641     pcommand(p, "addcomment %s Email address removed.\n", player_globals.parray[p1].name);
642     
643   } else {
644     player_globals.parray[p1].emailAddress = strdup(param[1].val.word);
645     pprintf(p, "Email address of %s changed to \"%s\".\n", player_globals.parray[p1].name, param[1].val.word);
646     pcommand(p, "addcomment %s Email address changed from %s to %s.\n", player_globals.parray[p1].name, oldemail, player_globals.parray[p1].emailAddress);
647   }
648   free(oldemail);
649   player_save(p1);
650   if (connected) {
651     if (param[1].type == TYPE_NULL) {
652       pprintf_prompt(p1, "\n\n%s has removed your email address.\n\n", pp->name);
653     } else {
654       pprintf_prompt(p1, "\n\n%s has changed your email address.\n\n", pp->name);
655     }
656   } else {
657     player_remove(p1);
658   }
659   return COM_OK;
660 }
661
662 /*
663  * asetrealname
664  *
665  * Usage:  asetrealname user newname
666  *
667  *   This command sets the user's real name (as displayed to admins on finger
668  *   notes) to "newname".
669  */
670 int com_asetrealname(int p, param_list param)
671 {
672   struct player *pp = &player_globals.parray[p];
673   int p1, connected;
674
675   if (!FindPlayer(p, param[0].val.word, &p1, &connected))
676     return COM_OK;
677
678   if (!check_admin2(p, p1)) {
679     pprintf(p, "You can only set real names for players below your adminlevel.\n");
680     if (!connected)
681       player_remove(p1);
682     return COM_OK;
683   }
684   if (player_globals.parray[p1].fullName)
685     free(player_globals.parray[p1].fullName);
686   if (param[1].type == TYPE_NULL) {
687     player_globals.parray[p1].fullName = NULL;
688     pprintf(p, "Real name for %s removed\n", player_globals.parray[p1].name);
689   } else {
690     player_globals.parray[p1].fullName = strdup(param[1].val.word);
691     pprintf(p, "Real name of %s changed to \"%s\".\n", player_globals.parray[p1].name, param[1].val.word);
692   }
693   player_save(p1);
694   if (connected) {
695     if (param[1].type == TYPE_NULL) {
696       pprintf_prompt(p1, "\n\n%s has removed your real name.\n\n", pp->name);
697     } else {
698       pprintf_prompt(p1, "\n\n%s has changed your real name.\n\n", pp->name);
699     }
700   } else {
701     player_remove(p1);
702   }
703   return COM_OK;
704 }
705
706 /*
707  * asethandle
708  *
709  * Usage: asethandle oldname newname
710  *
711  *   This command changes the handle of the player from oldname to
712  *   newname.  The various player information, messages, logins, comments
713  *   and games should be automatically transferred to the new account.
714  */
715 int com_asethandle(int p, param_list param)
716 {
717   char *player = param[0].val.word;
718   char *newplayer = param[1].val.word;
719   char playerlower[MAX_LOGIN_NAME], newplayerlower[MAX_LOGIN_NAME];
720   int p1;
721
722   strcpy(playerlower, player);
723   stolower(playerlower);
724   strcpy(newplayerlower, newplayer);
725   stolower(newplayerlower);
726   if (player_find_bylogin(playerlower) >= 0) {
727     pprintf(p, "A player by that name is logged in.\n");
728     return COM_OK;
729   }
730   if (player_find_bylogin(newplayerlower) >= 0) {
731     pprintf(p, "A player by that new name is logged in.\n");
732     return COM_OK;
733   }
734   p1 = player_new();
735   if (player_read(p1, playerlower)) {
736     pprintf(p, "No player by the name %s is registered.\n", player);
737     player_remove(p1);
738     return COM_OK;
739   } else {
740           if (!check_admin2(p, p1)) {
741                   pprintf(p, "You can't set handles for an admin with a level higher than or equal to yourself.\n");
742                   player_remove(p1);
743                   return COM_OK;
744           }
745   }
746   player_remove(p1);
747
748   p1 = player_new();
749   if ((!player_read(p1, newplayerlower)) && (strcmp(playerlower, newplayerlower))) {
750     pprintf(p, "Sorry that handle is already taken.\n");
751     player_remove(p1);
752     return COM_OK;
753   }
754   player_remove(p1);
755
756   if ((!player_rename(playerlower, newplayerlower)) && (!player_read(p1, newplayerlower))) {
757     pprintf(p, "Player %s renamed to %s.\n", player, newplayer);
758     free(player_globals.parray[p1].name);
759     player_globals.parray[p1].name = strdup(newplayer);
760     player_save(p1);
761     if (player_globals.parray[p1].s_stats.rating > 0)
762       UpdateRank(TYPE_STAND, newplayer, &player_globals.parray[p1].s_stats, player);
763     if (player_globals.parray[p1].b_stats.rating > 0)
764       UpdateRank(TYPE_BLITZ, newplayer, &player_globals.parray[p1].b_stats, player);
765     if (player_globals.parray[p1].w_stats.rating > 0)
766       UpdateRank(TYPE_WILD, newplayer, &player_globals.parray[p1].w_stats, player);
767   } else {
768     pprintf(p, "Asethandle failed.\n");
769   }
770   player_remove(p1);
771   return COM_OK;
772 }
773
774 /*
775  * asetadmin
776  *
777  * Usage: asetadmin player AdminLevel
778  *
779  *   Sets the admin level of the player with the following restrictions.
780  *   1. You can only set the admin level of players lower than yourself.
781  *   2. You can only set the admin level to a level that is lower than
782  *      yourself.
783  */
784 int com_asetadmin(int p, param_list param)
785 {
786   struct player *pp = &player_globals.parray[p];
787   int p1, connected, oldlevel;
788
789   if (!FindPlayer(p, param[0].val.word,&p1, &connected))
790     return COM_OK;
791
792   if (!check_admin2(p, p1)) {
793     pprintf(p, "You can only set adminlevel for players below your adminlevel.\n");
794     if (!connected)
795       player_remove(p1);
796     return COM_OK;
797   }
798   if (!strcmp(player_globals.parray[p1].login, pp->login)) {
799     pprintf(p, "You can't change your own adminlevel.\n");
800     return COM_OK;
801   }
802   if (!check_admin(p, param[1].val.integer+1)) {
803     pprintf(p, "You can't promote someone to or above your adminlevel.\n");
804     if (!connected)
805       player_remove(p1);
806     return COM_OK;
807   }
808   oldlevel = player_globals.parray[p1].adminLevel;
809   player_globals.parray[p1].adminLevel = param[1].val.integer;
810   pprintf(p, "Admin level of %s set to %d.\n", player_globals.parray[p1].name, player_globals.parray[p1].adminLevel);
811   player_save(p1);
812   if (connected) {
813     pprintf_prompt(p1, "\n\n%s has set your admin level to %d.\n\n", pp->name, player_globals.parray[p1].adminLevel);
814   } else {
815     player_remove(p1);
816   }
817   return COM_OK;
818 }
819
820 static void SetRating(int p1, param_list param, struct statistics *s)
821 {
822   s->rating = param[1].val.integer;
823   if (s->ltime == 0L)
824     s->sterr = 70.0;
825
826   if (param[2].type == TYPE_INT) {
827     s->win = param[2].val.integer;
828     if (param[3].type == TYPE_INT) {
829       s->los = param[3].val.integer;
830       if (param[4].type == TYPE_INT) {
831         s->dra = param[4].val.integer;
832         if (param[5].type == TYPE_INT) {
833           s->sterr = (double) param[5].val.integer;
834         }
835       }
836     }
837   }
838   s->num = s->win + s->los + s->dra;
839   if (s->num == 0) {
840     s->ltime = 0L;
841 #if 0
842     s->dra = 1;
843     s->num = 1;
844 #endif
845   } else {
846     s->ltime = time(0);
847   }
848 }
849
850 /*
851  * asetblitz
852  *
853  * Usage: asetblitz handle rating won lost drew RD
854  *
855  *   This command allows admins to set a user's statistics for Blitz games.
856  *   The parameters are self-explanatory: rating, # of wins, # of losses,
857  *   # of draws, and ratings deviation.
858  */
859 int com_asetblitz(int p, param_list param)
860 {
861   int p1, connected;
862
863   if (!FindPlayer(p, param[0].val.word, &p1, &connected))
864     return COM_OK;
865
866   if (CheckPFlag(p1, PFLAG_REG)) {
867       SetRating(p1, param, &player_globals.parray[p1].b_stats);
868       player_save(p1);
869       UpdateRank(TYPE_BLITZ, player_globals.parray[p1].name, &player_globals.parray[p1].b_stats,
870              player_globals.parray[p1].name);
871   } else 
872     pprintf(p, "%s is unregistered. Can't modify rating.\n", player_globals.parray[p1].name);
873
874   if (!connected)
875     player_remove(p1);
876   return COM_OK;
877 }
878
879 /*
880  * asetwild
881  *
882  * Usage: asetwild handle rating won lost drew RD
883  *
884  *   This command allows admins to set a user's statistics for Wild games.
885  *   The parameters are self-explanatory: rating, # of wins, # of losses,
886  *   # of draws, and ratings deviation.
887  */
888 int com_asetwild(int p, param_list param)
889 {
890   int p1, connected;
891
892   if (!FindPlayer(p, param[0].val.word, &p1, &connected))
893     return COM_OK;
894
895   if (CheckPFlag(p1, PFLAG_REG)) {
896     SetRating(p1, param, &player_globals.parray[p1].w_stats);
897     player_save(p1);
898     UpdateRank(TYPE_WILD, player_globals.parray[p1].name, &player_globals.parray[p1].w_stats,
899              player_globals.parray[p1].name);
900     pprintf(p, "Wild rating for %s modified.\n", player_globals.parray[p1].name);
901   } else 
902     pprintf(p, "%s is unregistered. Can't modify rating.\n", player_globals.parray[p1].name);
903
904   if (!connected)
905     player_remove(p1);
906   return COM_OK;
907 }
908
909 /*
910  * asetstd
911  *
912  * Usage: asetstd handle rating won lost drew RD
913  *
914  *   This command allows admins to set a user's statistics for Standard games.
915  *   The parameters are self-explanatory: rating, # of wins, # of losses, # of
916  *   draws, and ratings deviation.
917  */
918 int com_asetstd(int p, param_list param)
919 {
920   int p1, connected;
921
922   if (!FindPlayer(p, param[0].val.word, &p1, &connected))
923     return COM_OK;
924
925   if (CheckPFlag(p1, PFLAG_REG)) {
926     SetRating(p1, param, &player_globals.parray[p1].s_stats);
927     player_save(p1);
928     UpdateRank(TYPE_STAND, player_globals.parray[p1].name, &player_globals.parray[p1].s_stats,
929              player_globals.parray[p1].name);
930     pprintf(p, "Standard rating for %s modified.\n", player_globals.parray[p1].name);
931   } else
932     pprintf(p, "%s is unregistered. Can't modify rating.\n", player_globals.parray[p1].name);
933
934   if (!connected)
935     player_remove(p1);
936   return COM_OK;
937 }
938
939 /*
940  * asetlight
941  *
942  * Usage: asetlight handle rating won lost drew RD
943  *
944  *   This command allows admins to set a user's statistics for Lightning games.
945  *   The parameters are self-explanatory: rating, # of wins, # of losses, # of
946  *   draws, and ratings deviation.
947  */
948 int com_asetlight(int p, param_list param)
949 {
950   int p1, connected;
951
952   if (!FindPlayer(p, param[0].val.word, &p1, &connected))
953     return COM_OK;
954
955   if (CheckPFlag(p1, PFLAG_REG)) {
956     SetRating(p1, param, &player_globals.parray[p1].l_stats);
957     player_save(p1);
958     pprintf(p, "Lightning rating for %s modified.\n", player_globals.parray[p1].name);
959   } else
960     pprintf(p, "%s is unregistered. Can't modify rating.\n", player_globals.parray[p1].name);
961
962   if (!connected)
963     player_remove(p1);
964   return COM_OK;
965 }
966
967 /*
968  * asetbug
969  *
970  * Usage: asetbug handle rating won lost drew RD
971  *
972  *   This command allows admins to set a user's statistics for Bughouse
973  *   games.  The parameters are self-explanatory: rating, # of wins,
974  *   # of losses, # of draws, and ratings deviation.
975  */
976 int com_asetbug(int p, param_list param)
977 {
978   int p1, connected;
979
980   if (!FindPlayer(p, param[0].val.word, &p1, &connected))
981     return COM_OK;
982
983   if (CheckPFlag(p1, PFLAG_REG)) {
984     SetRating(p1, param, &player_globals.parray[p1].bug_stats);
985     player_save(p1);
986     pprintf(p, "Bughouse rating for %s modified.\n", player_globals.parray[p1].name);
987   } else
988     pprintf(p, "%s is unregistered. Can't modify rating.\n", player_globals.parray[p1].name);
989
990   if (!connected)
991     player_remove(p1);
992   return COM_OK;
993 }
994
995 /* ftell
996  *
997  * Usage: ftell user
998  *
999  *   This command forwards all further conversation between an admin and a
1000  *   user to all those listening in channel 0.
1001  *   It is unset as soon as the user logs off or ftell is typed without a
1002  *   parameter.
1003  */
1004
1005 int com_ftell(int p, param_list param)
1006 {
1007   struct player *pp = &player_globals.parray[p];
1008   int p1;
1009   char command[1024];
1010
1011   if (param[0].type == TYPE_WORD) {
1012
1013     if ((p1 = player_find_part_login(param[0].val.word)) < 0) {
1014       pprintf(p, "%s isn't logged in.\n", param[0].val.word);
1015       return COM_OK;
1016     }
1017
1018     if (p1 == p) {
1019        pprintf (p, "Nobody wants to listen to you talking to yourself! :-)\n");
1020        return COM_OK;
1021     }
1022
1023     if (pp->ftell != -1) {
1024       sprintf (command, "tell 0 I will no longer be forwarding the conversation between *%s* and myself.", player_globals.parray[pp->ftell].name);
1025       pcommand (p,command);
1026     } 
1027
1028     sprintf (command, "tell 0 I will be forwarding the conversation between *%s* and myself to channel 0.", player_globals.parray[p1].name);
1029     pcommand (p,command);
1030
1031     pp->ftell = p1;
1032     return COM_OK_NOPROMPT;
1033
1034   } else {
1035
1036       if (pp->ftell != -1) {
1037
1038         pprintf (p,"Stopping the forwarding of the conservation with %s.\n",
1039                 player_globals.parray[pp->ftell].name);
1040         pcommand (p,"tell 0 I will no longer be forwarding the conversation between *%s* and myself.",
1041                  player_globals.parray[pp->ftell].name);
1042
1043         pp->ftell = -1;
1044         return COM_OK_NOPROMPT;
1045       } else
1046         pprintf (p,"You were not forwarding a conversation.\n");
1047   }
1048
1049   return COM_OK;
1050 }
1051
1052 /*
1053  * nuke
1054  *
1055  * Usage: nuke user
1056  *
1057  *   This command disconnects the user from the server.  The user is informed
1058  *   that she/he has been nuked by the admin named and a comment is
1059  *   automatically placed in the user's files (if she/he is a registered
1060  *   user, of course).
1061  */
1062 int com_nuke(int p, param_list param)
1063 {
1064         struct player *pp = &player_globals.parray[p];
1065         int p1, fd;
1066         
1067         if ((p1 = player_find_part_login(param[0].val.word)) < 0) {
1068                 pprintf(p, "%s isn't logged in.\n", param[0].val.word);
1069                 return COM_OK;
1070         }
1071
1072         if (!check_admin2(p, p1)) {
1073                 pprintf(p, "You need a higher adminlevel to nuke %s!\n", param[0].val.word);
1074                 return COM_OK;
1075         }
1076
1077         pprintf(p, "Nuking: %s\n", param[0].val.word);
1078         pprintf(p, "Please leave a comment explaining why %s was nuked.\n", player_globals.parray[p1].name);
1079         pprintf(p1, "\n\n**** You have been kicked out by %s! ****\n\n", pp->name);
1080         pcommand(p, "addcomment %s Nuked\n", player_globals.parray[p1].name);
1081         fd = player_globals.parray[p1].socket;
1082         process_disconnection(fd);
1083         net_close_connection(fd);
1084         return COM_OK;
1085 }
1086
1087 /*
1088  * summon
1089  *
1090  * Usage: summon player
1091  *
1092  *   This command gives a beep and a message to the player indicating that you
1093  *   want to talk with him/her.  The command is useful for waking someone up,
1094  *   for example a sleepy admin or an ignorant player.
1095  */
1096 int com_summon(int p, param_list param)
1097 {
1098         struct player *pp = &player_globals.parray[p];
1099         int p1;
1100         
1101         if ((p1 = player_find_part_login(param[0].val.word)) < 0) {
1102                 pprintf(p, "%s isn't logged in.\n", param[0].val.word);
1103                 return COM_OK;
1104         }
1105
1106         pprintf(p1, "\a\n");
1107         pprintf_highlight(p1, "%s", pp->name);
1108         pprintf_prompt(p1, " needs to talk with you.  Use tell %s <message>  to reply.\a\n", pp->name);
1109         pprintf(p, "Summoning sent to %s.\n", player_globals.parray[p1].name);
1110         return COM_OK;
1111 }
1112
1113 /*
1114  * addcomment
1115  *
1116  * Usage: addcomment user comment
1117  *
1118  *   Places "comment" in the user's comments.  If a user has comments, the
1119  *   number of comments is indicated to admins using the "finger" command.
1120  *   The comments themselves are displayed by the "showcomments" command.
1121  */
1122 int com_addcomment(int p, param_list param)
1123 {
1124   int p1, connected;
1125
1126   if (!FindPlayer(p, param[0].val.word, &p1, &connected))
1127     return COM_OK;
1128
1129   if (player_add_comment(p, p1, param[1].val.string)) {
1130     pprintf(p, "Error adding comment!\n");
1131   } else {
1132     pprintf(p, "Comment added for %s.\n", player_globals.parray[p1].name);
1133     player_save(p1);
1134   }
1135   if (!connected)
1136     player_remove(p1);
1137   return COM_OK;
1138 }
1139
1140 /*
1141  * showcomment
1142  *
1143  * Usage: showcomment user
1144  *
1145  *   This command will display all of the comments added to the user's account.
1146  */
1147 int com_showcomment(int p, param_list param)
1148 {
1149   int p1, connected;
1150
1151   if (!FindPlayer(p, param[0].val.word, &p1, &connected))
1152     return COM_OK;
1153   player_show_comments(p, p1);
1154   if (!connected)
1155     player_remove(p1);
1156   return COM_OK;
1157 }
1158
1159 /*
1160  * admin
1161  *
1162  * Usage: admin
1163  *
1164  *   This command toggles your admin symbol (*) on/off.  This symbol appears
1165  *   in who listings.
1166  */
1167 int com_admin(int p, param_list param)
1168 {
1169   TogglePFlag(p, PFLAG_ADMINLIGHT);
1170   if (CheckPFlag(p, PFLAG_ADMINLIGHT)) {
1171     pprintf(p, "Admin mode (*) is now shown.\n");
1172   } else {
1173     pprintf(p, "Admin mode (*) is now not shown.\n");
1174   }
1175   return COM_OK;
1176 }
1177
1178 int com_hideinfo(int p, param_list param)
1179 {
1180         TogglePFlag(p, PFLAG_HIDEINFO);
1181  
1182         if (CheckPFlag(p, PFLAG_HIDEINFO))
1183                 pprintf(p, "Private user information now not shown.\n");
1184         else
1185                 pprintf(p, "Private user information now shown.\n");
1186         
1187         return COM_OK;
1188 }
1189
1190 /*
1191  * quota
1192  *
1193  * Usage: quota [n]
1194  *
1195  *   The command sets the number of seconds (n) for the shout quota, which
1196  *   affects only those persons on the shout quota list.  If no parameter
1197  *   (n) is given, the current setting is displayed.
1198  */
1199 int com_quota(int p, param_list param)
1200 {
1201   if (param[0].type == TYPE_NULL) {
1202     pprintf(p, "The current shout quota is 2 shouts per %d seconds.\n", seek_globals.quota_time);
1203     return COM_OK;
1204   }
1205   seek_globals.quota_time = param[0].val.integer;
1206   pprintf(p, "The shout quota is now 2 shouts per %d seconds.\n", seek_globals.quota_time);
1207   return COM_OK;
1208 }
1209
1210
1211 /* 
1212    force a server reload
1213 */
1214 int com_areload(int p, param_list param)
1215 {
1216         extern unsigned chessd_reload_flag;
1217
1218         chessd_reload_flag = 1;
1219         
1220         pprintf(p, "Server reload started\n");
1221
1222         return COM_OK;
1223 }