1 /*************************************************************************
2  *  TinyFugue - programmable mud client
3  *  Copyright (C) 1993, 1994, 1995, 1996, 1997, 1998, 1999, 2002, 2003, 2004, 2005, 2006-2007 Ken Keys
4  *
5  *  TinyFugue (aka "tf") is protected under the terms of the GNU
6  *  General Public License.  See the file "COPYING" for details.
7  ************************************************************************/
8 static const char RCSid[] = "$Id: world.c,v 35004.77 2007/01/13 23:12:39 kkeys Exp $";
9 
10 
11 /********************************************************
12  * Fugue world routines.                                *
13  ********************************************************/
14 
15 #include "tfconfig.h"
16 #include "port.h"
17 #include "tf.h"
18 #include "util.h"
19 #include "pattern.h"
20 #include "search.h"	/* for tfio.h */
21 #include "tfio.h"
22 #include "history.h"
23 #include "world.h"
24 #include "process.h"
25 #include "macro.h"	/* remove_world_macros() */
26 #include "cmdlist.h"
27 #include "socket.h"
28 #include "output.h"	/* columns */
29 
30 #define LW_TABLE	001
31 #define LW_UNNAMED	002
32 #define LW_SHORT	004
33 
34 static int  list_worlds(const Pattern *name, const Pattern *type,
35     struct TFILE *file, int flags, int (*cmp)(const void *, const void *));
36 static void free_world(World *w);
37 static World *alloc_world(void);
38 
39 static World *hworld = NULL;
40 
41 World *defaultworld = NULL;
42 
43 
free_world(World * w)44 static void free_world(World *w)
45 {
46     if (w->name)      FREE(w->name);
47     if (w->character) FREE(w->character);
48     if (w->pass)      FREE(w->pass);
49     if (w->host)      FREE(w->host);
50     if (w->port)      FREE(w->port);
51     if (w->mfile)     FREE(w->mfile);
52     if (w->type)      FREE(w->type);
53     if (w->myhost)    FREE(w->myhost);
54     if (w->screen)    free_screen(w->screen);
55 #if !NO_HISTORY
56     if (w->history) {
57         free_history(w->history);
58         FREE(w->history);
59     }
60 #endif
61     FREE(w);
62 }
63 
64 #if USE_DMALLOC
free_worlds(void)65 void free_worlds(void)
66 {
67     World *next;
68 
69     if (defaultworld)
70         free_world(defaultworld);
71     for ( ; hworld; hworld = next) {
72         next = hworld->next;
73         free_world(hworld);
74     }
75 }
76 #endif
77 
alloc_world(void)78 static World *alloc_world(void)
79 {
80     World *result;
81     result = (World *) XMALLOC(sizeof(World));
82     memset(result, 0, sizeof(World));
83     return result;
84 }
85 
86 /* A NULL name means unnamed; world will be given a temp name. */
new_world(const char * name,const char * type,const char * host,const char * port,const char * character,const char * pass,const char * mfile,int flags,const char * myhost)87 World *new_world(const char *name, const char *type,
88     const char *host, const char *port,
89     const char *character, const char *pass,
90     const char *mfile, int flags,
91     const char *myhost)
92 {
93     World *result;
94     static int unnamed = 1;
95     smallstr buffer;
96     int is_redef = FALSE;
97 
98     /* unnamed worlds can't be defined but can have other fields changed. */
99     if (name && (!*name || strchr(name, ' ') ||
100 	(*name == '(' && (*host || *port))))
101     {
102         eprintf("illegal world name: %s", name);
103         return NULL;
104     }
105 
106     if (name && cstrcmp(name, "default") == 0) {
107         if (defaultworld) {
108 	    /* redefine existing default world */
109             result = defaultworld;
110             FREE(defaultworld->name);
111             is_redef = TRUE;
112         } else {
113 	    /* define default world */
114             result = defaultworld = alloc_world();
115         }
116 
117     } else if (name && (result = find_world(name))) {
118 	/* redefine existing world */
119         FREE(result->name);
120         is_redef = TRUE;
121 
122     } else {
123 	/* define new world */
124         World **pp;
125 	if ((!*host && *port) || (*host && !*port)) {
126 	    eprintf("world %s: host and port must be both blank or both "
127 		"non-blank.", name);
128 	    return NULL;
129 	}
130         for (pp = &hworld; *pp; pp = &(*pp)->next);
131         *pp = result = alloc_world();
132 	init_list(result->triglist);
133 	init_list(result->hooklist);
134 #if !NO_HISTORY
135         /* Don't allocate the history's queue until we actually need it. */
136         result->history = init_history(NULL, 0);
137 #endif
138     }
139 
140     if (name) {
141         result->name = STRDUP(name);
142     } else {
143         sprintf(buffer, "(unnamed%d)", unnamed++);
144         result->name = STRDUP(buffer);
145     }
146 
147 #define setfield(field) \
148     do { \
149         if (field && *field) { \
150             if (result->field) FREE(result->field); \
151             result->field = STRDUP(field); \
152         } \
153     } while (0);
154 
155     setfield(character);
156     setfield(pass);
157     setfield(host);
158     setfield(port);
159     setfield(mfile);
160     setfield(type);
161     setfield(myhost);
162     result->flags |= flags;
163 
164 #ifdef PLATFORM_UNIX
165 # ifndef __CYGWIN32__
166     if (pass && *pass && loadfile && (loadfile->mode & (S_IROTH | S_IRGRP)) &&
167         !loadfile->warned)
168     {
169         wprintf("file contains passwords and is readable by others.");
170         loadfile->warned++;
171     }
172 # endif /* __CYGWIN32__ */
173 #endif /* PLATFORM_UNIX */
174 
175     if (is_redef)
176         do_hook(H_REDEF, "!Redefined %s %s", "%s %s", "world", result->name);
177     return result;
178 }
179 
180 /* should not be called for defaultworld */
nuke_world(World * w)181 int nuke_world(World *w)
182 {
183     World **pp;
184 
185     if (w->sock) {
186         eprintf("%s is in use.", w->name);
187         return 0;
188     }
189 
190     for (pp = &hworld; *pp != w; pp = &(*pp)->next);
191     *pp = w->next;
192     remove_world_macros(w);
193     kill_procs_by_world(w);
194     free_world(w);
195     return 1;
196 }
197 
handle_unworld_command(String * args,int offset)198 struct Value *handle_unworld_command(String *args, int offset)
199 {
200     World *w;
201     int result = 0;
202     const char *name;
203     char *ptr = args->data + offset;
204 
205     while (*(name = stringarg(&ptr, NULL))) {
206         if (defaultworld && cstrcmp(name, "default") == 0) {
207             free_world(defaultworld);
208             defaultworld = NULL;
209         } else if ((w = find_world(name))) {
210             result += nuke_world(w);
211         } else {
212             eprintf("No world %s", name);
213         }
214     }
215     return newint(result);
216 }
217 
worldtypecmp(const void * a,const void * b)218 static int worldtypecmp(const void *a, const void *b) {
219     return nullcstrcmp((*(World**)a)->type, (*(World**)b)->type);
220 }
221 
worldhostcmp(const void * a,const void * b)222 static int worldhostcmp(const void *a, const void *b) {
223     return nullcstrcmp((*(World**)a)->host, (*(World**)b)->host);
224 }
225 
worldportcmp(const void * a,const void * b)226 static int worldportcmp(const void *a, const void *b) {
227     return nullcstrcmp((*(World**)a)->port, (*(World**)b)->port);
228 }
229 
worldcharcmp(const void * a,const void * b)230 static int worldcharcmp(const void *a, const void *b) {
231     return nullcstrcmp((*(World**)a)->character, (*(World**)b)->character);
232 }
233 
handle_listworlds_command(String * args,int offset)234 struct Value *handle_listworlds_command(String *args, int offset)
235 {
236     int flags = LW_TABLE, mflag = matching, error = 0, result, n;
237     char c;
238     const char *ptr;
239     int (*cmp)(const void *, const void *) = cstrpppcmp;
240     Pattern type, name, *namep = NULL;
241 
242     init_pattern_str(&type, NULL);
243     init_pattern_str(&name, NULL);
244     startopt(CS(args), "T:uscm:S:");
245     while ((c = nextopt(&ptr, NULL, NULL, &offset))) {
246         switch (c) {
247             case 'T':	free_pattern(&type);
248 			error += !init_pattern_str(&type, ptr);
249 			break;
250             case 'u':	flags |= LW_UNNAMED; break;
251             case 's':	flags |= LW_SHORT;   /* fall through */
252             case 'c':	flags &= ~LW_TABLE;  break;
253             case 'm':	error = ((mflag = enum2int(ptr,0,enum_match,"-m")) < 0);
254 			break;
255 	    case 'S':	n = strlen(ptr);
256 			if (n == 0 || cstrncmp(ptr, "name", n) == 0)
257 			    cmp = cstrpppcmp;
258 			else if (cstrncmp(ptr, "type", n) == 0)
259 			    cmp = worldtypecmp;
260 			else if (cstrncmp(ptr, "host", n) == 0)
261 			    cmp = worldhostcmp;
262 			else if (cstrncmp(ptr, "port", n) == 0)
263 			    cmp = worldportcmp;
264 			else if (cstrncmp(ptr, "character", n) == 0)
265 			    cmp = worldcharcmp;
266 			else if (cstrncmp(ptr, "-", n) == 0)
267 			    cmp = NULL;
268 			else { eprintf("invalid sort criterion"); error++; }
269 			break;
270             default:	return shareval(val_zero);
271         }
272     }
273     if (error) return shareval(val_zero);
274     init_pattern_mflag(&type, mflag, 'T');
275     Stringstriptrail(args);
276     if (args->len > offset) {
277         namep = &name;
278         error += !init_pattern(namep, args->data + offset, mflag);
279     }
280     if (error) return shareval(val_zero);
281     result = list_worlds(namep, type.str?&type:NULL, tfout, flags, cmp);
282     free_pattern(&name);
283     free_pattern(&type);
284     return newint(result);
285 }
286 
list_worlds(const Pattern * name,const Pattern * type,TFILE * file,int flags,int (* cmp)(const void *,const void *))287 static int list_worlds(const Pattern *name, const Pattern *type, TFILE *file,
288     int flags, int (*cmp)(const void *, const void *))
289 {
290     World *p;
291     Vector worlds = vector_init(128);
292     int first = 1, i;
293     int need, width, width_name, width_type, width_host, width_port;
294     STATIC_BUFFER(buf);
295 
296     if (flags & LW_TABLE) {
297         width = (columns < 80 ? 80 : columns) - 1;
298         width_name = width / 5;
299         width_type = width / 7;
300         width_host = width / 3;
301         width_port = 6;
302         tfprintf(file, "%-*s %-*s %*s %-*s %s",
303             width_name, "NAME", width_type, "TYPE", width_host, "HOST",
304             width_port, "PORT", "CHARACTER");
305     }
306 
307     /* collect matching worlds */
308     for (p = defaultworld; p || first; p = first ? hworld : p->next, first=0)
309     {
310         if (!p || (!(flags & LW_UNNAMED) && p->flags & WORLD_TEMP)) continue;
311         if (name && !patmatch(name, NULL, p->name)) continue;
312         if (type && !patmatch(type, NULL, p->type ? p->type : "")) continue;
313 	vector_add(&worlds, p);
314     }
315 
316     if (cmp)
317 	vector_sort(&worlds, cmp);
318 
319     Stringtrunc(buf, 0);
320     for (i = 0; i < worlds.size; i++) {
321 	p = worlds.ptrs[i];
322         if (flags & LW_SHORT) {
323             tfputs(p->name, file);
324         } else if (flags & LW_TABLE) {
325             tfprintf(file, "%-*.*s %-*.*s %*.*s %-*.*s %s",
326                 width_name, width_name, p->name,
327                 width_type, width_type, p->type,
328                 width_host, width_host, p->host,
329                 width_port, width_port, p->port,
330                 p->character);
331         } else {
332             if (p->myhost) need = 9;
333             else if (p->flags & ~WORLD_TEMP) need = 8;
334             else if (p->mfile) need = 7;
335             else if (p->character || p->pass) need = 6;
336             else if (p->host || p->port) need = 4;
337             else need = 2;
338 
339             Stringcpy(buf, "/test addworld(");
340             Sappendf(buf, "\"%q\"", '"', p->name);
341             Sappendf(buf, ", \"%q\"", '"', p->type);
342 
343             if (need < 3) goto listworld_tail;
344             Sappendf(buf, ", \"%q\"", '"', p->host);
345 
346             if (need < 4) goto listworld_tail;
347             Sappendf(buf, ", \"%q\"", '"', p->port);
348 
349             if (need < 5) goto listworld_tail;
350             Sappendf(buf, ", \"%q\"", '"', p->character);
351 
352             if (need < 6) goto listworld_tail;
353             Sappendf(buf, ", \"%q\"", '"', p->pass);
354 
355             if (need < 7) goto listworld_tail;
356             Sappendf(buf, ", \"%q\"", '"', p->mfile);
357 
358             if (need < 8) goto listworld_tail;
359             Sappendf(buf, ", \"%s%s%s\"",
360                 (p->flags & WORLD_NOPROXY) ? "p" : "",
361                 (p->flags & WORLD_SSL) ? "x" : "",
362                 (p->flags & WORLD_ECHO) ? "e" : "");
363 
364             if (need < 9) goto listworld_tail;
365             Sappendf(buf, ", \"%q\"", '"', p->myhost);
366 
367 listworld_tail:
368 	    Stringadd(buf, ')');
369             tfputs(buf->data, file);
370         }
371     }
372     vector_free(&worlds);
373     return worlds.size;
374 }
375 
handle_saveworld_command(String * args,int offset)376 struct Value *handle_saveworld_command(String *args, int offset)
377 {
378     TFILE *file;
379     char opt;
380     const char *mode = "w";
381     char *name;
382     int result;
383 
384     if (restriction >= RESTRICT_FILE) {
385         eprintf("restricted");
386         return shareval(val_zero);
387     }
388 
389     startopt(CS(args), "a");
390     while ((opt = nextopt(NULL, NULL, NULL, &offset))) {
391         if (opt != 'a') return shareval(val_zero);
392         mode = "a";
393     }
394     if ((name = tfname(args->data + offset, "WORLDFILE")) == NULL)
395         return shareval(val_zero);
396     if ((file = tfopen(name, mode)) == NULL) {
397         operror(args->data + offset);
398         return shareval(val_zero);
399     }
400     oprintf("%% %sing world definitions to %s", *mode == 'a' ? "Append" :
401         "Writ", file->name);
402     result = list_worlds(NULL, NULL, file, 0, NULL);
403     tfclose(file);
404     return newint(result);
405 }
406 
find_world(const char * name)407 World *find_world(const char *name)
408 {
409     World *p;
410 
411     if (!name || !*name) return hworld;
412     for (p=hworld; p && (!p->name || cstrcmp(name, p->name) != 0); p = p->next);
413     return p;
414 }
415 
416 /* Perform (*func)(world) on every world */
mapworld(void (* func)(World * world))417 void mapworld(void (*func)(World *world))
418 {
419     World *w;
420 
421     for (w = hworld; w; w = w->next)
422         (*func)(w);
423 }
424 
425