1    | /*
2    |  * $Log: users.c,v $
3    |  * Revision 1.12  2003/02/28 20:16:47  nzmb
4    |  * Changed the name of resignation ratio to CD ratio, to avoid confusion with
5    |  * Doug Massey's DRR.
6    |  *
7    |  * Revision 1.11  2002/11/24 23:22:23  millis
8    |  * Removed unused function "checklist"
9    |  *
10   |  * Revision 1.10  2002/11/08 21:59:26  millis
11   |  * Fixed bug 36, to check if player is really blacklisted or simply trying
12   |  * on a restricted judge
13   |  *
14   |  * Revision 1.9  2002/08/27 22:27:58  millis
15   |  * Updated for automake/autoconf functionality
16   |  *
17   |  * Revision 1.8  2002/06/23 22:56:29  nzmb
18   |  * Cosmetic change to infoplayer/getdedication display.
19   |  *
20   |  * Revision 1.7  2001/12/29 20:38:04  nzmb
21   |  *
22   |  * Added infoplayer, record commands. Put judge version to 1.0.0 as we think it is
23   |  * stable.
24   |  *
25   |  * Revision 1.6  2001/07/15 09:20:56  greg
26   |  * added support for game directories in a sub directory
27   |  *
28   |  * Revision 1.5  2001/06/24 06:08:18  nzmb
29   |  * Added code so players may now see their plyrdata records via the "get
30   |  * dedication" command.
31   |  *
32   |  * Revision 1.4  2001/05/08 07:47:47  greg
33   |  * minor fix to allow whogame command by players after a gunboat game ends, unless it's noreveal
34   |  *
35   |  * Revision 1.3  2001/01/06 18:20:42  davidn
36   |  * Correction to parsing of players.DENY to only read lines with = at the
37   |  * start. This stops it reading lines which are commented out.
38   |  *
39   |  * Revision 1.2  2000/11/14 14:27:37  miller
40   |  * Minor changes only
41   |  *
42   |  * Revision 1.1  1998/02/28 17:49:42  david
43   |  * Initial revision
44   |  *
45   |  * Revision 1.1  1996/10/20 12:29:45  rpaar
46   |  * Morrolan v9.0
47   |  */
48   | 
49   | /*  users.c
50   |  *  Copyright 1989, Lowe.
51   |  *
52   |  *  Diplomacy is a trademark of the Avalon Hill Game Company, Baltimore,
53   |  *  Maryland, all rights reserved; used with permission.
54   |  *
55   |  *  Redistribution and use in source and binary forms are permitted
56   |  *  provided that it is for non-profit purposes, that this and the 
57   |  *  above notices are preserved and that due credit is given to Mr.
58   |  *  Lowe.
59   |  */
60   | 
61   | #include <stdio.h>
62   | #include <stdlib.h>
63   | #include <string.h>
64   | #include <sys/stat.h>
65   | 
66   | #include "config.h"
67   | #include "dip.h"
68   | #include "mail.h"
69   | #include "functions.h"
70   | #include "plyrdata.h"
71   | 
72   | /*  Max. number of lines in which necessary information must appear  */
73   | 
74   | #define MAXLINE 30
75   | 
76   | /*  Defines for registration commands  */
77   | 
78   | #define PHONE    1
79   | #define EMAIL    2
80   | #define SITE     3
81   | #define LEVEL    4
82   | #define USER     5
83   | #define COUNTRY  6
84   | #define _PACKAGE  7
85   | 
86   | /*  Defines for "Package:" options  */
87   | 
88   | #define DFT_SEND 0		/* Default:  check for Package.Autosend file */
89   | #define SEND     1		/* Definitely send the package               */
90   | #define NO_SEND  2		/* Definitely don't send the package         */
91   | 
92   | /*
93   |  *  Keep track of a database of users for Diplomacy.
94   |  */
95   | 
96   | /*
97   |  *  newuser(addr).  Add or replace an entry to the user database.  More
98   |  *  user information is on the input file until an "end" command is found.
99   |  */
100  | 
101  | int newuser(char *addr, FILE * fp)
102  | {
103  | 
104  | 	int i, j, k, n, userid, siteid, level, got, sded = nded, new_entry,
105  | 	 send_pkg = DFT_SEND;
106  | 	char *s, *t;
107  | 	char line[1024], *lines[MAXLINE], *lookfor();
108  | 	char *phone = NULL, *mail = NULL, *site = NULL, *country = NULL,
109  | 	*not_eof;
110  | 	FILE *fp1, *fp2;
111  | 	struct stat sbuf;
112  | 
113  | 	static char *keys[] =
114  | 	{"", "phone:", "email:", "e-mail:", "site:", "level:",
115  | 	 "user:", "country:", "package:"};
116  | 	static int vals[] =
117  | 	{0, PHONE, EMAIL, EMAIL, SITE, LEVEL, USER, COUNTRY,
118  | 	 _PACKAGE};
119  | 
120  | 	static char *levs[] =
121  | 	{"", "novice", "advanced", "intermediate",
122  | 	 "amateur", "expert", "winner"};
123  | 	static int lval[] =
124  | 	{L_AMATEUR, L_NOVICE, L_ADVANCED, L_INTERMEDIATE,
125  | 	 L_AMATEUR, L_EXPERT, L_WINNER};
126  | 	static char *pkgs[] =
127  | 	{"", "yes", "send", "no", "don't send", "don't"};
128  | 	/* TODO
129  | 	 * pval is unused... unlike most it looks important, so i'm leaving it
130  | 	 * in, but commented out -- nw Sat Jun  7 22:18:24 GMT 1997
131  | 	 */
132  | 	/* static int pval[] = {DFT_SEND, SEND, SEND, NO_SEND, NO_SEND, NO_SEND}; */
133  | 	n = got = level = 0;
134  | 	while ((not_eof = fgets(line, sizeof(line), fp))) {
135  | 		fputs(line, log_fp);
136  | 		fputs(line, ifp);
137  | 		if (!strchr(line, ':')) {
138  | 			not_eof = NULL;
139  | 			break;
140  | 		}
141  | 		if (!(lines[n++] = (char *) malloc(strlen(line) + 1))) {
142  | 			fprintf(rfp, "Unable to malloc space in newuser.\n");
143  | 			return E_FATAL;
144  | 		}
145  | 		strcpy(lines[n - 1], line);
146  | 		s = lookfor(lines[n - 1], keys, nentry(keys), &i);
147  | 		switch (vals[i]) {
148  | 		case PHONE:
149  | 			got |= 1;
150  | 			phone = s;
151  | 			break;
152  | 
153  | 		case COUNTRY:
154  | 			got |= 16;
155  | 			country = s;
156  | 			break;
157  | 
158  | 		case EMAIL:
159  | 			got |= 2;
160  | 			mail = s;
161  | 			break;
162  | 
163  | 		case SITE:
164  | 			got |= 4;
165  | 			site = s;
166  | 			break;
167  | 
168  | 		case LEVEL:
169  | 			got |= 8;
170  | 			lookfor(s, levs, nentry(levs), &i);
171  | 			if (!i)
172  | 				fprintf(rfp, "Unrecognized level specified, assuming %s.\n",
173  | 					levs[lval[i]]);
174  | 			level = lval[i];
175  | 			break;
176  | 
177  | 		case USER:
178  | 			free(lines[--n]);	/* Don't save 'User:' line */
179  | 			break;
180  | 
181  | 		case _PACKAGE:
182  | 			printf("found package: %s\n", s);
183  | 			lookfor(s, pkgs, nentry(pkgs), &send_pkg);
184  | 			free(lines[--n]);	/* Don't save 'Package:' line */
185  | 			break;
186  | 		}
187  | 		if ((n >= MAXLINE) || (got == (1 | 2 | 4 | 8 | 16)))
188  | 			break;
189  | 	}
190  | 
191  | 	if ((got & (1 | 2 | 4 | 16)) != (1 | 2 | 4 | 16)) {
192  | 		fprintf(rfp, "Your phone number, email addresses, country and the ");
193  | 		fprintf(rfp, "name of your site must\n");
194  | 		fprintf(rfp, "appear within the first %d lines of the registration ", MAXLINE);
195  | 		fprintf(rfp, "data and you must supply\n");
196  | 		fprintf(rfp, "a colon following the keyword on each line.\n");
197  | 		return E_WARN;
198  | 	}
199  | 	if (!(fp1 = fopen("dip.addr", "r"))) {
200  | 		perror("addresses");
201  | 		fprintf(rfp, "Unable to open addresses.\n");
202  | 		return E_FATAL;
203  | 	}
204  | 	userid = 1;
205  | 	while ((s = fgets(line, sizeof(line), fp1))) {
206  | 		i = atoi(line + 1);
207  | 		if (i >= userid)
208  | 			userid = i + 1;
209  | 		s = strchr(line, '=');
210  | 		if (!s) {
211  | 			fprintf(rfp, "Address data is corrupted.\n");
212  | 			return E_FATAL;
213  | 		}
214  | 		s++;
215  | 		if (cmpaddr(s, addr))
216  | 			break;
217  | 		if (cmpaddr(s, mail))
218  | 			break;
219  | 	}
220  | 	fclose(fp1);
221  | 
222  | 	/*
223  | 	 *  Set the site code based on the phone number.
224  | 	 */
225  | 
226  | 	if (s) {
227  | 		new_entry = 0;
228  | 		sscanf(line + 1, "%d %d", &userid, &siteid);
229  | 	} else {
230  | 		new_entry = 1;
231  | 		s = phone;
232  | 		while (*s && !isdigit(*s))
233  | 			s++;
234  | 		siteid = atoi(s) * 100;
235  | 	}
236  | 
237  | 	/*
238  | 	 *  Attempt to get a better site code based on the country.
239  | 	 */
240  | 
241  | 	if (country) {
242  | 		while (isspace(*country))
243  | 			country++;
244  | 		s = country + strlen(country) - 1;
245  | 		while (isspace(*s))
246  | 			*s-- = '\0';
247  | 		i = -1;
248  | 		if ((fp1 = fopen("data/countries.x", "r"))) {
249  | 			while (fgets(line, sizeof(line), fp1)) {
250  | 				if ((s = strchr(line, ':'))) {
251  | 					*s++ = '\0';
252  | 					if (!strcasecmp(country, line)) {
253  | 						if ((i = atoi(s)))
254  | 							siteid = i * 100;
255  | 						break;
256  | 					}
257  | 				}
258  | 			}
259  | 			fclose(fp1);
260  | 		}
261  | 		if (i == -1) {
262  | 			fprintf(xfp, "Unknown country name: [%s].\n", country);
263  | 		}
264  | 		strcat(country, "\n");
265  | 	}
266  | 	/* if the siteid is 0, don't process the registration. */
267  | 	if (!siteid) {
268  | 		fprintf(rfp, "Your Site ID resolved to a 0.  Please verify your phone ");
269  | 		fprintf(rfp, "number and your country.\nIf you think there is a problem ");
270  | 		fprintf(rfp, "on the judge's end, please contact the JK (%s).\n", GAMES_MASTER);
271  | 		return E_WARN;
272  | 	}
273  | 	if (userid >= MAXUSER) {
274  | 		fprintf(rfp, "Too many people registered.  Increase MAXUSER.\n");
275  | 		return E_FATAL;
276  | 	}
277  | 	/*
278  | 	 *  Build a new whois database.  Check for other people at this site.
279  | 	 */
280  | 
281  | 	if (!(fp1 = fopen("dip.whois", "r"))) {
282  | 		perror("whois");
283  | 		fprintf(rfp, "Unable to open whois database.\n");
284  | 		return E_FATAL;
285  | 	}
286  | 	if (!(fp2 = fopen("dip.whois.new", "w"))) {
287  | 		perror("whois.new");
288  | 		fprintf(rfp, "Unable to create whois database.\n");
289  | 		return E_FATAL;
290  | 	}
291  | 	while (fgets(line, sizeof(line), fp1)) {
292  | 		if (!strncmp(line, "User:", 5)) {
293  | 			sscanf(line + 5, "%d%d%d", &i, &j, &k);
294  | 			if (i >= sded) {
295  | 				if (i > MAXUSER - 1 || i < 0) {
296  | 					fprintf(rfp, "Whois database corrupted.  Bad userid.\n");
297  | 					return E_FATAL;
298  | 				}
299  | 				if (i >= nded)
300  | 					nded = i + 1;
301  | 				ded[i].r = k;
302  | 			}
303  | 			if (i == userid) {
304  | 				while ((s = fgets(line, sizeof(line), fp1)) && strncmp(line, "User:", 5));
305  | 				if (!s)
306  | 					break;
307  | 				sscanf(line + 5, "%d%d%d", &i, &j, &k);
308  | 				if (i >= sded) {
309  | 					if (i > MAXUSER - 1 || i < 0) {
310  | 						fprintf(rfp, "Whois database corrupted.  Bad userid.\n");
311  | 						return E_FATAL;
312  | 					}
313  | 					if (i >= nded)
314  | 						nded = i + 1;
315  | 					ded[i].r = k;
316  | 				}
317  | 			}
318  | 		}
319  | 		if (!strncasecmp(line, "Site:", 5)) {
320  | 			for (s = line + 5; isspace(*s); s++);
321  | 			if (!strcasecmp(s, site))
322  | 				siteid = j;
323  | 		}
324  | 		fputs(line, fp2);
325  | 	}
326  | 	fclose(fp1);
327  | 
328  | 	fprintf(xfp, "Registration request processed:\n");
329  | 
330  | 	fprintf(xfp, "User: %4d %d %d\n", userid, siteid, ded[userid].r);
331  | 	fprintf(fp2, "User: %4d %d %d\n", userid, siteid, ded[userid].r);
332  | 	for (i = 0; i < n; i++) {
333  | 		fputs(lines[i], xfp);
334  | 		fputs(lines[i], fp2);
335  | 	}
336  | 
337  | 	while ((not_eof = fgets(line, sizeof(line), fp))) {
338  | 		fputs(line, log_fp);
339  | 		fputs(line, ifp);
340  | 		if (!strchr(line, ':'))
341  | 			break;
342  | 		s = lookfor(line, keys, nentry(keys), &i);
343  | 		switch (i) {
344  | 		case USER:
345  | 			/* Ignore 'User:' if entered */
346  | 			break;
347  | 
348  | 		case _PACKAGE:
349  | 			lookfor(s, pkgs, nentry(pkgs), &send_pkg);
350  | 			/* Don't write 'Package:' to dip.whois file */
351  | 			break;
352  | 
353  | 		default:
354  | 			fputs(line, xfp);
355  | 			fputs(line, fp2);
356  | 		}
357  | 	}
358  | 
359  | 	for (s = line; isspace(*s); s++);
360  | 	if (*s && strncasecmp("end", line, 3)) {
361  | 		fprintf(rfp, "Warning, end of registration command not found.\n");
362  | 		fprintf(rfp, "Discarding: %s", s);
363  | 	}
364  | 	fprintf(xfp, "\n");
365  | 
366  | 	ferrck(fp2, 4001);
367  | 	fclose(fp2);
368  | 
369  | 	/*
370  | 	 *  Rebuild the addresses file.
371  | 	 */
372  | 
373  | 	if (!(fp1 = fopen("dip.addr", "r"))) {
374  | 		perror("addresses");
375  | 		fprintf(rfp, "Unable to open address file.\n");
376  | 		return E_FATAL;
377  | 	}
378  | 	if (!(fp2 = fopen("dip.addr.new", "w"))) {
379  | 		perror("address.new");
380  | 		fprintf(rfp, "Unable to create new address file.\n");
381  | 		return E_FATAL;
382  | 	}
383  | 	while (fgets(line, sizeof(line), fp1)) {
384  | 		if (atoi(line + 1) == userid) {
385  | 			if (*line != '+')
386  | 				continue;
387  | 			s = strchr(line, '=');
388  | 			if (!s) {
389  | 				fprintf(rfp, "Address data is corrupted.\n");
390  | 				return E_FATAL;
391  | 			}
392  | 			s++;
393  | 			if (cmpaddr(s, addr))
394  | 				continue;
395  | 			if (cmpaddr(s, mail))
396  | 				continue;
397  | 		}
398  | 		fputs(line, fp2);
399  | 	}
400  | 	fprintf(fp2, "+%d %d %d =%s\n", userid, siteid, level, addr);
401  | 	s = mail;
402  | 	while (*s) {
403  | 		t = s;
404  | 		while (*t && !isspace(*t) && *t != ',')
405  | 			t++;
406  | 		if (*t)
407  | 			*t++ = '\0';
408  | 		fprintf(fp2, "*%d %d %d =%s\n", userid, siteid, level, s);
409  | 		s = t;
410  | 		while (isspace(*s))
411  | 			s++;
412  | 	}
413  | 	fclose(fp1);
414  | 	ferrck(fp2, 4002);
415  | 	fclose(fp2);
416  | 
417  | 	/*
418  | 	 *  Rename the files and get rid of excess baggage.
419  | 	 */
420  | 
421  | 	rename("dip.whois.new", "dip.whois");
422  | 	rename("dip.addr.new", "dip.addr");
423  | 	for (i = 0; i < n; i++)
424  | 		free(lines[i]);
425  | 
426  | 	/*
427  | 	 *  If this is a new registration, check whether to send the package.
428  | 	 */
429  | 
430  | 	if (new_entry) {
431  | 		if ((send_pkg == SEND) ||
432  | 		    ((send_pkg == DFT_SEND) && !stat("Package.Autosend", &sbuf))) {
433  | 			fprintf(rfp, "Newuser package being sent separately.\n");
434  | 			send_package(addr);
435  | 		} else {
436  | 			fprintf(rfp, "Newuser package is not being sent.  To get it, send ");
437  | 			fprintf(rfp, "'get package' to the Judge.\n");
438  | 		}
439  | 	} else {
440  | 		fprintf(rfp, "This is an update to an existing registration; newuser ");
441  | 		fprintf(rfp, "package is not sent.\n");
442  | 		fprintf(rfp, "To get it, send 'get package' to the Judge.\n");
443  | 	}
444  | 
445  | 	fprintf(rfp, "Registration processed.\n");
446  | 
447  | 	return 0;
448  | 
449  | }
450  | 
451  | /***************************************************************************/
452  | 
453  | /*
454  |  *  Send Newuser Package of Files (upon registration, or upon request)
455  |  */
456  | 
457  | void send_package(char *addr)
458  | {
459  | 	char line[150], cmd[150], *eol;
460  | 	FILE *pfp;
461  | 	struct stat sbuf;
462  | 
463  | 	if (!(pfp = fopen("data/package", "r"))) {
464  | 		fprintf(rfp, "Package list is not available.  Please request individual ");
465  | 		fprintf(rfp, "files separately.\n");
466  | 	} else {
467  | 		while (fgets(line, sizeof(line), pfp)) {
468  | 			for (eol = line; *eol && !isspace(*eol); ++eol);
469  | 			*eol = '\0';
470  | 			if (stat(line, &sbuf)) {
471  | 				fprintf(rfp, "Package file %s is not available.\n", line);
472  | 			} else {
473  | 				sprintf(cmd, "%s %s 'Diplomacy file %s' '%s'", SMAIL_CMD,line, line, addr);
474  | 				execute(cmd);
475  | 				fprintf(rfp, "Package file %s sent.\n", line);
476  | 			}
477  | 		}
478  | 		fclose(pfp);
479  | 		fprintf(rfp, "\nPackage send completed.\n");
480  | 	}
481  | 	return;
482  | }
483  | 
484  | /***************************************************************************/
485  | 
486  | /*
487  |  *  I am Also: Add a new entry to the addresses file.
488  |  */
489  | 
490  | int iamalso(char *addr, char *oldaddr)
491  | {
492  | 
493  | 	int userid, siteid, level;
494  | 	char *s, line[200];
495  | 	FILE *fp1, *fp2;
496  | 
497  | 	if (!(fp1 = fopen("dip.addr", "r"))) {
498  | 		perror("address");
499  | 		bailout(1);
500  | 	}
501  | 	if (!(fp2 = fopen("dip.addr.new", "w"))) {
502  | 		perror("address.new");
503  | 		bailout(1);
504  | 	}
505  | 	userid = 0;
506  | 	while (fgets(line, sizeof(line), fp1)) {
507  | 		s = strchr(line, '=');
508  | 		if (s && !strcasecmp(s + 1, addr)) {
509  | 			fprintf(rfp, "IamAlso ignored, %s already registered.\n", addr);
510  | 			fclose(fp1);
511  | 			fclose(fp2);
512  | 			remove("dip.addr.new");
513  | 			return 0;
514  | 		}
515  | 		if (s && cmpaddr(s + 1, oldaddr))
516  | 			sscanf(line + 1, "%d %d %d", &userid, &siteid, &level);
517  | 		fputs(line, fp2);
518  | 	}
519  | 
520  | 	if (!userid) {
521  | 		fprintf(rfp, "IamAlso %sAddress not found as registered.\n", oldaddr);
522  | 		fclose(fp1);
523  | 		fclose(fp2);
524  | 		remove("dip.addr.new");
525  | 		return 1;
526  | 	}
527  | 	fprintf(rfp, "%s registered as user #%d.\n", addr, userid);
528  | 	fprintf(xfp, "%s registered as user #%d.\n", addr, userid);
529  | 	fprintf(fp2, "+%d %d %d =%s\n", userid, siteid, level, addr);
530  | 	fclose(fp1);
531  | 	ferrck(fp2, 4003);
532  | 	fclose(fp2);
533  | 	rename("dip.addr.new", "dip.addr");
534  | 	return 0;
535  | 
536  | }
537  | 
538  | /***************************************************************************/
539  | 
540  |  /*
541  |   * Get user: search the database to find the user and site ids.
542  |   */
543  | 
544  | int getuser(char *addr, int *userid, int *siteid, int *level)
545  | {
546  | 
547  | 	char *s, line[200];
548  | 	FILE *fp;
549  | 
550  | 	if (!(fp = fopen("dip.addr", "r"))) {
551  | 		perror("address");
552  | 		bailout(1);
553  | 	}
554  | 	while (fgets(line, sizeof(line), fp)) {
555  | 		s = strchr(line, '=');
556  | 		if (s && cmpaddr(s + 1, addr)) {
557  | 			sscanf(line + 1, "%d %d %d", userid, siteid, level);
558  | 			return 1;
559  | 		}
560  | 	}
561  | 	return 0;
562  | }
563  | 
564  | 
565  | /***************************************************************************/
566  | 
567  |  /*
568  |   * Whogame: search dip.master for a game, and whois the players.
569  |   */
570  | 
571  | void whogame(int f)
572  | {
573  | 	int x;
574  | 
575  | 	if ((dipent.flags & F_GUNBOAT)
576  | 	    && (!signedon || dipent.players[player].power != MASTER)
577  | 	    && (dipent.phase[6] != 'X'))
578  | 		fprintf(rfp, "Game '%s' is a gunboat game.\n", dipent.name);
579  | 	else
580  | 		if ((dipent.flags & F_GUNBOAT) && (dipent.flags & F_NOREVEAL)
581  | 		    && (!signedon || dipent.players[player].power != MASTER))
582  | 			fprintf(rfp, "Game '%s' is a gunboat noreveal game.\n", dipent.name);
583  | 		else
584  | 		for (x = 0; x < dipent.n; x++)
585  | 			if (dipent.players[x].power < 0)
586  | 				continue;
587  | 			else if (!f && (dipent.players[x].power == AUTONOMOUS ||
588  | 				    dipent.players[x].power == OBSERVER))
589  | 				continue;
590  | 			else {
591  | 				fprintf(rfp, "%s in game '%s':\n\n",
592  | 					powers[dipent.players[x].power], dipent.name);
593  | 				if (!strcmp(dipent.players[x].address, "*"))
594  | 					fprintf(rfp, "%s is abandoned.  Address unknown.\n\n",
595  | 					powers[dipent.players[x].power]);
596  | 				else
597  | 					whois(dipent.players[x].address);
598  | 			}
599  | 	putc('\n', rfp);
600  | 	return;
601  | }
602  | 
603  | /***************************************************************************/
604  | 
605  | /*
606  |  * Whois: dump user info to the reply file.
607  |  */
608  | 
609  | void whois(char *t)
610  | {
611  | 
612  | 	int users[MAXUSER], user, i, n = 0, len;
613  | 	char *s, *p, *q, line[200];
614  | 	FILE *fp;
615  | 
616  | 	if (!*t) {
617  | 		fprintf(rfp, "Who is whom?\n\n");
618  | 		return;
619  | 	}
620  | 	fprintf(rfp, "Whois %s", t);
621  | 	if (!(fp = fopen("dip.addr", "r"))) {
622  | 		fprintf(rfp, "Whois: can't open address file.\n\n");
623  | 	} else {
624  | 		n = 0;
625  | 		while (fgets(line, sizeof(line), fp)) {
626  | 			user = atoi(line + 1);
627  | 			for (i = 0; i < n; i++)
628  | 				if (user == users[i])
629  | 					break;
630  | 			if (i < n)
631  | 				continue;
632  | 			if (!(s = strchr(line, '=')))
633  | 				continue;
634  | 			p = t;
635  | 			while (*p) {
636  | 				for (len = 0, q = p; *q && !isspace(*q); q++, len++);
637  | 				if (!strncasecmp(s + 1, p, len)) {
638  | 					users[n++] = user;
639  | 					break;
640  | 				}
641  | 				p = q;
642  | 				while (isspace(*p))
643  | 					p++;
644  | 			}
645  | 			if (n >= MAXUSER)
646  | 				break;
647  | 		}
648  | 		fclose(fp);
649  | 	}
650  | 
651  | 	if (!n) {
652  | 		fprintf(rfp, "No matching Email addresses found.\n\n");
653  | 	} else {
654  | 		if (!(fp = fopen("dip.whois", "r"))) {
655  | 			fprintf(rfp, "Whois: can't open whois file.\n\n");
656  | 		} else {
657  | 			i = 0;
658  | 			while (fgets(line, sizeof(line), fp)) {
659  | 				if (!strncasecmp(line, "User:", 5)) {
660  | 					user = atoi(line + 5);
661  | 					for (i = 0; i < n; i++)
662  | 						if (user == users[i])
663  | 							break;
664  | 					if (i < n) {
665  | 						putc('\n', rfp);
666  | 					}
667  | 				}
668  | 				if (i < n)
669  | 					fputs(line, rfp);
670  | 			}
671  | 			putc('\n', rfp);
672  | 			fclose(fp);
673  | 		}
674  | 	}
675  | }
676  | 
677  | /***************************************************************************/
678  | 
679  |  /*
680  |   * send_dedication: Send the user's current dedication
681  |   */
682  | 
683  | int send_dedication(char *raddr)
684  | {
685  | 	int userid, siteid, level;
686  | 	float orat,rrat;
687  | 
688  | 	if (!getuser(raddr, &userid, &siteid, &level)) {
689  | 		if (!msg_header_done)
690  | 			msg_header(rfp);
691  | 		fprintf(rfp,
692  | 		   "The address %s is not registered with the judge.\n",
693  | 			raddr);
694  | 		fprintf(rfp,
695  | 			"Use a 'get form' request for information on registering.\n");
696  | 		return E_WARN;
697  | 	}
698  | 	fprintf(rfp, "User ID: %d\n\n", userid);
699  | 	fprintf(rfp, "Current dedication: %d\n", ded[userid].r);
700  | 	fprintf(rfp, "Turns ontime: %lu\n",get_data(userid,ontime));
701  | 	fprintf(rfp, "Turns total: %lu\n",get_data(userid,total));
702  | 	fprintf(rfp, "Games started: %lu\nPositions taken over: %lu\n",
703  | 		get_data(userid,started),get_data(userid,tookover));
704  | 	fprintf(rfp, "Resignations: %lu\n",get_data(userid,resigned));
705  | 	if(get_data(userid,total) == 0)
706  | 	{
707  | 		fprintf(rfp, "Played 0 turns; a perfect timeliness record.\n");
708  | 	}
709  | 	else
710  | 	{
711  | 		orat = 1.0 * get_data(userid,ontime)/get_data(userid,total);
712  | 		fprintf(rfp, "Ontime ratio: %.3f\n", orat);
713  | 	}
714  | 	if(get_data(userid,started) == 0 && get_data(userid,tookover) == 0)
715  | 	{
716  | 		fprintf(rfp, "No started games; perfect resignation record.\n\n");
717  | 	}
718  | 	else
719  | 	{
720  | 		rrat = 1.0 * get_data(userid,resigned) / (get_data(userid,started) + get_data(userid,tookover));
721  | 		fprintf(rfp, "CD ratio: %.3f\n\n", rrat);
722  | 	}
723  | 	/* TODO i'm not sure what to return here, was no return at all -- nw */
724  | 	return 0;
725  | }
726  | 
727  | /*  DEDICATION CODE, WILL BE ACTIVE IN THE FUTURE  */
728  | /*  Contact Travis C. Ruelle (ruelle@isp.nwu.edu) for details  */
729  | 
730  | /***************************************************************************/
731  | 
732  | /* TODO figure out what we wanted to do here.  the next two functions
733  |  * were commented out, i changed that to an #if out */
734  | 
735  | #if 0
736  |  /*
737  |   * Getded: Dump dedication information to a reply file.
738  |   */
739  | 
740  | getded(t)
741  | char *t;
742  | {
743  | 	fprintf(rfp, "getded: Dedicate %s\n\n", t);
744  | }
745  | 
746  | 
747  | /****************************************************************************/
748  | 
749  |  /*
750  |   * Dedgame: search dip.master for a game, and dedicate the players
751  |   */
752  | 
753  | dedgame(int f)
754  | {
755  | 	int x;
756  | 
757  | 	if ((dipent.flags & F_GUNBOAT)
758  | 	    && (!signedon || dipent.players[player].power != MASTER))
759  | 		fprintf(rfp, "Game '%s' is a gunboat game.\n", dipent.name);
760  | 	else
761  | 		for (x = 0; x < dipent.n; x++)
762  | 			if (dipent.players[x].power < 0)
763  | 				continue;
764  | 			else if (!f && (dipent.players[x].power == WILD_PLAYER ||
765  | 				 dipent.players[x].power == AUTONOMOUS ||
766  | 				    dipent.players[x].power == OBSERVER))
767  | 				continue;
768  | 			else {
769  | 				fprintf(rfp, "%s in game '%s':\n\n",
770  | 					powers[dipent.players[x].power], dipent.name);
771  | 				getded(dipent.players[x].address);
772  | 			}
773  | 	putc('\n', rfp);
774  | 	return;
775  | }
776  | 
777  | #endif
778  | 
779  | 
780  | /***************************************************************************/
781  | 
782  |  /*
783  |   * Set a particular site code to a non-generic one.
784  |   */
785  | 
786  | int setsite(char *s)
787  | {
788  | 	int i, j, k, n = 0, siteid, userid, users[MAXUSER], sded = nded;
789  | 	char *t, line[1024];
790  | 	FILE *fp1, *fp2, *fp3;
791  | 
792  | 
793  | 	if ((siteid = atoi(s))) {
794  | 		while (isdigit(*s))
795  | 			s++;
796  | 		while (isspace(*s))
797  | 			s++;
798  | 		fprintf(rfp, "Setting siteid to %d for %s", siteid, s);
799  | 
800  | 		/*
801  | 		 * First run through and figure out which users we're talking about.
802  | 		 */
803  | 
804  | 		if (!(fp1 = fopen("dip.whois", "r"))) {
805  | 			perror("whois");
806  | 			bailout(1);
807  | 		}
808  | 		n = 0;
809  | 		while (fgets(line, sizeof(line), fp1)) {
810  | 			if (!strncasecmp(line, "User:", 5))
811  | 				userid = atoi(line + 5);
812  | 			else if (!strncasecmp(line, "Site:", 5)) {
813  | 				for (t = line + 5; isspace(*t); t++);
814  | 				if (!strcasecmp(t, s)) {
815  | 					if (n >= MAXUSER) {
816  | 						fprintf(rfp, "Can't change siteid, too many users.\n");
817  | 						return 0;
818  | 					}
819  | 					users[n++] = userid;
820  | 				}
821  | 			}
822  | 		}
823  | 
824  | 		/*
825  | 		 *  Then copy the file while changing their site id.
826  | 		 */
827  | 
828  | 		rewind(fp1);
829  | 		if (!(fp2 = fopen("dip.whois.new", "w"))) {
830  | 			perror("whois.new");
831  | 			bailout(1);
832  | 		}
833  | 		while (fgets(line, sizeof(line), fp1)) {
834  | 			if (!strncasecmp(line, "User:", 5)) {
835  | 				sscanf(line + 5, "%d%d%d", &userid, &j, &k);
836  | 				if (userid >= sded) {
837  | 					if (userid > MAXUSER - 1 || userid < 0) {
838  | 						fprintf(rfp, "Whois database corrupted.  Bad userid: %s", line);
839  | 						return E_FATAL;
840  | 					}
841  | 					if (userid >= nded)
842  | 						nded = userid + 1;
843  | 					ded[userid].r = k;
844  | 				}
845  | 				for (i = 0; i < n; i++) {
846  | 					if (userid == users[i]) {
847  | 						j = siteid;
848  | 						break;
849  | 					}
850  | 				}
851  | 				fprintf(fp2, "User: %4d %d %d\n", userid, j, ded[userid].r);
852  | 				continue;
853  | 			}
854  | 			fputs(line, fp2);
855  | 		}
856  | 
857  | 		fclose(fp1);
858  | 		ferrck(fp2, 4004);
859  | 		fclose(fp2);
860  | 
861  | 		/*
862  | 		 *  Then copy the addresses file.
863  | 		 */
864  | 
865  | 		if (!(fp1 = fopen("dip.addr", "r"))) {
866  | 			perror("addresses");
867  | 			bailout(1);
868  | 		}
869  | 		if (!(fp2 = fopen("dip.addr.new", "w+"))) {
870  | 			perror("address.new");
871  | 			bailout(1);
872  | 		}
873  | 		while (fgets(line, sizeof(line), fp1)) {
874  | 			userid = atoi(line + 1);
875  | 			for (i = 0; i < n; i++)
876  | 				if (userid == users[i])
877  | 					break;
878  | 			if (i != n) {
879  | 				t = line + 1;
880  | 				while (isdigit(*t))
881  | 					t++;	/* Skip over userid */
882  | 				while (isspace(*t))
883  | 					t++;
884  | 				while (isdigit(*t))
885  | 					t++;	/* Skip over siteid */
886  | 				fprintf(fp2, "%c%d %d%s", *line, userid, siteid, t);
887  | 				fprintf(rfp, "%c%d %d%s", *line, userid, siteid, t);
888  | 				continue;
889  | 			}
890  | 			fputs(line, fp2);
891  | 		}
892  | 
893  | 		fclose(fp1);
894  | 		ferrck(fp2, 4005);
895  | 		fflush(fp2);
896  | 		fp3 = fp2;
897  | 
898  | 		rename("dip.whois.new", "dip.whois");
899  | 		rename("dip.addr.new", "dip.addr");
900  | 
901  | 	} else {
902  | 		if (!(fp3 = fopen("dip.addr", "r"))) {
903  | 			perror("addresses");
904  | 			bailout(1);
905  | 		}
906  | 	}
907  | 
908  | 	/*
909  | 	 *  Then fix up the master file.
910  | 	 */
911  | 
912  | 	if (!(fp1 = fopen(MASTER_FILE, "r"))) {
913  | 		perror(MASTER_FILE);
914  | 		bailout(1);
915  | 	}
916  | 	if (!(fp2 = fopen(TMASTER_FILE, "w"))) {
917  | 		perror(TMASTER_FILE);
918  | 		bailout(1);
919  | 	}
920  | 	while (getdipent(fp1)) {
921  | 		for (i = 0; i < dipent.n; i++) {
922  | 			if (!dipent.players[i].userid) {
923  | 				rewind(fp3);
924  | 				while (fgets(line, sizeof(line), fp3)) {
925  | 					t = strchr(line, '=') + 1;
926  | 					if (cmpaddr(dipent.players[i].address, t)) {
927  | 						sscanf(line + 1, "%d%d", &dipent.players[i].userid,
928  | 						       &dipent.players[i].siteid);
929  | 						fprintf(rfp, "Setting userid for %s in '%s' to %d, %s.\n",
930  | 							powers[dipent.players[i].power], dipent.name,
931  | 							dipent.players[i].userid, dipent.players[i].address);
932  | 						break;
933  | 					}
934  | 				}
935  | 			}
936  | 			for (j = 0; j < n; j++)
937  | 				if (dipent.players[i].userid == users[j])
938  | 					break;
939  | 			if (j != n) {
940  | 				dipent.players[i].siteid = siteid;
941  | 				fprintf(rfp, "Setting siteid for %s in '%s' to %d, %s.\n",
942  | 					powers[dipent.players[i].power], dipent.name,
943  | 					dipent.players[i].siteid, dipent.players[i].address);
944  | 			}
945  | 		}
946  | 		putdipent(fp2, 1);
947  | 	}
948  | 
949  | 	fclose(fp1);
950  | 	ferrck(fp2, 4006);
951  | 	fclose(fp2);
952  | 	fclose(fp3);
953  | 	rename(TMASTER_FILE, MASTER_FILE);
954  | 
955  | 	/* TODO check this return value.  there was no return at all originally,
956  | 	 * but there is a return of zero above on what appears to be an error
957  | 	 * of some kind.  so i figured that returning one here would be safe.
958  | 	 * -- nw Sat Jun  7 22:29:58 GMT 1997 */
959  | 
960  | 	return 1;
961  | }
962  | 
963  | /***************************************************************************
964  |  *
965  |  *	ALLOW/DENY Functionality
966  |  *	Jan 1995:  Larry Richardson
967  |  *
968  |  **************************************************************************/
969  | 
970  | /*
971  |  * is_allowed( int type_flag )
972  |  *  where:   type_flag = GLOBAL_PLAYER - global player list
973  |  *                     = GLOBAL_MASTER - global master list
974  |  *                     = GAME_PLAYER   - game-specific player list
975  |  *                     = GAME_MASTER   - included for completeness, not
976  |  *                                       used
977  |  * returns:  TRUE  - player is allowed at the level specified
978  |  *           FALSE - player is NOT allowed at the level specified
979  |  */
980  | #define ALLOW  ".ALLOW"
981  | #define DENY   ".DENY"
982  | 
983  | int is_allowed(int type_flag)
984  | {
985  |         char fname[40], uname[40];
986  |         int ret_val = 1;
987  |         FILE *fp;
988  | 
989  |         switch (type_flag) {
990  |         case GLOBAL_MASTER:
991  |                 strcpy(fname, "masters");
992  |                 break;
993  |         case GAME_PLAYER:
994  |                 if (strlen(dipent.name) == 0) {
995  |                         perror("nogame");
996  |                         return 0;
997  |                 }
998  |                 sprintf(fname, "%s%s/players", GAME_DIR, dipent.name);
999  |                 break;
1000 |         case GAME_MASTER:
1001 |                  if (strlen(dipent.name) == 0) {
1002 |                         perror("nogame");
1003 |                         return 0;
1004 |                 }
1005 |                 sprintf(fname, "%s%s/masters", GAME_DIR, dipent.name);
1006 |                 break;
1007 |         case GLOBAL_PLAYER:
1008 |         default:
1009 |                 strcpy(fname, "players");
1010 |                 break;
1011 |         }
1012 | /* Pass #1.  Look for a file named fname.ALLOW.  If it exists, check to see
1013 |    if the name is in there and set the return value accordingly. */
1014 |         sprintf(uname, "%s%s", fname, ALLOW);
1015 |         fp = fopen(uname, "r");
1016 |         if (fp != (FILE *) NULL) {
1017 |                 ret_val = new_checklist(fp, raddr);
1018 |                 fclose(fp);
1019 |         }
1020 | /* Pass #2.  Look for a file named fname.DENY.  If it exists, check to see
1021 |    if the name is in there and set the return value accordingly. */
1022 |         else {
1023 |                 sprintf(uname, "%s%s", fname, DENY);
1024 |                 fp = fopen(uname, "r");
1025 |                 if (fp != (FILE *) NULL) {
1026 |                         ret_val = !new_checklist(fp, raddr);
1027 |                         fclose(fp);
1028 |                 }
1029 |         }
1030 | 
1031 | /* Pass #3.  If neither the .ALLOW or the .DENY file exists, it must be ok,
1032 |    so set the return value, and return. */
1033 |         return ret_val;
1034 | }
1035 | 
1036 | /*
1037 |  * is_disallowed( int type_flag )
1038 |  *  where:   type_flag = GLOBAL_PLAYER - global player list
1039 |  *                     = GLOBAL_MASTER - global master list
1040 |  *                     = GAME_PLAYER   - game-specific player list
1041 |  *                     = GAME_MASTER   - included for completeness, not
1042 |  *                                       used
1043 |  * returns:  TRUE  - player is specifically denied at the level specified
1044 |  *           FALSE - player is NOT specifically denied (but may still not be allowed)
1045 |  *
1046 |  * Note: This function should only be called after calling is_allowed() to deterimine
1047 |  *       why player is not allowed
1048 |  */
1049 | 
1050 | int is_disallowed(int type_flag)
1051 | {
1052 | 	char fname[40], uname[40];
1053 | 	int ret_val = 0;
1054 | 	FILE *fp;
1055 | 
1056 | 	switch (type_flag) {
1057 | 	case GLOBAL_MASTER:
1058 | 		strcpy(fname, "masters");
1059 | 		break;
1060 | 	case GAME_PLAYER:
1061 | 		if (strlen(dipent.name) == 0) {
1062 | 			perror("nogame");
1063 | 			return 0;
1064 | 		}
1065 | 		sprintf(fname, "%s%s/players", GAME_DIR, dipent.name);
1066 | 		break;
1067 | 	case GAME_MASTER:
1068 | 		 if (strlen(dipent.name) == 0) {
1069 |                         perror("nogame");
1070 |                         return 0;
1071 |                 }
1072 |                 sprintf(fname, "%s%s/masters", GAME_DIR, dipent.name);
1073 |                 break;
1074 | 	case GLOBAL_PLAYER:
1075 | 	default:
1076 | 		strcpy(fname, "players");
1077 | 		break;
1078 | 	}
1079 | /* Pass #1.  Look for a file named fname.DENY.  If it exists, check to see
1080 |    if the name is in there and set the return value accordingly. */
1081 | 	{
1082 | 		sprintf(uname, "%s%s", fname, DENY);
1083 | 		fp = fopen(uname, "r");
1084 | 		if (fp != (FILE *) NULL) {
1085 | 			ret_val = new_checklist(fp, raddr);
1086 | 			fclose(fp);
1087 | 		}
1088 | 	}
1089 | 
1090 | /* Pass #2.  If not in .DENY file, player is not banned 
1091 |    so set the return value, and return. */
1092 | 	return ret_val;
1093 | }
1094 | 
1095 | /*
1096 |  *  new_checklist: See if the user's name is in the passed file
1097 |  *
1098 |  *  new_checklist( FILE *fp, char *raddr )
1099 |  *   where:  fp - the file pointer to use
1100 |  *           raddr - the addres to look up
1101 |  *
1102 |  *   returns:   TRUE if the address is in the file
1103 |  *           FALSE if the address is NOT in the file
1104 |  */
1105 | 
1106 | int new_checklist(FILE * fp, char *addr)
1107 | {
1108 | 	char *s, line[200];
1109 | 
1110 | 	while (fgets(line, sizeof(line), fp)) {
1111 | 	        if (*(s = line) != '=')
1112 | 			continue;
1113 | 		if (cmpaddr(s + 1, addr))
1114 | 			return 1;
1115 | 	}
1116 | 	return 0;
1117 | }
1118 | 
1119 | /*
1120 |  *  add_player: Add/Remove a player's name to one of the ALLOW/DENY files
1121 |  *
1122 |  *  add_player( char *name, char *file, char addflag )
1123 |  *   where:     name    - the name to be added/removed
1124 |  *              file    - the file to be operated on
1125 |  *              addflag - = 0, remove the name
1126 |  *                        = 1, add the name
1127 |  *
1128 |  *   returns:   None.
1129 |  */
1130 | 
1131 | void add_player(char *player_name, char *file, char addflag)
1132 | {
1133 | 	char line[200], *s;
1134 | 	FILE *in_fp, *out_fp;
1135 | 
1136 | 	if (addflag) {
1137 | 		/* First, check that this player isn't already in the file. */
1138 | 		in_fp = fopen(file, "r");
1139 | 		if ((in_fp != (FILE *) NULL) &&
1140 | 		    new_checklist(in_fp, player_name)) {
1141 | 			fprintf(rfp, "Player %s is already in the %s file",
1142 | 				player_name, file);
1143 | 			if (!strcmp(dipent.name, "control"))
1144 | 				fprintf(rfp, ".\n");
1145 | 			else
1146 | 				fprintf(rfp, "\nfor Game '%s'.\n",
1147 | 					dipent.name);
1148 | 		} else {
1149 | 			/* S/He isn't, so go ahead and add him/her. */
1150 | 			if (in_fp != (FILE *) NULL)
1151 | 				fclose(in_fp);
1152 | 			in_fp = fopen(file, "a");
1153 | 			fprintf(in_fp, "=%s\n", player_name);
1154 | 
1155 | 			fprintf(rfp, "Player %s is now in the %s file",
1156 | 				player_name, file);
1157 | 			if (!strcmp(dipent.name, "control"))
1158 | 				fprintf(rfp, ".\n");
1159 | 			else
1160 | 				fprintf(rfp, "\nfor Game '%s'.\n",
1161 | 					dipent.name);
1162 | 		}
1163 | 		fclose(in_fp);
1164 | 	} else {
1165 | 		/* First, check that this player is already in the file. */
1166 | 		in_fp = fopen(file, "r");
1167 | 		if ((in_fp == (FILE *) NULL) ||
1168 | 		    !new_checklist(in_fp, player_name)) {
1169 | 			if (in_fp != (FILE *) NULL)
1170 | 				fclose(in_fp);
1171 | 			fprintf(rfp, "Player %s is not in the %s file",
1172 | 				player_name, file);
1173 | 			if (!strcmp(dipent.name, "control"))
1174 | 				fprintf(rfp, ".\n");
1175 | 			else
1176 | 				fprintf(rfp, "\nfor Game '%s'.\n",
1177 | 					dipent.name);
1178 | 		} else {
1179 | 			struct stat file_stat;
1180 | 
1181 | 			rewind(in_fp);
1182 | 			out_fp = fopen("dip.allowdeny", "w");
1183 | 			while (fgets(line, sizeof(line), in_fp)) {
1184 | 				s = strchr(line, '=');
1185 | 				if (s == 0)
1186 | 					continue;
1187 | 				if (!cmpaddr(s + 1, player_name))
1188 | 					fputs(line, out_fp);
1189 | 			}
1190 | 			fclose(out_fp);
1191 | 			fclose(in_fp);
1192 | 
1193 | 			/* Move the temporary file to the real file after
1194 | 			   saving the original file. */
1195 | 			sprintf(line, "%s.bak", file);
1196 | 			rename(file, line);
1197 | 			rename("dip.allowdeny", file);
1198 | 
1199 | 			fprintf(rfp, "Player %s is no longer in the %s file",
1200 | 				player_name, file);
1201 | 			if (!strcmp(dipent.name, "control"))
1202 | 				fprintf(rfp, ".\n");
1203 | 			else
1204 | 				fprintf(rfp, "\nfor Game '%s'.\n",
1205 | 					dipent.name);
1206 | 
1207 | 			/* As a last step, see if the file is empty,
1208 | 			   if it is, remove it. */
1209 | 			if (!stat(file, &file_stat)) {
1210 | 				if (!file_stat.st_size)
1211 | 					remove(file);
1212 | 			}
1213 | 		}
1214 | 	}
1215 | }
1216 | 
1217 | /***************************************************************************/
1218 | 
1219 |  /*
1220 |   * Compare an address to see if it's one we've got.
1221 |   */
1222 | 
1223 | int cmpaddr(char *addr, char *list)
1224 | {
1225 | 
1226 | 	register char *s, *t, c = 0, d = 0, k;
1227 | 
1228 | 	s = list;
1229 | 	while (*s) {
1230 | 		t = addr;
1231 | 		k = 0;
1232 | 		while (*t) {
1233 | 			c = *s++;
1234 | 			d = *t++;
1235 | 			if (islower(c))
1236 | 				c = toupper(c);
1237 | 			if (islower(d))
1238 | 				d = toupper(d);
1239 | 			if (c == '%' || c == '@' || c == '.')
1240 | 				c = ++k;
1241 | 			if (d == '%' || d == '@' || d == '.')
1242 | 				d = k;
1243 | 			if (c != d)
1244 | 				break;
1245 | 		}
1246 | 		if (c == d)
1247 | 			return 1;
1248 | 		if (!c || isspace(c) || c == ',')
1249 | 			c = 0;
1250 | 		if (!d || isspace(d) || d == ',')
1251 | 			d = 0;
1252 | 		if ((!c && !d) ||
1253 | 		    (!c && d == k && k > 1) ||
1254 | 		    (!d && c == k && k > 1)) {
1255 | 			return 1;
1256 | 		}
1257 | 		while (*s && *s++ != ',');
1258 | 		while (isspace(*s))
1259 | 			s++;
1260 | 	}
1261 | 	return 0;
1262 | }