1 /* XQF - Quake server browser and launcher Copyright (C) 1998-2000 Roman
2  * Pozlevich <roma@botik.ru>
3  *
4  * This program is free software; you can redistribute it and/or modify
5  * it under the terms of the GNU General Public License as published by
6  * the Free Software Foundation; either version 2 of the License, or
7  * (at your option) any later version.
8  *
9  * This program is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12  * GNU General Public License for more details.
13  *
14  * You should have received a copy of the GNU General Public License
15  * along with this program; if not, write to the Free Software
16  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
17  */
18 
19 #include "gnuconfig.h"
20 
21 #include <sys/types.h>
22 #include <stdio.h>
23 #include <stdlib.h>     /* strtol */
24 #include <string.h>     /* strchr, strcmp, strstr */
25 #include <sys/stat.h>   /* stat */
26 #include <unistd.h>     /* stat */
27 #include <sys/socket.h> /* inet_aton, inet_ntoa */
28 #include <netinet/in.h> /* inet_aton, inet_ntoa */
29 #include <arpa/inet.h>  /* inet_aton, inet_ntoa */
30 #include <errno.h>      /* errno */
31 
32 #include <gtk/gtk.h>
33 
34 #include "i18n.h"
35 #include "xqf.h"
36 #include "xqf-ui.h"
37 #include "source.h"
38 #include "server.h"
39 #include "host.h"
40 #include "pref.h"
41 #include "dialogs.h"
42 #include "game.h"
43 #include "utils.h"
44 #include "config.h"
45 #include "dns.h"
46 #include "zipped.h"
47 #include "stat.h"
48 
49 static gboolean master_options_compare(QFMasterOptions* lhs, QFMasterOptions* rhs);
50 static QFMasterOptions parse_master_options(const char* str);
51 static void master_options_release(QFMasterOptions* o, gboolean doit);
52 
53 struct master *favorites = NULL;
54 GSList *master_groups = NULL;
55 
56 char* master_prefixes[MASTER_NUM_QUERY_TYPES] = {
57 	"master://",
58 	"gmaster://",
59 	"http://",
60 	"lan://",
61 	"file://",
62 	"gslist://",
63 };
64 
65 char* master_designation[MASTER_NUM_QUERY_TYPES] = {
66 	N_("Standard"),
67 	N_("Gamespy"),
68 	N_("http"),
69 	N_("LAN"),
70 	N_("File"),
71 	"gslist",
72 };
73 
74 static GSList *all_masters = NULL;
75 
76 
save_list(FILE * f,struct master * m)77 static void save_list (FILE *f, struct master *m) {
78 	GSList *srv;
79 	struct server *s;
80 
81 	if (!m->servers)
82 		return;
83 	debug (6, "save_list() -- Saving Server List \"%s\"", m->name);
84 
85 #ifdef DEBUG
86 	fprintf (stderr, "Saving server list \"%s\"...\n", m->name);
87 #endif
88 
89 	if (m == favorites) {
90 		fprintf (f, "[%s]\n", m->name);
91 	}
92 	else {
93 		char* str = master_to_url(m);
94 		fputc('[', f);
95 		fputs(games[m->type].id, f);
96 		fputc(' ', f);
97 		fputs(str, f);
98 		fputs("]\n", f);
99 		g_free(str);
100 	}
101 
102 	for (srv = m->servers; srv; srv = srv->next) {
103 		s = (struct server *) srv->data;
104 		fprintf (f, "%s %s:%d\n", games[s->type].id, inet_ntoa (s->host->ip), s->port);
105 	}
106 
107 	fprintf (f, "\n");
108 }
109 
110 
save_favorites(void)111 void save_favorites (void) {
112 	FILE *f;
113 	char *realname;
114 
115 	debug (6, "save_favorites() --");
116 	realname = file_in_dir (user_rcdir, FILENAME_FAVORITES);
117 
118 	while (1) {
119 		f = fopen (realname, "w");
120 
121 		if (!f) {
122 			if (dialog_yesno (NULL, 0, _("Retry"), _("Skip"),
123 						_("Cannot write to file %s\nSystem Error: %s\n"),
124 						realname, g_strerror (errno))) {
125 				continue;
126 			}
127 			g_free (realname);
128 			return;
129 		}
130 
131 		break;
132 	}
133 
134 	save_list (f, favorites);
135 	fclose (f);
136 	g_free (realname);
137 }
138 
139 
save_lists(GSList * list,const char * filename)140 static void save_lists (GSList *list, const char *filename) {
141 	char *realname;
142 	struct zstream z;
143 
144 	debug (6, "save_lists() -- %s", filename);
145 	realname = file_in_dir (user_rcdir, filename);
146 
147 	if (!list) {
148 		zstream_unlink (realname);
149 		g_free (realname);
150 		return;
151 	}
152 
153 	zstream_open_w (&z, realname);
154 
155 	if (z.f) {
156 		while (list) {
157 			save_list (z.f, (struct master *) list->data);
158 			list = list->next;
159 		}
160 		zstream_close (&z);
161 	}
162 
163 	g_free (realname);
164 }
165 
166 
save_server_info(const char * filename,GSList * servers)167 static void save_server_info (const char *filename, GSList *servers) {
168 	char *realname;
169 	struct zstream z;
170 	struct server *s;
171 
172 	realname = file_in_dir (user_rcdir, filename);
173 
174 	if (!servers) {
175 		zstream_unlink (realname);
176 		g_free (realname);
177 		return;
178 	}
179 
180 	zstream_open_w (&z, realname);
181 
182 	if (z.f) {
183 		while (servers) {
184 			s = (struct server *) servers->data;
185 
186 			if (s->ref_count > 0 && games[s->type].save_info)
187 				(*games[s->type].save_info) (z.f, s);
188 
189 			servers = servers->next;
190 		}
191 		zstream_close (&z);
192 	}
193 
194 	g_free (realname);
195 }
196 
197 
find_master_server(char * addr,unsigned short port,char * str,QFMasterOptions * options)198 static struct master *find_master_server (char *addr, unsigned short port, char *str, QFMasterOptions* options) {
199 	GSList *list;
200 	struct master *m = NULL;
201 	struct in_addr ip;
202 
203 	if (!addr || !port)
204 		return NULL;
205 
206 	for (list = all_masters; list; list = list->next) {
207 		m = (struct master *) list->data;
208 		if (m->port && m->port == port)
209 		{
210 			if (m->hostname)
211 			{
212 				if (!g_ascii_strcasecmp (m->hostname, addr)) {
213 					if ((!str || // pre 0.9.4e list file
214 								!g_ascii_strcasecmp (games[m->type].id, str)) // list file in new format
215 							&& master_options_compare(&m->options, options))
216 						break;
217 				}
218 			}
219 			else
220 			{
221 				if (m->host && inet_aton (addr, &ip) && ip.s_addr == m->host->ip.s_addr) {
222 					if ((!str || // pre 0.9.4e list file
223 								!g_ascii_strcasecmp (games[m->type].id, str))
224 							&& master_options_compare(&m->options, options))
225 						break;
226 				}
227 			}
228 		}
229 		m = NULL;
230 	}
231 
232 	return m;
233 }
234 
235 
236 /*
237  *  This is internal function to make compare_urls() function work.
238  *  Don't use it for any other purposes.
239  *
240  *  Unification is
241  *    1) lower case protocol and hostname
242  *    2) remove port reference if it's standard port for the protocol
243  *    3) remove trailing slashes from the end of the file location
244  */
245 
unify_url(const char * url)246 static char *unify_url (const char *url) {
247 	char *unified, *tmp;
248 	char *ptr, *ptr2, *ptr3;
249 	long port;
250 	int len;
251 
252 	if (!url)
253 		return NULL;
254 
255 	ptr = strstr (url, "://");
256 	if (ptr == NULL)
257 		return NULL;
258 
259 	ptr += 3;
260 
261 	if (*ptr == '\0')
262 		return NULL;
263 
264 	unified = g_malloc (strlen (url) + 1);
265 
266 	ptr2 = strchr (ptr, ':');
267 	if (!ptr2) {
268 		ptr2 = strchr (ptr, '/');
269 		if (!ptr2)
270 			ptr2 = ptr + strlen (ptr);
271 		ptr3 = ptr2;
272 	}
273 	else {
274 		port = strtol (ptr2 + 1, &ptr3, 10);
275 		if (port != 80)
276 			ptr2 = ptr3;
277 	}
278 
279 	strncpy (unified, url, ptr2 - url);
280 	unified[ptr2 - url] = '\0';
281 	tmp = g_ascii_strdown (unified, -1);    /* g_ascii_strdown does not modify string in place */
282 	strcpy(unified, tmp);                   /* so recopy returned string at same address */
283 	g_free(tmp);
284 
285 	strcpy (unified + (ptr2 - url), ptr3);
286 
287 	while ((len = strlen (unified)) > 0 && unified[len - 1] == '/')
288 		unified[len - 1] = '\0';
289 
290 #ifdef DEBUG
291 	fprintf (stderr, "URL unification %s -> %s\n", url, unified);
292 #endif
293 
294 	return unified;
295 }
296 
297 
_compare_urls(const char * url1,const char * url2,gboolean withopts)298 static int _compare_urls (const char *url1, const char *url2, gboolean withopts) {
299 	char *uni1;
300 	char *uni2;
301 	int res = 1;
302 
303 	uni1 = unify_url (url1);
304 	uni2 = unify_url (url2);
305 
306 	/*
307 	 *  It's error if any URL is NULL
308 	 *  Don't take care of situation when both of URLs are NULL
309 	 */
310 
311 	if (uni1 != NULL && uni2 != NULL) {
312 		if (!withopts) {
313 			char* p = NULL;
314 			p = strchr(uni1, ';');
315 			if (p) *p='\0';
316 			p = strchr(uni2, ';');
317 			if (p) *p='\0';
318 		}
319 		res = strcmp (uni1, uni2);
320 	}
321 
322 	if (uni1) g_free (uni1);
323 	if (uni2) g_free (uni2);
324 	return res;
325 }
326 
327 #if 0
328 static int compare_urls (const char *url1, const char *url2) {
329 	return _compare_urls(url1, url2, TRUE);
330 }
331 #endif
332 
compare_urls_noopts(const char * url1,const char * url2)333 static int compare_urls_noopts (const char *url1, const char *url2) {
334 	return _compare_urls(url1, url2, FALSE);
335 }
336 
find_master_url(char * url,QFMasterOptions * options)337 static struct master *find_master_url (char *url, QFMasterOptions* options) {
338 	GSList *list;
339 	struct master *m = NULL;
340 
341 	if (!url)
342 		return NULL;
343 
344 	debug(5, "%s %p", url, options);
345 
346 	for (list = all_masters; list; list = list->next) {
347 		m = (struct master *) list->data;
348 		if (m->url && compare_urls_noopts (m->url, url) == 0
349 				&& master_options_compare(&m->options, options))
350 			break;
351 		else
352 			m = NULL;
353 	}
354 
355 	return m;
356 }
357 
358 
359 /**
360   @param str type string or favorites->name
361   @param url url string, NULL for favorites
362   */
read_list_parse_master(char * str,char * url)363 static struct master *read_list_parse_master (char *str, char *url) {
364 	char *addr;
365 	unsigned short port;
366 	struct master *m = NULL;
367 	enum master_query_type query_type;
368 	QFMasterOptions options = { 0, NULL };
369 	char* optstr = NULL;
370 
371 	if (favorites && !g_ascii_strcasecmp (str, favorites->name))
372 		return favorites;
373 
374 	debug(5, "%s %s", str, url);
375 
376 	query_type = get_master_query_type_from_address(str);
377 
378 	optstr = strchr(str + strlen(master_prefixes[query_type]), ';');
379 	if (optstr) {
380 		*optstr++ = '\0';
381 		options = parse_master_options(optstr);
382 	}
383 
384 	switch(query_type) {
385 		case MASTER_NATIVE:
386 		case MASTER_GAMESPY:
387 		case MASTER_LAN:
388 			if (parse_address (str + strlen(master_prefixes[query_type]), &addr, &port)) {
389 				m = find_master_server (addr, port, url, &options);
390 				g_free (addr);
391 			}
392 			break;
393 		case MASTER_HTTP:
394 		case MASTER_FILE:
395 		case MASTER_GSLIST:
396 			m = find_master_url (str, &options);
397 			break;
398 		case MASTER_NUM_QUERY_TYPES:
399 		case MASTER_INVALID_TYPE:
400 			break;
401 	}
402 
403 	master_options_release(&options, TRUE);
404 	return m;
405 }
406 
server_sorting_helper(const struct server * s1,const struct server * s2)407 static gint server_sorting_helper (const struct server *s1,
408 		const struct server *s2) {
409 	if (s1==s2)
410 		return 0;
411 	else if (s1<s2)
412 		return -1;
413 	else
414 		return 1;
415 }
416 
master_add_server(struct master * m,char * str,enum server_type type)417 static void master_add_server (struct master *m, char *str, enum server_type type) {
418 	gchar *addr, *tmp;
419 	unsigned short port;
420 	struct host *h;
421 	struct server *s;
422 	struct userver *us;
423 
424 	debug (6, "master_add_server() -- Master %lx", m);
425 	if (parse_address (str, &addr, &port)) {
426 		h = host_add (addr);
427 		if (h) {    /* IP address */
428 			host_ref (h);
429 			if ((s = server_add (h, port, type)) != NULL) {
430 				m->servers = server_list_prepend_ndp (m->servers, s);
431 				/* Since the server_add increments the ref count, and
432 				   server_list_prepend_ndp ups the ref_count, we should
433 				   unref it once because we are only keeping it in
434 				   one list after this function.
435 				   */
436 				server_unref (s);
437 			}
438 			host_unref (h);
439 		}
440 		else {      /* hostname */
441 			tmp = g_ascii_strdown (addr, -1);   /* g_ascii_strdown does not modify string in place */
442 			strcpy(addr, tmp);                  /* so recopy returned string at same address */
443 			g_free(tmp);
444 			if ((us = userver_add (addr, port, type)) != NULL)
445 				m->uservers = userver_list_add (m->uservers, us);
446 		}
447 		g_free (addr);
448 	}
449 }
450 
read_lists(const char * filename)451 static void read_lists (const char *filename) {
452 	struct zstream z;
453 	char *realname;
454 	char buf[4096];
455 	char *ch;
456 	struct master *m = NULL;
457 	char *token[8];
458 	int n;
459 	enum server_type type;
460 
461 	realname = file_in_dir (user_rcdir, filename);
462 	zstream_open_r (&z, realname);
463 
464 	if (z.f == NULL) {
465 		g_free (realname);
466 		return;
467 	}
468 
469 	while (fgets (buf, 4096, z.f)) {
470 		n = tokenize (buf, token, 8, " \t\n\r");
471 		if (n < 1)
472 			continue;
473 
474 		if (token[0][0] == '[') {               // line is a master
475 			ch = strchr (token[0] + 1, ']');    // does token 0 have a ]?
476 			if (ch) {                           // it's a favorites or pre 0.9.4e lists file
477 				*ch = '\0';
478 
479 				if (m && m->servers)
480 					m->servers = g_slist_reverse (m->servers);
481 
482 				m = read_list_parse_master (token[0] + 1, NULL);
483 			}
484 			else {
485 				ch = strchr (token[1], ']');    // does token 1 have a ]?
486 				if (ch) {
487 
488 					*ch = '\0';
489 
490 					if (m && m->servers)
491 						m->servers = g_slist_reverse (m->servers);
492 
493 					m = read_list_parse_master (token[1], token[0] + 1);    // master, type
494 				}
495 			}
496 		}
497 		else {
498 			if (!m || n < 2)
499 				continue;
500 
501 			type = id2type (token[0]);
502 
503 			if (type != UNKNOWN_SERVER)
504 				master_add_server (m, token[1], type);
505 		}
506 	}
507 
508 	if (m && m->servers)
509 		m->servers = g_slist_reverse (m->servers);
510 
511 	zstream_close (&z);
512 	g_free (realname);
513 }
514 
515 
read_server_info(const char * filename)516 static void read_server_info (const char *filename) {
517 	struct zstream z;
518 	char *realname;
519 	char *buf;
520 	char *pos;
521 	GSList *strings;
522 	int line = 1;
523 
524 	debug(3,"read_server_info(%s)",filename);
525 
526 	realname = file_in_dir (user_rcdir, filename);
527 	zstream_open_r (&z, realname);
528 
529 	if (z.f == NULL) {
530 		g_free (realname);
531 		return;
532 	}
533 
534 	buf = g_malloc (1024*16);
535 	pos = buf;
536 	strings = NULL;
537 
538 	while (1) {
539 		if (pos - buf > 1024*16 - 16) {
540 			xqf_error("server record is too large in %s, line %d\n", realname, line);
541 			g_slist_free (strings);
542 			break;
543 		}
544 
545 		if (!fgets (pos, 1024*16 - (pos - buf), z.f)) {
546 
547 			/* EOF || Error */
548 
549 			if (strings) {
550 				parse_saved_server (strings);
551 				g_slist_free (strings);
552 			}
553 			break;
554 
555 		}
556 		else {
557 
558 			if (pos[0] == '\n') {   /* empty line */
559 				++line;
560 				parse_saved_server (strings);
561 				g_slist_free (strings);
562 				strings = NULL;
563 				pos = buf;
564 			}
565 			else {
566 				strings = g_slist_append (strings, pos);
567 
568 				while (*pos) {
569 					if (*pos == '\n') {
570 						++line;
571 						*pos = '\0';
572 						break;
573 					}
574 					pos++;
575 				}
576 				pos++;
577 			}
578 
579 		}
580 	}
581 
582 	g_free (buf);
583 
584 	zstream_close (&z);
585 	g_free (realname);
586 }
587 
588 
read_favorites_old_format(char * fn)589 static void read_favorites_old_format (char *fn) {
590 	FILE *f;
591 	char buf[1024];
592 	char *token[3];
593 	int n;
594 	enum server_type type;
595 
596 	f = fopen (fn, "r");
597 	if (!f)
598 		return;
599 
600 	while (fgets (buf, 1024, f)) {
601 		n = tokenize_bychar (buf, token, 3, '|');
602 		if (n < 2)
603 			continue;
604 
605 		type = id2type (token[0]);
606 
607 		if (type != UNKNOWN_SERVER)
608 			master_add_server (favorites, token[1], type);
609 	}
610 
611 	if (favorites->servers)
612 		favorites->servers = g_slist_reverse (favorites->servers);
613 
614 	fclose (f);
615 }
616 
617 
compat_convert_favorites(void)618 static void compat_convert_favorites (void) {
619 	struct stat statbuf;
620 	char *fn;
621 
622 	fn = file_in_dir (user_rcdir, FILENAME_FAVORITES);
623 	if (stat (fn, &statbuf) == 0) {
624 		g_free (fn);
625 		return;
626 	}
627 	g_free (fn);
628 
629 	fn = file_in_dir (user_rcdir, "Favorites");
630 	read_favorites_old_format (fn);
631 	g_free (fn);
632 }
633 
634 
create_master(char * name,enum server_type type,int group)635 static struct master *create_master (char *name, enum server_type type,
636 		int group) {
637 	struct master *m;
638 
639 	m = g_malloc0 (sizeof (struct master));
640 	m->name = g_strdup (name);
641 	m->type = type;
642 	m->state = SOURCE_NA;
643 	m->isgroup = group;
644 
645 	if (!group)
646 		all_masters = g_slist_append (all_masters, m);
647 
648 	return m;
649 }
650 
651 
master_qstat_option(struct master * m)652 char* master_qstat_option(struct master* m) {
653 	g_return_val_if_fail(m!=NULL,NULL);
654 
655 	if (m->_qstat_master_option)
656 		return m->_qstat_master_option;
657 	else
658 		return games[m->type].qstat_master_option;
659 }
660 
master_set_qstat_option(struct master * m,const char * opt)661 void master_set_qstat_option(struct master* m, const char* opt) {
662 	g_return_if_fail(m!=NULL);
663 	g_free(m->_qstat_master_option);
664 	m->_qstat_master_option = opt?g_strdup(opt):NULL;
665 }
666 
667 /** no deep copy! */
parse_master_options(const char * str)668 static QFMasterOptions parse_master_options(const char* str) {
669 	const char* semicolon = NULL;
670 	const char* val = NULL;
671 	QFMasterOptions options = {0, NULL};
672 
673 	if (!str)
674 		return options;
675 
676 	while (str && *str) {
677 		semicolon = strchr(str, ';');
678 		if (!semicolon) semicolon = str+strlen(str);
679 
680 		// find next = before ;
681 		for (val=str; *val && *val != ';' && *val != '='; ++val);
682 		if (*val != '=')
683 			val = NULL;
684 		else
685 			++val;
686 
687 		if (val && semicolon-val && !strncmp(str, "gsmtype", 7))
688 			options.gsmtype = g_strndup(val, semicolon-val);
689 		if (val && semicolon-val && !strncmp(str, "portadjust", 7))
690 			options.portadjust = atoi(val);
691 
692 		if (*semicolon)
693 			str = semicolon+1;
694 		else
695 			str = semicolon;
696 	}
697 
698 	return options;
699 }
700 
master_options_compare(QFMasterOptions * lhs,QFMasterOptions * rhs)701 static gboolean master_options_compare(QFMasterOptions* lhs, QFMasterOptions* rhs) {
702 	if (!lhs || !rhs)
703 		return FALSE;
704 
705 	if (lhs->portadjust != rhs->portadjust)
706 		return FALSE;
707 
708 	if (lhs->gsmtype && rhs->gsmtype && strcmp(lhs->gsmtype, rhs->gsmtype))
709 		return FALSE;
710 
711 	return TRUE;
712 }
713 
714 #if 0
715 char* master_to_string(QFMaster* m) {
716 	char buf[1024] = {0};
717 
718 	switch(m->master_type) {
719 		case MASTER_NATIVE:
720 		case MASTER_GAMESPY:
721 		case MASTER_LAN:
722 			snprintf (buf, sizeof(buf), "[%s %s%s:%d",
723 					games[m->type].id,
724 					master_prefixes[m->master_type],
725 					(m->hostname)? m->hostname : inet_ntoa (m->host->ip),
726 					m->port
727 				 );
728 			break;
729 		case MASTER_HTTP:
730 		case MASTER_FILE:
731 		case MASTER_GSLIST:
732 			snprintf (buf, sizeof(buf), "[%s %s", games[m->type].id, m->url);
733 			break;
734 		case MASTER_NUM_QUERY_TYPES:
735 		case MASTER_INVALID_TYPE:
736 			break;
737 	}
738 
739 	if (m->options.portadjust)
740 		snprintf(buf+strlen(buf), sizeof(buf)-strlen(buf), ";portadjust=%d", m->options.portadjust);
741 	if (m->options.gsmtype)
742 		snprintf(buf+strlen(buf), sizeof(buf)-strlen(buf), ";gsmtype=%s", m->options.gsmtype);
743 
744 	snprintf(buf, sizeof(buf)-strlen(buf), "]\n");
745 
746 	if (*buf)
747 		return g_strdup(buf);
748 	else
749 		return NULL;
750 }
751 #endif
752 
master_to_url(QFMaster * m)753 char *master_to_url(QFMaster* m) {
754 	char *query_type;
755 	char *address;
756 	char buf[1024] = {0};
757 
758 	if (m->master_type >= MASTER_NATIVE
759 			&& m->master_type < MASTER_NUM_QUERY_TYPES) {
760 		query_type = master_prefixes[m->master_type];
761 	}
762 	else
763 		return NULL;
764 
765 	switch(m->master_type) {
766 		case MASTER_NATIVE:
767 		case MASTER_GAMESPY:
768 		case MASTER_LAN:
769 			if (m->hostname) {
770 				address = m->hostname;
771 			}
772 			else if (m->host) {
773 				address = inet_ntoa(m->host->ip);
774 			}
775 			else {
776 				xqf_error("master %s has neither hostname nor ip address", m->name);
777 				return NULL;
778 			}
779 			snprintf (buf, sizeof(buf), "%s%s:%d", query_type, address, m->port);
780 			break;
781 		case MASTER_HTTP:
782 		case MASTER_FILE:
783 		case MASTER_GSLIST:
784 			snprintf (buf, sizeof(buf), "%s", m->url);
785 			break;
786 		case MASTER_NUM_QUERY_TYPES:
787 		case MASTER_INVALID_TYPE:
788 			break;
789 	}
790 
791 	if (m->options.portadjust)
792 		snprintf(buf+strlen(buf), sizeof(buf)-strlen(buf), ";portadjust=%d", m->options.portadjust);
793 	if (m->options.gsmtype)
794 		snprintf(buf+strlen(buf), sizeof(buf)-strlen(buf), ";gsmtype=%s", m->options.gsmtype);
795 
796 	if (*buf)
797 		return g_strdup(buf);
798 	else
799 		return NULL;
800 }
801 
802 
803 /** only free content, not pointer itself */
master_options_release(QFMasterOptions * o,gboolean doit)804 static void master_options_release(QFMasterOptions* o, gboolean doit) {
805 	if (!doit || !o) return;
806 	g_free(o->gsmtype);
807 }
808 
add_master(char * path,char * name,enum server_type type,const char * qstat_query_arg,int user,int lookup_only)809 struct master *add_master (char *path, char *name, enum server_type type, const char* qstat_query_arg, int user, int lookup_only) {
810 	char *addr = NULL;
811 	unsigned short port = 0;
812 	struct master *m = NULL;
813 	struct host *h = NULL;
814 	struct master *group = NULL;
815 	enum master_query_type query_type;
816 	char* optstr = NULL;
817 	QFMasterOptions options = {0, NULL};
818 	gboolean freeoptions = FALSE;
819 
820 	debug(6,"%s,%s,%d,%d,%d",path,name,type,user,lookup_only);
821 
822 	query_type = get_master_query_type_from_address(path);
823 	if (query_type == MASTER_INVALID_TYPE) {
824 		debug(1,"Invalid Master %s",path);
825 		return NULL;
826 	}
827 
828 	optstr = strchr(path + strlen(master_prefixes[query_type]), ';');
829 	if (optstr) {
830 		*optstr++ = '\0';
831 		options = parse_master_options(optstr);
832 		freeoptions = TRUE;
833 	}
834 
835 	switch(query_type) {
836 		case MASTER_NATIVE:
837 		case MASTER_GAMESPY:
838 		case MASTER_LAN:
839 			// check for valid hostname/ip
840 			if (parse_address (path + strlen(master_prefixes[query_type]), &addr, &port)) {
841 				// if no port was specified, add default master port if available or fail
842 				if (!port) {
843 					// use default_port instead of default_master_port for lan broadcasts
844 					if (query_type == MASTER_LAN) {
845 						port = games[type].default_port;
846 						/* no longer necessary as of qstat 2.5c
847 						// unreal needs one port higher
848 						if (!strcmp(games[type].qstat_option,"-uns")) {
849 						port++;
850 						type=GPS_SERVER;
851 						}
852 						*/
853 					}
854 					// do not use default for gamespy
855 					else if (query_type != MASTER_GAMESPY && games[type].default_master_port) {
856 						port = games[type].default_master_port;
857 					}
858 					else {
859 						master_options_release(&options, freeoptions);
860 						g_free (addr);
861 						// translator: %s == url, eg gmaster://bla.blub.org
862 						dialog_ok (NULL, _("You have to specify a port number for %s."),path);
863 						return NULL;
864 					}
865 				}
866 
867 				m = find_master_server (addr, port, games[type].id, &options);
868 
869 			}
870 			break;
871 
872 		case MASTER_GSLIST:
873 			if (!options.gsmtype) {
874 				master_options_release(&options, freeoptions);
875 				dialog_ok (NULL, _("'gsmtype' options missing for master %s."), name);
876 				return NULL;
877 			}
878 		case MASTER_HTTP:
879 		case MASTER_FILE:
880 			m = find_master_url (path, &options);
881 			break;
882 		case MASTER_NUM_QUERY_TYPES:
883 		case MASTER_INVALID_TYPE:
884 			master_options_release(&options, freeoptions);
885 			return NULL;
886 	}
887 
888 	if (lookup_only) {
889 		master_options_release(&options, freeoptions);
890 		g_free (addr);
891 		return m;
892 	}
893 
894 	// we don't update qstat_query_arg for existing masters -- ln
895 	if (m) {
896 		if (user) { // Master renaming is forced by user
897 			g_free (m->name);
898 			m->name = g_strdup (name);
899 			m->user = TRUE;
900 			master_options_release(&m->options, TRUE);
901 			m->options = options;
902 			freeoptions = FALSE;
903 		}
904 		else { // Automatically rename masters that are not edited by user
905 			if (!m->user) {
906 				g_free (m->name);
907 				m->name = g_strdup (name);
908 				master_options_release(&m->options, TRUE);
909 				m->options = options;
910 				freeoptions = FALSE;
911 			}
912 		}
913 		master_options_release(&options, freeoptions);
914 		g_free (addr);
915 		return m;
916 	}
917 
918 	// master was not known already, create new
919 	switch(query_type) {
920 		case MASTER_NATIVE:
921 		case MASTER_GAMESPY:
922 		case MASTER_LAN:
923 			m = create_master (name, type, FALSE);
924 
925 			master_set_qstat_option(m, qstat_query_arg);
926 
927 			h = host_add (addr);
928 			if (h) {
929 				m->host = h;
930 				host_ref (h);
931 				g_free (addr);
932 				addr = NULL;
933 			}
934 			else {
935 				m->hostname = addr;
936 			}
937 			m->port = port;
938 			break;
939 		case MASTER_HTTP:
940 		case MASTER_FILE:
941 		case MASTER_GSLIST:
942 			m = create_master (name, type, FALSE);
943 			m->url = g_strdup (path);
944 			break;
945 		case MASTER_NUM_QUERY_TYPES:
946 		case MASTER_INVALID_TYPE:
947 			master_options_release(&options, freeoptions);
948 			return NULL;
949 	}
950 
951 	m->master_type = query_type;
952 	m->options = options;
953 	freeoptions = FALSE;
954 
955 	if (m) {
956 		group = (struct master *) g_slist_nth_data (master_groups, type);
957 		group->masters = g_slist_append (group->masters, m);
958 		m->user = user;
959 	}
960 
961 	master_options_release(&options, freeoptions);
962 	return m;
963 }
964 
free_master(struct master * m)965 void free_master (struct master *m) {
966 	struct master *group;
967 
968 	if (!m)
969 		return;
970 
971 	if (m->isgroup) {
972 		while (m->masters)
973 			free_master ((struct master *) m->masters->data);
974 	}
975 	else {
976 		if (m->type != UNKNOWN_SERVER) {
977 			group = (struct master *) g_slist_nth_data (master_groups, m->type);
978 			group->masters = g_slist_remove (group->masters, m);
979 		}
980 		all_masters = g_slist_remove (all_masters, m);
981 	}
982 
983 	host_unref (m->host);
984 
985 	g_free (m->hostname);
986 	g_free (m->name);
987 	g_free (m->url);
988 	g_free (m->_qstat_master_option);
989 	g_free (m->options.gsmtype);
990 
991 	server_list_free (m->servers);
992 	userver_list_free (m->uservers);
993 
994 	g_free (m);
995 }
996 
997 
998 static char *builtin_masters_update_info[] = {
999 
1000 	"ADD QS http://www.gameaholic.com/servers/qspy-quake gameaholic.com",
1001 	"DELETE QS http://ironman.planetquake.com/serversqspy.txt Ironman",             // does no longer work
1002 
1003 	"ADD QWS master://192.246.40.37:27000 id Limbo",
1004 	"ADD QWS master://192.246.40.37:27002 id CTF",
1005 	"ADD QWS master://192.246.40.37:27003 id Team Fortress",
1006 	"ADD QWS master://192.246.40.37:27004 id Misc",
1007 	"ADD QWS master://192.246.40.37:27006 id Deathmatch",
1008 	"ADD QWS master://qwmaster.ocrana.de:27000 Germany",
1009 	"DELETE QWS master://santa.quakeforge.net:27000 QuakeForge",                    // doesn't work (26.09.2004)
1010 
1011 	"DELETE Q2S master://satan.idsoftware.com:27900 id",                            // doesn't work (26.09.2004)
1012 	"DELETE Q2S master://q2master.planetquake.com:27900 PlanetQuake",               // doesn't work (26.09.2004)
1013 	"DELETE Q2S master://telefragged.com:27900 TeleFragged",                        // doesn't work (26.09.2004)
1014 	"DELETE Q2S master://qwmaster.barrysworld.com:27900 BarrysWorld (UK)",          // doesn't work (26.09.2004)
1015 	"DELETE Q2S master://master.quake.inet.fi:27900 iNET (Finland)",                // doesn't work (26.09.2004)
1016 	"DELETE Q2S master://q2master.mondial.net.au:27900 Australia",                  // doesn't work (26.09.2004)
1017 	"DELETE Q2S master://q2master.gxp.de:27900 gXp (Germany)",                      // doesn't work (26.09.2004)
1018 	"ADD Q2S http://www.gameaholic.com/servers/qspy-quake2 gameaholic.com",
1019 	"ADD Q2S http://www.lithium.com/quake2/gamespy.txt Lithium",
1020 	"ADD Q2S master://masterserver.exhale.de exhale.de",
1021 	"ADD Q2S master://netdome.biz netdome.biz",
1022 	"ADD Q2S master://master.planetgloom.com gloom",
1023 
1024 	"DELETE Q3S master://master3.idsoftware.com id",                                // switched off
1025 	"ADD Q3S master://master.ioquake3.org master.ioquake3.org",
1026 	/* removed, because it's added as IOURT_Master and all q3ut4 servers are also in IDs Q3 Master by default
1027 	 * "ADD Q3S master://master.urbanterror.net Urban Terror",
1028 	 */
1029 
1030 	//  "ADD Q3S master://q3master.splatterworld.de Germany",
1031 	//  "ADD Q3S master://q3.golsyd.net.au Australia",
1032 	"DELETE Q3S master://q3master.barrysworld.com:27950 BarrysWorld",               // doesn't work (26.09.2004)
1033 	"ADD Q3S http://www.gameaholic.com/servers/qspy-quake3 gameaholic.com",
1034 	"ADD Q3S master://dpmaster.deathmask.net dpmaster.deathmask.net",
1035 
1036 	"ADD Q4S master://q4master.idsoftware.com id",
1037 
1038 	//  "ADD HWS master://santa.quakeforge.net:26900 QuakeForge",
1039 
1040 	"ADD SNS http://www.gameaholic.com/servers/qspy-sin gameaholic.com",
1041 	"ADD SNS http://asp.planetquake.com/sinserverlist/servers.txt PlanetQuake",
1042 
1043 	"DELETE HLS master://half-life.east.won.net WON East",                          // switched off
1044 	"DELETE HLS master://half-life.west.won.net WON West",                          // switched off
1045 
1046 	"ADD HLS,-stm master://steam1.steampowered.com Steam 1",
1047 	"ADD HLS,-stm master://steam2.steampowered.com Steam 2",
1048 
1049 	"ADD HLA2S master://steam1.steampowered.com Steam 1",
1050 
1051 	"ADD A2S,-stma2s master://steam1.steampowered.com:27011 Steam 1",
1052 	"ADD A2S,-stma2s master://steam2.steampowered.com:27011 Steam 2",
1053 	"ADD A2S,-stma2s master://69.28.151.178:27011 Valve 1",
1054 
1055 	"ADD Q2S:KP http://www.gameaholic.com/servers/qspy-kingpin gameaholic.com",
1056 	"ADD Q2S:KP http://www.ogn.org:6666 OGN",
1057 
1058 	"ADD Q2S:HR http://www.gameaholic.com/servers/qspy-heretic2 gameaholic.com",
1059 
1060 	"DELETE UNS gmaster://unreal.epicgames.com:28900 Epic",                         // doesn't work (26.11.2004)
1061 	"DELETE UNS gmaster://utmaster.barrysworld.com:28909 BarrysWorld",              // doesn't work (26.09.2004)
1062 
1063 	"DELETE T2S master://211.233.32.77:28002 Tribes2 Master",                       // does no longer work
1064 
1065 	"DELETE T2S master://198.74.32.54:27999 Master 1",                              // does no longer work
1066 	"DELETE T2S master://198.74.32.55:27999 Master 2",                              // does no longer work
1067 	"DELETE T2S master://211.233.86.203:28002 Master 3",                            // does no longer work
1068 
1069 	"ADD T2S master://198.74.35.11:27999 Master 1",
1070 	"ADD T2S master://198.74.35.17:27999 Master 2",
1071 	"ADD T2S master://198.74.35.18:27999 Master 3",
1072 
1073 	"DELETE WOS master://wolf.idsoftware.com:27950 id",                             // does no longer work
1074 	"ADD WOS master://wolfmaster.idsoftware.com:27950 wolfmaster.idsoftware.com",
1075 
1076 	"ADD WOETS master://etmaster.idsoftware.com:27950 etmaster.idsoftware.com",
1077 
1078 	"ADD ETLS master://etmaster.idsoftware.com:27950 etmaster.idsoftware.com",
1079 
1080 	"ADD DM3S master://idnet.ua-corp.com:27650 idnet.ua-corp.com:",
1081 
1082 	"ADD ETQWS http://etqw-ipgetter.demonware.net/ipgetter/ demonware",
1083 
1084 	"ADD EFS http://www.gameaholic.com/servers/qspy-startrekeliteforce gameaholic.com",
1085 	"ADD EFS master://master.stef1.ravensoft.com:27953  Ravensoft",
1086 
1087 	"DELETE D3P master://gt.pxo.net:3445 PXO",                                      // doesn't work
1088 	"DELETE D3P http://www.gameaholic.com/servers/qspy-descent3 gameaholic.com",    // no usable servers anymore
1089 	"ADD D3G http://d3.descent.cx/d3cxraw.d3?terse=y d3.descent.cx",
1090 
1091 	"ADD SFS http://www.gameaholic.com/servers/qspy-soldieroffortune gameaholic.com",
1092 
1093 	"ADD RUNESRV http://www.gameaholic.com/servers/qspy-rune gameaholic.com",
1094 
1095 	"ADD SOF2S master://master.sof2.ravensoft.com:20110 Raven",
1096 
1097 	"DELETE UT2S http://ut2003master.epicgames.com/serverlist/full-all.txt Epic",       // does no longer work
1098 	"DELETE UT2S http://ut2003master.epicgames.com/serverlist/demo-all.txt Epic Demo",  // does no longer work
1099 
1100 	"DELETE SAS http://masterserver.savagedemo.s2games.com/gamelist.dat S2 Games",      // does no longer work
1101 	"ADD SAS http://masterserver.savage.s2games.com/gamelist_full.dat S2 Games",
1102 
1103 	"DELETE UNS http://tourneys.multiplay.co.uk/public/servers.pl?opt=ListGamespy&event=Online&type=UT&hostport=1 multiplay.co.uk - UT",                    // does no longer work
1104 	"DELETE MHS http://tourneys.multiplay.co.uk/public/servers.pl?opt=ListGamespy&event=Online&type=MOHAA&hostport=1 multiplay.co.uk - MOHAA",              // does no longer work
1105 	"DELETE EFS http://tourneys.multiplay.co.uk/public/servers.pl?opt=ListGamespy&event=Online&type=EF&hostport=1 multiplay.co.uk - Elite Force",           // does no longer work
1106 
1107 	"ADD AMS http://simplembs.armygame.com/sparse.txt armygame.com",
1108 
1109 	// version 2.0 not listed here
1110 	"DELETE AMS http://tourneys.multiplay.co.uk/public/servers.pl?opt=ListGamespy&event=Online&type=AA&hostport=1 multiplay.co.uk - Army Ops",              // does no longer work
1111 	"DELETE SOF2S http://tourneys.multiplay.co.uk/public/servers.pl?opt=ListGamespy&event=Online&type=SOF2&hostport=1 multiplay.co.uk - SOF2",              // does no longer work
1112 	"DELETE UT2S http://tourneys.multiplay.co.uk/public/servers.pl?opt=ListGamespy&event=Online&type=UT2K3&hostport=1 multiplay.co.uk - UT2003",            // does no longer work
1113 	"DELETE UT2004S http://tourneys.multiplay.co.uk/public/servers.pl?opt=ListGamespy&event=Online&type=UT2K4&hostport=1 multiplay.co.uk - UT2004",         // does no longer work
1114 	"DELETE UT2004S http://tourneys.multiplay.co.uk/public/servers.pl?opt=ListGamespy&event=Online&type=UT2K4D&hostport=1 multiplay.co.uk - UT2004 Demo",   // does no longer work
1115 	"DELETE HLS http://tourneys.multiplay.co.uk/public/servers.pl?opt=ListGamespy&event=Online&type=HLS&hostport=1 multiplay.co.uk - Half-Life",            // does no longer work
1116 
1117 	"ADD CODS master://cod01.activision.com Activision",
1118 	"DELETE CODS http://tourneys.multiplay.co.uk/public/servers.pl?opt=ListGamespy&event=Online&type=COD multiplay.co.uk - Call of Duty",
1119 
1120 	"DELETE BF1942 http://tourneys.multiplay.co.uk/public/servers.pl?opt=ListGamespy&event=Online&type=BF1942&hostport=1 multiplay.co.uk - BF1942",
1121 
1122 	"ADD JK2S master://masterjk2.ravensoft.com:28060 Ravensoft",
1123 	"ADD JK2S master://master.ouned.de:28060 master.ouned.de",
1124 
1125 	"ADD JK3S master://masterjk3.ravensoft.com:29060 Ravensoft",
1126 
1127 	"ADD UT2004S master://ut2004master1.epicgames.com:28902 Epic 1",
1128 	"ADD UT2004S master://ut2004master2.epicgames.com:28902 Epic 2",
1129 
1130 	"ADD NETP master://netpanzer.dyndns.org netpanzer.dyndns.org",
1131 
1132 	"ADD NEXUIZS master://ghdigital.com ghdigital.com",
1133 	"ADD NEXUIZS master://dpmaster.deathmask.net dpmaster.deathmask.net",
1134 
1135 	"ADD XONOTICS master://ghdigital.com ghdigital.com",
1136 	"ADD XONOTICS master://dpmaster.deathmask.net dpmaster.deathmask.net",
1137 	"ADD XONOTICS master://dpmaster.tchr.no dpmaster.tchr.no",
1138 
1139 	"ADD WARSOWS master://ghdigital.com ghdigital.com",
1140 	"ADD WARSOWS master://dpmaster.deathmask.net dpmaster.deathmask.net",
1141 
1142 	"ADD TREMULOUSS master://master.tremulous.net:30710 tremulous.net",
1143 
1144 	"ADD TREMULOUSGPPS master://master.tremulous.net:30700 tremulous.net",
1145 
1146 	"ADD TREMFUSIONS master://master.tremulous.net:30710 tremulous.net",
1147 
1148 	"ADD UNVANQUISHEDS master://unvanquished.net:27950 unvanquished.net",
1149 
1150 	"ADD OPENARENAS master://dpmaster.deathmask.net dpmaster.deathmask.net",
1151 	"ADD OPENARENAS master://master.ioquake3.org master.ioquake3.org",
1152 
1153 	"ADD OTTDS master://master.openttd.org OpenTTD",
1154 
1155 	"DELETE Q3RALLYS master://master.q3rally.com master.q3rally.com",           // switched off
1156 	// "DELETE Q3RALLYS master://master.ioquake3.org master.ioquake3.org",      // not used anymore
1157 	"DELETE Q3RALLYS master://master.q3alive.net master.q3alive.net",           // switched off
1158 
1159 	"ADD WOPS master://master.worldofpadman.com:27955 master.worldofpadman.com",
1160 
1161 	"DELETE IOURTS master://master.urbanterror.net:27900 master.urbanterror.net",
1162 	"ADD IOURTS master://master.urbanterror.info:27900 master.urbanterror.info",
1163 
1164 	"ADD REACTIONS master://master.rq3.com master.rq3.com",
1165 	// "ADD REACTIONS master://master.ioquake3.org master.ioquake3.org",        // not used anymore
1166 
1167 	"ADD SMOKINGUNSS master://master.smokin-guns.org master.smokin-guns.org",
1168 	// "DELETE SMOKINGUNSS master://master.ioquake3.org master.ioquake3.org",   // not used anymore
1169 	"ADD SMOKINGUNSS master://parttimegeeks.net:27950 parttimegeeks.net",
1170 	// "DELETE SMOKINGUNSS master://soulserv.net:27950 soulserv.net",           // switched off
1171 	// "DELETE SMOKINGUNSS master://master.q3alive.net master.q3alive.net",     // switched off
1172 
1173 	"ADD ZEQ2LITES master://master.ioquake3.org master.ioquake3.org",
1174 
1175 	"ADD TURTLEARENAS master://master.ioquake3.org master.ioquake3.org",
1176 	"ADD TURTLEARENAS master://dpmaster.deathmask.net dpmaster.deathmask.net",
1177 
1178 	"ADD ALIENARENAS master://master.corservers.com:27900 master.corservers.com",
1179 	"ADD ALIENARENAS master://master2.corservers.com:27900 master2.corservers.com",
1180 
1181 	"ADD QS lan://255.255.255.255 LAN",
1182 	"ADD QWS lan://255.255.255.255 LAN",
1183 	"ADD Q2S lan://255.255.255.255 LAN",
1184 	"ADD HLS lan://255.255.255.255 LAN",
1185 	"ADD Q3S lan://255.255.255.255 LAN",
1186 	"ADD Q4S lan://255.255.255.255 LAN",
1187 	"ADD WOS lan://255.255.255.255 LAN",
1188 	"ADD WOETS lan://255.255.255.255 LAN",
1189 	"ADD ETLS lan://255.255.255.255 LAN",
1190 	"ADD EFS lan://255.255.255.255 LAN",
1191 	"ADD UT2S lan://255.255.255.255 LAN",
1192 	"ADD UT2004S lan://255.255.255.255 LAN",
1193 	"ADD UNS lan://255.255.255.255 LAN",
1194 	"ADD RUNESRV lan://255.255.255.255 LAN",
1195 	"ADD MHS lan://255.255.255.255 LAN",
1196 	"ADD AMS lan://255.255.255.255 LAN",
1197 	"ADD CODS lan://255.255.255.255 LAN",
1198 	"ADD JK2S lan://255.255.255.255 LAN",
1199 	"ADD JK3S lan://255.255.255.255 LAN",
1200 	"ADD T2S lan://255.255.255.255 LAN",
1201 	"ADD POSTAL2 lan://255.255.255.255 LAN",
1202 	"ADD SFS lan://255.255.255.255 LAN",
1203 	"ADD DM3S lan://255.255.255.255 LAN",
1204 	"ADD ETQWS lan://255.255.255.255 LAN",
1205 	"ADD NEXUIZS lan://255.255.255.255 LAN",
1206 	"ADD XONOTICS lan://255.255.255.255 LAN",
1207 	"ADD WARSOWS lan://255.255.255.255 LAN",
1208 	"ADD TREMULOUSS lan://255.255.255.255 LAN",
1209 	"ADD TREMULOUSGPPS lan://255.255.255.255 LAN",
1210 	"ADD TREMFUSIONS lan://255.255.255.255 LAN",
1211 	"ADD UNVANQUISHEDS lan://255.255.255.255 LAN",
1212 	"ADD OPENARENAS lan://255.255.255.255 LAN",
1213 	"ADD OTTDS lan://255.255.255.255 LAN",
1214 	"ADD Q3RALLYS lan://255.255.255.255 LAN",
1215 	"ADD WOPS lan://255.255.255.255 LAN",
1216 	"ADD IOURTS lan://255.255.255.255 LAN",
1217 	"ADD REACTIONS lan://255.255.255.255 LAN",
1218 	"ADD SMOKINGUNSS lan://255.255.255.255 LAN",
1219 	"ADD ZEQ2LITES lan://255.255.255.255 LAN",
1220 	"ADD TURTLEARENAS lan://255.255.255.255 LAN",
1221 	"ADD ALIENARENAS lan://255.255.255.255 LAN",
1222 
1223 	NULL
1224 };
1225 
1226 static char *builtin_gslist_masters_update_info[] = {
1227 	"ADD QS gslist://master.gamespy.com;gsmtype=quake1 Gslist",
1228 	"ADD QWS gslist://master.gamespy.com;gsmtype=quakeworld Gslist",
1229 	"ADD Q2S gslist://master.gamespy.com;gsmtype=quake2 Gslist",
1230 	"ADD Q3S gslist://master.gamespy.com;gsmtype=quake3 Gslist",
1231 	"ADD Q4S gslist://master.gamespy.com;gsmtype=quake4 Gslist",
1232 	"ADD WOS gslist://master.gamespy.com;gsmtype=rtcw Gslist",
1233 	"ADD WOETS gslist://master.gamespy.com;gsmtype=rtcwet Gslist",
1234 	"ADD DM3S gslist://master.gamespy.com;gsmtype=doom3 Gslist",
1235 	"ADD RUNESRV gslist://master.gamespy.com;portadjust=-1;gsmtype=rune Gslist",
1236 	"ADD UNS gslist://master.gamespy.com;portadjust=-1;gsmtype=ut Gslist",
1237 	"ADD UT2004S gslist://master.gamespy.com;portadjust=-10;gsmtype=ut2004 Gslist",
1238 	"ADD UT2004S gslist://master.gamespy.com;portadjust=-10;gsmtype=ut2004d Gslist (Demo)",
1239 	"ADD POSTAL2 gslist://master.gamespy.com;portadjust=-1;gsmtype=postal2 Gslist",
1240 	"ADD POSTAL2 gslist://master.gamespy.com;portadjust=-1;gsmtype=postal2d Gslist (Demo)",
1241 	"ADD AMS gslist://master.gamespy.com;portadjust=-1;gsmtype=armygame Gslist",
1242 	"ADD D3G gslist://master.gamespy.com;gsmtype=descent3 Gslist",
1243 	"ADD SOF2S gslist://master.gamespy.com;gsmtype=sof2 Gslist",
1244 	// "ADD GPS gslist://master.gamespy.com;gsmtype=mohaa Gslist",                          // no fixed port offset
1245 	// "ADD SMS gslist://master.gamespy.com;portadjust=-1;gsmtype=serioussam Gslist",       // not compatible with linux version
1246 	// "ADD SMSSE gslist://master.gamespy.com;portadjust=-1;gsmtype=serioussamse Gslist",   // not compatible with linux version
1247 	NULL
1248 };
1249 
1250 /** parse type of the form <TYPE>[,QSTAT_MASTER_OPION]
1251  * DESTRUCTIVE
1252  * @return qstat_master_option or NULL
1253  */
master_id2type(char * token,enum server_type * type)1254 char* master_id2type(char* token, enum server_type* type) {
1255 	char* ret = NULL;
1256 	char* coma = strchr(token, ',');
1257 
1258 	if (coma) {
1259 		*coma = '\0';
1260 		ret = ++coma;
1261 	}
1262 
1263 	*type = id2type(token);
1264 
1265 	return ret;
1266 }
1267 
1268 /*
1269  *   Format:  Action ServerType URL Description
1270  */
1271 
update_master_list_action(const char * action)1272 static void update_master_list_action (const char *action) {
1273 	char *str;
1274 	char *token[4];
1275 	int n;
1276 	enum server_type type;
1277 	struct master *m;
1278 
1279 	str = strdup_strip (action);
1280 
1281 	if (!str || !*str)
1282 		return;
1283 
1284 	n = tokenize (str, token, 4, " \t\n\r");
1285 
1286 	if (n == 4) {
1287 		char* qstat_query_arg = master_id2type (token[1], &type);
1288 		if (type != UNKNOWN_SERVER) {
1289 
1290 			if (g_ascii_strcasecmp (token[0], ACTION_ADD) == 0) {
1291 				m = add_master (token[2], token[3], type, qstat_query_arg, FALSE, FALSE);
1292 				if (m && source_ctree != NULL)
1293 					source_ctree_add_master (source_ctree, m);
1294 			}
1295 			else if (g_ascii_strcasecmp (token[0], ACTION_DELETE) == 0) {
1296 				m = add_master (token[2], token[3], type, qstat_query_arg, FALSE, TRUE);
1297 				if (m) {
1298 					if (source_ctree != NULL)
1299 						source_ctree_delete_master (source_ctree, m);
1300 					free_master (m);
1301 				}
1302 			}
1303 
1304 		}
1305 	}
1306 
1307 	g_free (str);
1308 }
1309 
1310 
update_master_list_builtin(void)1311 void update_master_list_builtin (void) {
1312 	char **ptr;
1313 
1314 	for (ptr = builtin_masters_update_info; *ptr; ptr++)
1315 		update_master_list_action (*ptr);
1316 }
1317 
update_master_gslist_builtin(void)1318 void update_master_gslist_builtin (void) {
1319 	char **ptr;
1320 
1321 	for (ptr = builtin_gslist_masters_update_info; *ptr; ptr++)
1322 		update_master_list_action (*ptr);
1323 }
1324 
1325 // Refresh list of games and masters in left pane
refresh_source_list(void)1326 void refresh_source_list (void) {
1327 	GtkCTreeNode *node;
1328 	GSList *list;
1329 	struct master *m;
1330 	struct master *group = NULL;
1331 
1332 	// Re-add masters to the list, or remove them if game is not
1333 	// configured and set to show only configured games.
1334 	for (list = all_masters; list; list = list->next) {
1335 		m = (struct master *) list->data;
1336 
1337 		if (m == favorites || m->isgroup)
1338 			continue;
1339 
1340 		// Look for existing entry in ctree so we only re-add if needed,
1341 		// otherwise everthing gets re-expanded
1342 		node = gtk_ctree_find_by_row_data (GTK_CTREE (source_ctree), NULL, m);
1343 
1344 		// If it's not there check to see if we should add it
1345 		if (!node) // It's not in the list so maybe we should add it
1346 		{
1347 			if (!default_show_only_configured_games || games[m->type].cmd)
1348 				source_ctree_add_master (source_ctree, m);
1349 		}
1350 		else // It's in the list so maybe we should delete it
1351 			if (default_show_only_configured_games && !games[m->type].cmd)
1352 				source_ctree_delete_master (source_ctree, m);
1353 	}
1354 	// Remove master group if set to show only configured games.
1355 	for (list = all_masters; list; list = list->next) {
1356 		m = (struct master *) list->data;
1357 
1358 		if (m == favorites)
1359 			continue;
1360 
1361 		if (default_show_only_configured_games && !games[m->type].cmd) {
1362 			group = (struct master *) g_slist_nth_data (master_groups, m->type);
1363 			source_ctree_remove_master_group (source_ctree, group);
1364 		}
1365 	}
1366 }
1367 
update_master_list_web(void)1368 void update_master_list_web (void) {
1369 	char **ptr;
1370 
1371 	for (ptr = builtin_masters_update_info; *ptr; ptr++)
1372 		update_master_list_action (*ptr);
1373 }
1374 
1375 
load_master_list(void)1376 static void load_master_list (void) {
1377 	struct master *m;
1378 	enum server_type type;
1379 	char* qstat_query_arg = NULL;
1380 	char conf[64];
1381 	char *token[3];
1382 	char *str;
1383 	char *tmp;
1384 	int user;
1385 	int i = 0;
1386 	int n;
1387 
1388 	config_push_prefix ("/" CONFIG_FILE "/Master List");
1389 
1390 	while (1) {
1391 		g_snprintf (conf, 64, "master%d", i);
1392 		str = config_get_string (conf);
1393 
1394 		if (!str)
1395 			break;
1396 
1397 		n = tokenize (str, token, 3, " \t\n\r");
1398 
1399 		if (n == 3) {
1400 
1401 			user = FALSE;
1402 			tmp = strrchr (token[0], ',');
1403 			if (tmp) {
1404 				user = (g_ascii_strcasecmp (tmp+1, "USER") == 0);
1405 				if (user)
1406 					*tmp = '\0';
1407 			}
1408 			qstat_query_arg = master_id2type (token[0], &type);
1409 
1410 			if (type != UNKNOWN_SERVER) {
1411 
1412 				/*  Use 'user' mode to insert master to the master list
1413 				 *  and fix m->user after that.
1414 				 */
1415 
1416 				m = add_master (token[1], token[2], type, qstat_query_arg, TRUE, FALSE);
1417 				if (m)
1418 					m->user = user;
1419 			}
1420 
1421 		}
1422 
1423 		g_free (str);
1424 		i++;
1425 	}
1426 
1427 	config_pop_prefix ();
1428 }
1429 
1430 
save_master_list(void)1431 static void save_master_list (void) {
1432 	GSList *list;
1433 	struct master *m;
1434 	char conf[64];
1435 	char typeid[128] = {0};
1436 	char *confstr;
1437 	char *str;
1438 	int n = 0;
1439 
1440 	config_clean_section ("/" CONFIG_FILE "/Master List");
1441 	config_push_prefix ("/" CONFIG_FILE "/Master List");
1442 
1443 	for (list = all_masters; list; list = list->next) {
1444 		m = (struct master *) list->data;
1445 
1446 		if (m == favorites || m->isgroup)
1447 			continue;
1448 
1449 		g_snprintf (conf, 64, "master%d", n);
1450 
1451 		g_snprintf (typeid, sizeof(typeid), "%s%s%s%s",
1452 				type2id (m->type),
1453 				m->_qstat_master_option?",":"",
1454 				m->_qstat_master_option?m->_qstat_master_option:"",
1455 				m->user?",USER":"");
1456 
1457 		str = master_to_url(m);
1458 		confstr = g_strjoin (" ", typeid, str, m->name, NULL);
1459 
1460 		config_set_string (conf, confstr);
1461 
1462 		g_free (str);
1463 		g_free (confstr);
1464 		n++;
1465 	}
1466 
1467 	config_pop_prefix ();
1468 }
1469 
init_masters(int update)1470 void init_masters (int update) {
1471 	typedef void (*server_unref_void)(void*);
1472 	struct master *m;
1473 	struct master *m2;
1474 	int i;
1475 	GSList *list;
1476 
1477 	favorites = create_master (N_("Favorites"), UNKNOWN_SERVER, FALSE);
1478 
1479 	for (i = 0; i < GAMES_TOTAL; i++) {
1480 		m = create_master (games[i].name, i, TRUE);
1481 		master_groups = g_slist_append (master_groups, m);
1482 	}
1483 
1484 	load_master_list ();
1485 
1486 	if (update) {
1487 		update_master_list_builtin ();
1488 		if (have_gslist_masters())
1489 			update_master_gslist_builtin();
1490 		config_sync ();
1491 	}
1492 
1493 	compat_convert_favorites ();
1494 
1495 	read_lists (FILENAME_FAVORITES);
1496 	debug (2, "starting to read server list");
1497 	read_lists (FILENAME_LISTS);
1498 	debug (2, "finished reading server list");
1499 
1500 	// Go through all servers, sort them and then remove duplicates
1501 	// master_add_server now uses server_list_prepend_ndp which does not
1502 	// search for duplicates while adding servers.  This is much faster.
1503 	// Because of this, duplicate checking must be done here.
1504 	// Only working on servers, not uservers.  Lists file only contains IPs.
1505 	debug (2, "Searching for duplicate server entries");
1506 
1507 	// for each master
1508 	for (list = all_masters; list; list = list->next) {
1509 		m2 = (struct master *) list->data;
1510 		if (!m2)
1511 			continue;
1512 		debug (2, "  Working on master: %s",m2->name);
1513 
1514 		m2->servers = slist_sort_remove_dups(m2->servers,(GCompareFunc)server_sorting_helper,(server_unref_void)server_unref);
1515 
1516 	}
1517 
1518 	read_server_info (FILENAME_SRVINFO);
1519 }
1520 
1521 
free_masters(void)1522 void free_masters (void) {
1523 	GSList *tmp;
1524 
1525 	debug (6, "free_masters() --");
1526 	save_favorites ();
1527 
1528 	if (default_save_lists) {
1529 		tmp = g_slist_copy (all_masters);
1530 		tmp = g_slist_remove (tmp, favorites);
1531 		save_lists (tmp, FILENAME_LISTS);
1532 		g_slist_free (tmp);
1533 
1534 		if (default_save_srvinfo) {
1535 			tmp = all_servers (); /* free done in two lines */
1536 			save_server_info (FILENAME_SRVINFO, tmp);
1537 			server_list_free (tmp);
1538 		}
1539 	}
1540 	else {
1541 		save_lists (NULL, FILENAME_LISTS);
1542 		if (default_save_srvinfo) {
1543 			save_server_info (FILENAME_SRVINFO, favorites->servers);
1544 		}
1545 	}
1546 
1547 	save_master_list ();
1548 
1549 	free_master (favorites);
1550 	favorites  = NULL;
1551 
1552 	g_slist_foreach (master_groups, (GFunc) free_master, NULL);
1553 	g_slist_free (master_groups);
1554 	master_groups = NULL;
1555 }
1556 
1557 
master_list_add_sigle(GSList * list,struct master * m)1558 static inline GSList *master_list_add_sigle (GSList *list, struct master *m) {
1559 	if (g_slist_find (list, m) == NULL)
1560 		list = g_slist_append (list, m);
1561 	return list;
1562 }
1563 
1564 
master_list_add(GSList ** masters,GSList ** servers,GSList ** uservers,struct master * m)1565 static void master_list_add (GSList **masters, GSList **servers,
1566 		GSList **uservers, struct master *m) {
1567 	GSList *tmp;
1568 
1569 	debug (6, "master_list_add() -- master '%s'", m->name);
1570 	if (m->isgroup || m == favorites) {
1571 		if (servers) {
1572 			*servers = server_list_append_list (*servers, favorites->servers,
1573 					m->type);
1574 		}
1575 		if (uservers) {
1576 			*uservers = userver_list_append_list (*uservers, favorites->uservers,
1577 					m->type);
1578 		}
1579 	}
1580 
1581 	if (masters) {
1582 		if (m->isgroup) {
1583 			for (tmp = m->masters; tmp; tmp = tmp->next)
1584 				*masters =  master_list_add_sigle (*masters,
1585 						(struct master *) tmp->data);
1586 		}
1587 		else {
1588 			if (m != favorites)
1589 				*masters = master_list_add_sigle (*masters, m);
1590 		}
1591 	}
1592 }
1593 
1594 
collate_server_lists(GSList * masters,GSList ** servers,GSList ** uservers)1595 void collate_server_lists (GSList *masters, GSList **servers,
1596 		GSList **uservers) {
1597 	struct master *m;
1598 	GSList *tmp;
1599 
1600 	for (tmp = masters; tmp; tmp = tmp->next) {
1601 		m = (struct master *) tmp->data;
1602 		if (servers) {
1603 			*servers = server_list_append_list (*servers, m->servers,
1604 					UNKNOWN_SERVER);
1605 		}
1606 		if (uservers) {
1607 			*uservers = userver_list_append_list (*uservers, m->uservers,
1608 					UNKNOWN_SERVER);
1609 		}
1610 	}
1611 }
1612 
1613 
master_selection_to_lists(GSList * list,GSList ** masters,GSList ** servers,GSList ** uservers)1614 void master_selection_to_lists (GSList *list, GSList **masters,
1615 		GSList **servers, GSList **uservers) {
1616 	struct master *m;
1617 	GSList *tmp;
1618 	debug (6, "master_selection_to_lists() --");
1619 	for (tmp = list; tmp; tmp = tmp->next) {
1620 		m = (struct master *) tmp->data;
1621 		uservers_to_servers (&m->uservers, &m->servers);
1622 		master_list_add (masters, servers, uservers, m);
1623 	}
1624 }
1625 
1626 
source_has_masters_to_update(GSList * source)1627 int source_has_masters_to_update (GSList *source) {
1628 	GSList *tmp;
1629 	struct master *m;
1630 
1631 	for (tmp = source; tmp; tmp = tmp->next) {
1632 		m = (struct master *) tmp->data;
1633 		if (m->isgroup) {
1634 			if (source_has_masters_to_update (m->masters))
1635 				return TRUE;
1636 		}
1637 		else {
1638 			if (m != favorites)
1639 				return TRUE;
1640 		}
1641 	}
1642 	return FALSE;
1643 }
1644 
1645 
source_has_masters_to_delete(GSList * source)1646 int source_has_masters_to_delete (GSList *source) {
1647 	GSList *tmp;
1648 	struct master *m;
1649 
1650 	for (tmp = source; tmp; tmp = tmp->next) {
1651 		m = (struct master *) tmp->data;
1652 		if (m != favorites && !m->isgroup)
1653 			return TRUE;
1654 	}
1655 	return FALSE;
1656 }
1657 
1658 
references_to_server(struct server * s)1659 GSList *references_to_server (struct server *s) {
1660 	GSList *list;
1661 	GSList *res = NULL;
1662 	struct master *m;
1663 
1664 	for (list = all_masters; list; list = list->next) {
1665 		m = (struct master *) list->data;
1666 		if (g_slist_find (m->servers, s))
1667 			res = g_slist_append (res, m);
1668 	}
1669 
1670 	return res;
1671 }
1672 
get_master_query_type_from_address(const gchar * address)1673 enum master_query_type get_master_query_type_from_address(const gchar* address) {
1674 	enum master_query_type type;
1675 	// check for known master prefix
1676 	debug(6,"get_master_query_type_from_address(%s)",address);
1677 	for (type=MASTER_NATIVE;type<MASTER_NUM_QUERY_TYPES;type++) {
1678 		if (!g_ascii_strncasecmp(address, master_prefixes[type],
1679 					strlen(master_prefixes[type]))) {
1680 			debug(6,"get_master_query_type_from_address: found %s",master_prefixes[type]);
1681 			return type;
1682 		}
1683 	}
1684 	// only accept if there is no :// part at all
1685 	if (lowcasestrstr(address,"://")) {
1686 		debug(6,"get_master_query_type_from_address: invalid");
1687 		return MASTER_INVALID_TYPE;
1688 	}
1689 	else {
1690 		debug(6,"get_master_query_type_from_address: default native");
1691 		return MASTER_NATIVE;
1692 	}
1693 }
1694 
have_gslist_masters()1695 gboolean have_gslist_masters() {
1696 	GSList *list;
1697 	struct master *m;
1698 
1699 	for (list = all_masters; list; list = list->next) {
1700 		m = (struct master *) list->data;
1701 		if (m->master_type == MASTER_GSLIST)
1702 			return TRUE;
1703 	}
1704 
1705 	return FALSE;
1706 }
1707 
have_gslist_installed()1708 gboolean have_gslist_installed() {
1709 	char* gslist = find_file_in_path("gslist");
1710 	g_free(gslist);
1711 	return (gslist != NULL);
1712 }
1713 
master_remove_server(struct master * m,struct server * s)1714 void master_remove_server(struct master* m, struct server* s) {
1715 	if (!m || !s)
1716 		return;
1717 
1718 	m->servers = server_list_remove(m->servers, s);
1719 }
1720 
server_remove_from_all(struct server * s)1721 void server_remove_from_all(struct server *s) {
1722 	GSList *list;
1723 	struct master *m;
1724 
1725 	for (list = all_masters; list; list = list->next) {
1726 		m = (struct master *) list->data;
1727 
1728 		if (m == favorites)
1729 			continue;
1730 
1731 		m->servers = server_list_remove(m->servers, s);
1732 	}
1733 }
1734