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