3
3
* Author: Brent Hendricks
4
4
* Project: GGZ Core Client Lib
6
* $Id: netxml.c,v 1.67 2003/05/10 06:52:48 dr_maux Exp $
6
* $Id: netxml.c 7909 2006-03-14 13:50:27Z josef $
8
8
* Code for parsing XML streamed from the server
10
10
* Copyright (C) 2000 Brent Hendricks.
12
* This program is free software; you can redistribute it and/or modify
13
* it under the terms of the GNU General Public License as published by
14
* the Free Software Foundation; either version 2 of the License, or
15
* (at your option) any later version.
17
* This program is distributed in the hope that it will be useful,
12
* This library is free software; you can redistribute it and/or
13
* modify it under the terms of the GNU Lesser General Public
14
* License as published by the Free Software Foundation; either
15
* version 2.1 of the License, or (at your option) any later version.
17
* This library is distributed in the hope that it will be useful,
18
18
* but WITHOUT ANY WARRANTY; without even the implied warranty of
19
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20
* GNU General Public License for more details.
22
* You should have received a copy of the GNU General Public License
23
* along with this program; if not, write to the Free Software
24
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
19
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
20
* Lesser General Public License for more details.
22
* You should have received a copy of the GNU Lesser General Public
23
* License along with this library; if not, write to the Free Software
24
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
27
27
#ifdef HAVE_CONFIG_H
28
# include <config.h> /* Site-specific config */
28
# include <config.h> /* Site-specific config */
33
33
#include <limits.h>
36
35
#include <stdlib.h>
37
36
#include <string.h>
38
38
#include <sys/stat.h>
39
39
#include <sys/types.h>
40
40
#include <unistd.h>
42
#ifdef HAVE_WINSOCK2_H
43
# include <winsock2.h>
44
48
#include <ggz_common.h>
125
139
/* Callbacks for XML parser */
126
static void _ggzcore_net_parse_start_tag(void *, const char*, const char **);
140
static void _ggzcore_net_parse_start_tag(void *data, const char *el,
127
142
static void _ggzcore_net_parse_end_tag(void *data, const char *el);
128
143
static void _ggzcore_net_parse_text(void *data, const char *text, int len);
129
static GGZXMLElement* _ggzcore_net_new_element(char *tag, char **attrs);
144
static GGZXMLElement *_ggzcore_net_new_element(const char *tag,
145
const char *const *attrs);
131
147
/* Handler functions for various tags */
132
static void _ggzcore_net_handle_server(GGZNet*, GGZXMLElement*);
133
static void _ggzcore_net_handle_options(GGZNet*, GGZXMLElement*);
134
static void _ggzcore_net_handle_motd(GGZNet*, GGZXMLElement*);
135
static void _ggzcore_net_handle_result(GGZNet*, GGZXMLElement*);
136
static void _ggzcore_net_handle_password(GGZNet *net, GGZXMLElement*);
137
static void _ggzcore_net_handle_list(GGZNet*, GGZXMLElement*);
138
static void _ggzcore_net_handle_update(GGZNet*, GGZXMLElement*);
139
static void _ggzcore_net_handle_game(GGZNet*, GGZXMLElement*);
140
static void _ggzcore_net_handle_protocol(GGZNet*, GGZXMLElement*);
141
static void _ggzcore_net_handle_allow(GGZNet*, GGZXMLElement*);
142
static void _ggzcore_net_handle_about(GGZNet*, GGZXMLElement*);
143
static void _ggzcore_net_handle_desc(GGZNet*, GGZXMLElement*);
144
static void _ggzcore_net_handle_room(GGZNet*, GGZXMLElement*);
145
static void _ggzcore_net_handle_player(GGZNet*, GGZXMLElement*);
146
static void _ggzcore_net_handle_table(GGZNet*, GGZXMLElement*);
147
static void _ggzcore_net_handle_seat(GGZNet*, GGZXMLElement*);
148
static void _ggzcore_net_handle_spectator_seat(GGZNet *net,
149
GGZXMLElement *seat);
150
static void _ggzcore_net_handle_chat(GGZNet*, GGZXMLElement*);
151
static void _ggzcore_net_handle_leave(GGZNet *net, GGZXMLElement *element);
152
static void _ggzcore_net_handle_join(GGZNet *net, GGZXMLElement *element);
153
static void _ggzcore_net_handle_ping(GGZNet*, GGZXMLElement*);
154
static void _ggzcore_net_handle_session(GGZNet*, GGZXMLElement*);
148
static void _ggzcore_net_handle_server(GGZNet *, GGZXMLElement *);
149
static void _ggzcore_net_handle_options(GGZNet *, GGZXMLElement *);
150
static void _ggzcore_net_handle_motd(GGZNet *, GGZXMLElement *);
151
static void _ggzcore_net_handle_result(GGZNet *, GGZXMLElement *);
152
static void _ggzcore_net_handle_password(GGZNet * net, GGZXMLElement *);
153
static void _ggzcore_net_handle_list(GGZNet *, GGZXMLElement *);
154
static void _ggzcore_net_handle_update(GGZNet *, GGZXMLElement *);
155
static void _ggzcore_net_handle_game(GGZNet *, GGZXMLElement *);
156
static void _ggzcore_net_handle_protocol(GGZNet *, GGZXMLElement *);
157
static void _ggzcore_net_handle_allow(GGZNet *, GGZXMLElement *);
158
static void _ggzcore_net_handle_about(GGZNet *, GGZXMLElement *);
159
static void _ggzcore_net_handle_bot(GGZNet *, GGZXMLElement *);
160
static void _ggzcore_net_handle_desc(GGZNet *, GGZXMLElement *);
161
static void _ggzcore_net_handle_room(GGZNet *, GGZXMLElement *);
162
static void _ggzcore_net_handle_player(GGZNet *, GGZXMLElement *);
163
static void _ggzcore_net_handle_table(GGZNet *, GGZXMLElement *);
164
static void _ggzcore_net_handle_seat(GGZNet *, GGZXMLElement *);
165
static void _ggzcore_net_handle_spectator_seat(GGZNet * net,
166
GGZXMLElement * seat);
167
static void _ggzcore_net_handle_chat(GGZNet *, GGZXMLElement *);
168
static void _ggzcore_net_handle_info(GGZNet *, GGZXMLElement *);
169
static void _ggzcore_net_handle_playerinfo(GGZNet *, GGZXMLElement *);
170
static void _ggzcore_net_handle_leave(GGZNet * net,
171
GGZXMLElement * element);
172
static void _ggzcore_net_handle_join(GGZNet * net,
173
GGZXMLElement * element);
174
static void _ggzcore_net_handle_ping(GGZNet *, GGZXMLElement *);
175
static void _ggzcore_net_handle_session(GGZNet *, GGZXMLElement *);
156
177
/* Extra functions fot handling data associated with specific tags */
157
static void _ggzcore_net_list_insert(GGZXMLElement*, void*);
158
static GGZGameData *_ggzcore_net_game_get_data(GGZXMLElement *game);
159
static void _ggzcore_net_game_set_protocol(GGZXMLElement*, char*, char *);
160
static void _ggzcore_net_game_set_allowed(GGZXMLElement*,
161
GGZNumberList, GGZNumberList, int);
162
static void _ggzcore_net_game_set_info(GGZXMLElement*, char*, char *);
163
static void _ggzcore_net_game_set_desc(GGZXMLElement*, char*);
164
static void _ggzcore_net_table_add_seat(GGZXMLElement*, struct _GGZSeat*,
178
static void _ggzcore_net_list_insert(GGZXMLElement *, void *);
179
static GGZGameData *_ggzcore_net_game_get_data(GGZXMLElement * game);
180
static void _ggzcore_net_game_set_protocol(GGZXMLElement * game,
182
const char *version);
183
static void _ggzcore_net_game_set_allowed(GGZXMLElement *,
184
GGZNumberList, GGZNumberList,
186
static void _ggzcore_net_game_set_info(GGZXMLElement *, const char *,
188
static void _ggzcore_net_game_add_bot(GGZXMLElement *, const char *,
190
static void _ggzcore_net_game_set_desc(GGZXMLElement *, char *);
191
static void _ggzcore_net_table_add_seat(GGZXMLElement *, GGZTableSeat *,
166
static void _ggzcore_net_player_update(GGZNet *net, GGZXMLElement *update, char *action);
167
static void _ggzcore_net_table_update(GGZNet *net, GGZXMLElement *update, char *action);
168
static GGZTableData *_ggzcore_net_table_get_data(GGZXMLElement *table);
169
static void _ggzcore_net_table_set_desc(GGZXMLElement*, char*);
170
static GGZTableData* _ggzcore_net_tabledata_new(void);
171
static void _ggzcore_net_tabledata_free(GGZTableData*);
172
static struct _GGZSeat* _ggzcore_net_seat_copy(struct _GGZSeat *orig);
173
static void _ggzcore_net_seat_free(struct _GGZSeat*);
193
static void _ggzcore_net_room_update(GGZNet * net, GGZXMLElement * update,
195
static void _ggzcore_net_player_update(GGZNet * net,
196
GGZXMLElement * update,
198
static void _ggzcore_net_table_update(GGZNet * net, GGZXMLElement * update,
200
static GGZTableData *_ggzcore_net_table_get_data(GGZXMLElement * table);
201
static void _ggzcore_net_table_set_desc(GGZXMLElement *, char *);
202
static GGZTableData *_ggzcore_net_tabledata_new(void);
203
static void _ggzcore_net_tabledata_free(GGZTableData *);
204
static GGZTableSeat *_ggzcore_net_seat_copy(GGZTableSeat * orig);
205
static void _ggzcore_net_seat_free(GGZTableSeat *);
207
static GGZPlayerInfoData *_ggzcore_net_playerinfo_get_data(GGZXMLElement * game);
208
static void _ggzcore_net_playerinfo_add_seat(GGZXMLElement *, int,
209
const char *, const char *, const char *);
175
211
/* Trigger network error event */
176
static void _ggzcore_net_error(GGZNet *net, char* message);
212
static void _ggzcore_net_error(GGZNet * net, char *message);
178
214
/* Dump network data to debugging file */
179
static void _ggzcore_net_dump_data(GGZNet *net, char *data, int size);
180
static void _ggzcore_net_negotiate_tls(GGZNet *net);
215
static void _ggzcore_net_dump_data(GGZNet * net, char *data, int size);
216
static void _ggzcore_net_negotiate_tls(GGZNet * net);
182
218
/* Utility functions */
183
static int _ggzcore_net_send_table_seat(GGZNet *net, struct _GGZSeat *seat);
184
static void _ggzcore_net_send_header(GGZNet *net);
185
static int _ggzcore_net_send_pong(GGZNet *net, const char *id);
186
static int _ggzcore_net_send_line(GGZNet *net, char *line, ...)
187
ggz__attribute((format(printf, 2, 3)));
219
static int _ggzcore_net_send_table_seat(GGZNet * net, GGZTableSeat * seat);
220
static void _ggzcore_net_send_header(GGZNet * net);
221
static int _ggzcore_net_send_pong(GGZNet * net, const char *id);
222
static int _ggzcore_net_send_line(GGZNet * net, char *line, ...)
223
ggz__attribute((format(printf, 2, 3)));
188
224
static int str_to_int(const char *str, int dflt);
192
228
/* Internal library functions (prototypes in net.h) */
194
GGZNet* _ggzcore_net_new(void)
230
GGZNet *_ggzcore_net_new(void)
198
234
net = ggz_malloc(sizeof(GGZNet));
200
236
/* Set fd to invalid value */
238
net->dump_file = NULL;
203
239
net->use_tls = -1;
209
void _ggzcore_net_init(GGZNet *net, GGZServer *server,
210
const char *host, unsigned int port, unsigned int use_tls)
245
void _ggzcore_net_init(GGZNet * net, GGZServer * server,
246
const char *host, unsigned int port,
247
unsigned int use_tls)
212
249
net->server = server;
213
250
net->host = ggz_strdup(host);
305
344
/* FIXME: set a timeout for connecting */
306
int _ggzcore_net_connect(GGZNet *net)
345
int _ggzcore_net_connect(GGZNet * net)
308
347
ggz_debug(GGZCORE_DBG_NET, "Connecting to %s:%d",
309
348
net->host, net->port);
310
349
net->fd = ggz_make_socket(GGZ_SOCK_CLIENT, net->port, net->host);
312
351
if (net->fd >= 0)
313
return 0; /* success */
352
return 0; /* success */
315
return net->fd; /* error */
354
return net->fd; /* error */
319
void _ggzcore_net_disconnect(GGZNet *net)
358
void _ggzcore_net_disconnect(GGZNet * net)
321
360
ggz_debug(GGZCORE_DBG_NET, "Disconnecting");
361
#ifdef HAVE_WINSOCK2_H
362
closesocket(net->fd);
370
/* Helper function, might go into libggz*/
371
char *_ggz_xml_cdata_escape(const char *str)
382
for(p = str; *p != '\0'; p++) {
383
if((*p == ']') && (*(p + 1) == ']') && (*(p + 2) == '>')) {
388
if(len == strlen(str))
389
return ggz_strdup(str);
391
q = new = ggz_malloc(len + 1);
392
for(p = str; *p != '\0'; p++) {
393
if((*p == ']') && (*(p + 1) == ']') && (*(p + 2) == '>')) {
394
memcpy(q, "]]>", 6);
408
/* Helper function, might go into libggz*/
409
char *_ggz_xml_cdata_unescape(const char *str)
420
for(p = str; *p != '\0'; p++) {
421
if(!strncmp(p, "]]>", 6)) {
428
if(len == strlen(str))
429
return ggz_strdup(str);
431
q = new = ggz_malloc(len + 1);
432
for(p = str; *p != '\0'; p++) {
433
if(!strncmp(p, "]]>", 6)) {
327
450
/* ggzcore_net_send_XXX() functions for sending messages to the server */
329
int _ggzcore_net_send_login(GGZNet *net)
452
/* Sends login packet. Login type is an enumerated value. Password is needed
453
* only for registered logins. */
454
int _ggzcore_net_send_login(GGZNet * net, GGZLoginType login_type,
455
const char *handle, const char *password, const char *email,
456
const char *language)
331
GGZLoginType login_type;
332
char *type, *handle, *password, *language;
458
const char *type = "guest";
461
char *password_quoted;
335
login_type = _ggzcore_server_get_type(net->server);
336
handle = _ggzcore_server_get_handle(net->server);
337
password = _ggzcore_server_get_password(net->server);
339
464
switch (login_type) {
383
int _ggzcore_net_send_motd(GGZNet *net)
525
int _ggzcore_net_send_motd(GGZNet * net)
387
ggz_debug(GGZCORE_DBG_NET, "Sending MOTD request");
529
ggz_debug(GGZCORE_DBG_NET, "Sending MOTD request");
388
530
_ggzcore_net_send_line(net, "<MOTD/>");
394
int _ggzcore_net_send_list_types(GGZNet *net, const char verbose)
536
int _ggzcore_net_send_list_types(GGZNet * net, const char verbose)
399
541
net->gametype_verbose = verbose;
401
ggz_debug(GGZCORE_DBG_NET, "Sending gametype list request");
543
ggz_debug(GGZCORE_DBG_NET, "Sending gametype list request");
402
544
full = bool_to_str(verbose);
404
546
_ggzcore_net_send_line(net, "<LIST TYPE='game' FULL='%s'/>", full);
410
int _ggzcore_net_send_list_rooms(GGZNet *net, const int type, const char verbose)
552
int _ggzcore_net_send_list_rooms(GGZNet * net, const int type,
415
558
net->room_verbose = verbose;
416
ggz_debug(GGZCORE_DBG_NET, "Sending room list request");
559
ggz_debug(GGZCORE_DBG_NET, "Sending room list request");
417
560
full = bool_to_str(verbose);
419
562
_ggzcore_net_send_line(net, "<LIST TYPE='room' FULL='%s'/>", full);
425
int _ggzcore_net_send_join_room(GGZNet *net, const unsigned int id)
568
int _ggzcore_net_send_join_room(GGZNet * net, const unsigned int room_id)
430
room = _ggzcore_server_get_room_by_id(net->server, id);
431
net->new_room = room;
433
ggz_debug(GGZCORE_DBG_NET, "Sending room join request");
435
_ggzcore_net_send_line(net, "<ENTER ROOM='%d'/>", id);
570
ggz_debug(GGZCORE_DBG_NET, "Sending room %d join request",
572
return _ggzcore_net_send_line(net, "<ENTER ROOM='%d'/>", room_id);
441
int _ggzcore_net_send_list_players(GGZNet *net)
576
int _ggzcore_net_send_list_players(GGZNet * net)
445
580
ggz_debug(GGZCORE_DBG_NET, "Sending player list request");
531
int _ggzcore_net_send_table_launch(GGZNet *net, GGZTable *table)
674
int _ggzcore_net_send_player_info(GGZNet * net, int seat_num)
678
ggz_debug(GGZCORE_DBG_NET, "Sending player info request");
680
if (seat_num == -1) {
681
result = _ggzcore_net_send_line(net,
684
result = _ggzcore_net_send_line(net,
693
int _ggzcore_net_send_table_launch(GGZNet * net, GGZTable * table)
533
695
int i, type, num_seats, status = 0;
536
699
ggz_debug(GGZCORE_DBG_NET, "Sending table launch request");
538
type = _ggzcore_gametype_get_id(_ggzcore_table_get_type(table));
539
desc = _ggzcore_table_get_desc(table);
540
num_seats = _ggzcore_table_get_num_seats(table);
701
type = ggzcore_gametype_get_id(ggzcore_table_get_type(table));
702
desc = ggzcore_table_get_desc(table);
703
num_seats = ggzcore_table_get_num_seats(table);
542
705
_ggzcore_net_send_line(net, "<LAUNCH>");
543
_ggzcore_net_send_line(net, "<TABLE GAME='%d' SEATS='%d'>", type, num_seats);
706
_ggzcore_net_send_line(net, "<TABLE GAME='%d' SEATS='%d'>", type,
709
desc_quoted = ggz_xml_escape(desc);
545
_ggzcore_net_send_line(net, "<DESC>%s</DESC>", desc);
547
for (i = 0; i < num_seats; i++)
548
_ggzcore_net_send_table_seat(net, _ggzcore_table_get_nth_seat(table, i));
712
_ggzcore_net_send_line(net, "<DESC>%s</DESC>", desc_quoted);
715
ggz_free(desc_quoted);
717
for (i = 0; i < num_seats; i++) {
718
GGZTableSeat seat = _ggzcore_table_get_nth_seat(table, i);
720
_ggzcore_net_send_table_seat(net, &seat);
550
723
_ggzcore_net_send_line(net, "</TABLE>");
551
724
_ggzcore_net_send_line(net, "</LAUNCH>");
557
static int _ggzcore_net_send_table_seat(GGZNet *net,
558
struct _GGZSeat *seat)
730
static int _ggzcore_net_send_table_seat(GGZNet * net, GGZTableSeat * seat)
560
732
const char *type;
562
734
ggz_debug(GGZCORE_DBG_NET, "Sending seat info");
564
736
type = ggz_seattype_to_string(seat->type);
567
739
return _ggzcore_net_send_line(net,
568
"<SEAT NUM='%d' TYPE='%s'/>",
740
"<SEAT NUM='%d' TYPE='%s'/>",
569
741
seat->index, type);
570
return _ggzcore_net_send_line(net,
571
"<SEAT NUM='%d' TYPE='%s'>%s</SEAT>",
572
seat->index,type, seat->name);
742
return _ggzcore_net_send_line(net,
743
"<SEAT NUM='%d' TYPE='%s'>%s</SEAT>",
744
seat->index, type, seat->name);
576
int _ggzcore_net_send_table_join(GGZNet *net,
577
const unsigned int num,
748
int _ggzcore_net_send_table_join(GGZNet * net,
749
const unsigned int num, int spectator)
580
751
ggz_debug(GGZCORE_DBG_NET, "Sending table join request");
581
return _ggzcore_net_send_line(net, "<JOIN TABLE='%d' SPECTATOR='%s'/>",
752
return _ggzcore_net_send_line(net,
753
"<JOIN TABLE='%d' SPECTATOR='%s'/>",
582
754
num, bool_to_str(spectator));
585
int _ggzcore_net_send_table_leave(GGZNet *net,
757
int _ggzcore_net_send_table_leave(GGZNet * net, int force, int spectator)
589
759
ggz_debug(GGZCORE_DBG_NET, "Sending table leave request");
590
760
return _ggzcore_net_send_line(net,
647
int _ggzcore_net_send_table_desc_update(GGZNet *net, GGZTable *table,
822
int _ggzcore_net_send_table_desc_update(GGZNet * net, GGZTable * table,
648
823
const char *desc)
650
ggz_debug(GGZCORE_DBG_NET, "Sending table description update request");
825
const GGZRoom *room = ggzcore_table_get_room(table);
826
int room_id = ggzcore_room_get_id(room);
827
int id = ggzcore_table_get_id(table);
830
ggz_debug(GGZCORE_DBG_NET,
831
"Sending table description update request");
651
832
_ggzcore_net_send_line(net,
652
833
"<UPDATE TYPE='table' ACTION='desc' ROOM='%d'>",
653
_ggzcore_room_get_id(table->room));
654
_ggzcore_net_send_line(net, "<TABLE ID='%d'>", table->id);
655
_ggzcore_net_send_line(net, "<DESC>%s</DESC>", desc);
836
desc_quoted = ggz_xml_escape(desc);
838
_ggzcore_net_send_line(net, "<TABLE ID='%d'>", id);
839
_ggzcore_net_send_line(net, "<DESC>%s</DESC>", desc_quoted);
656
840
_ggzcore_net_send_line(net, "</TABLE>");
657
return _ggzcore_net_send_line(net, "</UPDATE>");
842
ggz_free(desc_quoted);
844
return _ggzcore_net_send_line(net, "</UPDATE>");
661
int _ggzcore_net_send_table_boot_update(GGZNet *net, GGZTable *table,
662
struct _GGZSeat *seat)
848
int _ggzcore_net_send_table_boot_update(GGZNet * net, GGZTable * table,
664
ggz_debug(GGZCORE_DBG_NET, "Sending boot of player %s.", seat->name);
851
const GGZRoom *room = ggzcore_table_get_room(table);
852
int room_id = ggzcore_room_get_id(room);
853
int id = ggzcore_table_get_id(table);
855
ggz_debug(GGZCORE_DBG_NET, "Sending boot of player %s.",
683
int _ggzcore_net_send_logout(GGZNet *net)
875
int _ggzcore_net_send_logout(GGZNet * net)
685
ggz_debug(GGZCORE_DBG_NET, "Sending LOGOUT");
877
ggz_debug(GGZCORE_DBG_NET, "Sending LOGOUT");
686
878
return _ggzcore_net_send_line(net, "</SESSION>");
690
882
/* Check for incoming data */
691
int _ggzcore_net_data_is_pending(GGZNet *net)
883
int _ggzcore_net_data_is_pending(GGZNet * net)
694
struct pollfd fd[1] = {{net->fd, POLLIN, 0}};
696
885
if (net && net->fd >= 0) {
698
ggz_debug(GGZCORE_DBG_POLL, "Checking for net events");
699
if ( (pending = poll(fd, 1, 0)) < 0) {
890
FD_ZERO(&read_fd_set);
891
FD_SET(net->fd, &read_fd_set);
893
tv.tv_sec = tv.tv_usec = 0;
895
ggz_debug(GGZCORE_DBG_POLL, "Checking for net events");
897
select(net->fd + 1, &read_fd_set, NULL, NULL, &tv);
701
900
/* Ignore interruptions */
704
ggz_error_sys_exit("poll failed in ggzcore_server_data_is_pending");
904
("select failed in ggzcore_server_data_is_pending");
905
} else if (result > 0) {
707
906
ggz_debug(GGZCORE_DBG_POLL, "Found a net event!");
714
/* Read in a bit more from the server and send it to the parser */
715
int _ggzcore_net_read_data(GGZNet *net)
915
/* Read in a bit more from the server and send it to the parser. The return
916
* value seems to mean nothing (???). */
917
int _ggzcore_net_read_data(GGZNet * net)
720
922
/* We're already in a parse call, and XML parsing is *not* reentrant */
724
926
/* Set flag in case we get called recursively */
956
1176
/* Functions for <MOTD> tag */
957
static void _ggzcore_net_handle_motd(GGZNet *net, GGZXMLElement *element)
1177
static void _ggzcore_net_handle_motd(GGZNet * net, GGZXMLElement * element)
959
char *message, *priority;
1179
const char *message, *priority, *url;
1180
GGZMotdEventData motd;
962
1182
message = ggz_xmlelement_get_text(element);
963
1183
priority = ATTR(element, "PRIORITY");
1184
url = ATTR(element, "URL");
965
1186
ggz_debug(GGZCORE_DBG_NET, "Motd of priority %s", priority);
967
1188
/* In the old interface the MOTD was sent a line at a time,
968
1189
NULL-terminated. Now it's just sent all at once. */
969
buffer = ggz_malloc(2 * sizeof(char*));
1190
if (url && strlen(url) == 0) url = NULL;
1191
motd.motd = message;
973
1194
/* FIXME: do something with the priority */
974
_ggzcore_server_event(net->server, GGZ_MOTD_LOADED, buffer);
1195
_ggzcore_server_event(net->server, GGZ_MOTD_LOADED, &motd);
980
1199
/* Functions for <RESULT> tag */
981
static void _ggzcore_net_handle_result(GGZNet *net, GGZXMLElement *element)
1200
static void _ggzcore_net_handle_result(GGZNet * net,
1201
GGZXMLElement * element)
985
1205
GGZClientReqError code;
989
if (!element) return;
991
1212
action = ATTR(element, "ACTION");
992
1213
code = ggz_string_to_error(ATTR(element, "CODE"));
993
1214
data = ggz_xmlelement_get_data(element);
995
ggz_debug(GGZCORE_DBG_NET, "Result of %s was %d", action,
1216
ggz_debug(GGZCORE_DBG_NET, "Result of %s was %d", action, code);
998
1218
room = _ggzcore_server_get_cur_room(net->server);
1001
1221
/* Password may have already been updated. */
1002
1222
_ggzcore_server_set_login_status(net->server, code);
1003
1223
} else if (strcasecmp(action, "enter") == 0) {
1005
_ggzcore_server_set_room(net->server, net->new_room);
1006
1224
_ggzcore_server_set_room_join_status(net->server, code);
1007
} else if (strcasecmp(action, "launch") == 0)
1225
} else if (strcasecmp(action, "launch") == 0)
1008
1226
_ggzcore_room_set_table_launch_status(room, code);
1009
else if (strcasecmp(action, "join") == 0)
1227
else if (strcasecmp(action, "join") == 0)
1010
1228
_ggzcore_room_set_table_join_status(room, code);
1011
else if (strcasecmp(action, "leave") == 0)
1229
else if (strcasecmp(action, "leave") == 0)
1012
1230
_ggzcore_room_set_table_leave_status(room, code);
1013
else if (strcasecmp(action, "chat") == 0) {
1231
else if (strcasecmp(action, "chat") == 0) {
1014
1232
if (code != E_OK) {
1015
GGZErrorEventData error = {status: code};
1233
GGZErrorEventData error = { status:code };
1017
1235
switch (code) {
1018
1236
case E_NOT_IN_ROOM:
1019
snprintf(error.message, sizeof(error.message),
1237
snprintf(error.message,
1238
sizeof(error.message),
1020
1239
"Not in a room");
1022
1241
case E_BAD_OPTIONS:
1023
snprintf(error.message, sizeof(error.message),
1242
snprintf(error.message,
1243
sizeof(error.message),
1024
1244
"Bad options");
1026
1246
case E_NO_PERMISSION:
1027
snprintf(error.message, sizeof(error.message),
1247
snprintf(error.message,
1248
sizeof(error.message),
1030
1251
case E_USR_LOOKUP:
1031
snprintf(error.message, sizeof(error.message),
1252
snprintf(error.message,
1253
sizeof(error.message),
1032
1254
"No such player");
1034
1256
case E_AT_TABLE:
1035
snprintf(error.message, sizeof(error.message),
1257
snprintf(error.message,
1258
sizeof(error.message),
1036
1259
"Can't chat at table");
1262
snprintf(error.message,
1263
sizeof(error.message),
1264
"Must be at table");
1039
snprintf(error.message, sizeof(error.message),
1267
snprintf(error.message,
1268
sizeof(error.message),
1040
1269
"Unknown error");
1123
1353
_ggzcore_server_init_roomlist(net->server, count);
1125
1355
for (entry = ggz_list_head(list);
1127
entry = ggz_list_next(entry)) {
1356
entry; entry = ggz_list_next(entry)) {
1128
1357
_ggzcore_server_add_room(net->server,
1129
1358
ggz_list_get_data(entry));
1131
1360
_ggzcore_server_event(net->server, GGZ_ROOM_LIST, NULL);
1132
1361
} else if (strcasecmp(type, "game") == 0) {
1133
/* Free previous list of types*/
1362
/* Free previous list of types */
1134
1363
if (ggzcore_server_get_num_gametypes(net->server) > 0)
1135
1364
_ggzcore_server_free_typelist(net->server);
1137
1366
_ggzcore_server_init_typelist(net->server, count);
1138
1367
for (entry = ggz_list_head(list);
1140
entry = ggz_list_next(entry)) {
1368
entry; entry = ggz_list_next(entry)) {
1141
1369
_ggzcore_server_add_type(net->server,
1142
1370
ggz_list_get_data(entry));
1144
1372
_ggzcore_server_event(net->server, GGZ_TYPE_LIST, NULL);
1145
1373
} else if (strcasecmp(type, "player") == 0) {
1146
room = _ggzcore_server_get_room_by_id(net->server, room_num);
1375
_ggzcore_server_get_room_by_id(net->server, room_num);
1147
1376
_ggzcore_room_set_player_list(room, count, list);
1148
list = NULL; /* avoid freeing list */
1377
list = NULL; /* avoid freeing list */
1149
1378
} else if (strcasecmp(type, "table") == 0) {
1150
room = _ggzcore_server_get_room_by_id(net->server, room_num);
1380
_ggzcore_server_get_room_by_id(net->server, room_num);
1151
1381
_ggzcore_room_set_table_list(room, count, list);
1152
list = NULL; /* avoid freeing list */
1382
list = NULL; /* avoid freeing list */
1447
/* Handle room update. */
1448
static void _ggzcore_net_room_update(GGZNet * net, GGZXMLElement * update,
1451
GGZRoom *roomdata, *room;
1454
roomdata = ggz_xmlelement_get_data(update);
1457
id = ggzcore_room_get_id(roomdata);
1458
room = _ggzcore_server_get_room_by_id(net->server, id);
1461
if (strcasecmp(action, "players") == 0) {
1462
players = ggzcore_room_get_num_players(roomdata);
1463
_ggzcore_room_set_players(room, players);
1467
_ggzcore_room_free(roomdata);
1218
1471
/* Handle Player update */
1219
static void _ggzcore_net_player_update(GGZNet *net, GGZXMLElement *update,
1472
static void _ggzcore_net_player_update(GGZNet * net,
1473
GGZXMLElement * update,
1223
1477
GGZPlayer *player;
1479
const char *player_name;
1226
1481
room_num = str_to_int(ATTR(update, "ROOM"), -1);
1228
1483
player = ggz_xmlelement_get_data(update);
1487
player_name = ggzcore_player_get_name(player);
1229
1489
room = _ggzcore_server_get_room_by_id(net->server, room_num);
1231
if (strcasecmp(action, "add") == 0)
1232
_ggzcore_room_add_player(room, player);
1233
else if (strcasecmp(action, "delete") == 0)
1234
_ggzcore_room_remove_player(room, player->name);
1235
else if (strcasecmp(action, "lag") == 0) {
1491
_ggzcore_player_free(player);
1496
if (strcasecmp(action, "add") == 0) {
1497
int from_room = str_to_int(ATTR(update, "FROMROOM"), -2);
1499
_ggzcore_room_add_player(room, player, from_room);
1500
} else if (strcasecmp(action, "delete") == 0) {
1501
int to_room = str_to_int(ATTR(update, "TOROOM"), -2);
1503
_ggzcore_room_remove_player(room, player_name, to_room);
1504
} else if (strcasecmp(action, "lag") == 0) {
1236
1505
/* FIXME: Should be a player "class-based" event */
1237
_ggzcore_room_set_player_lag(room, player->name, player->lag);
1506
int lag = ggzcore_player_get_lag(player);
1508
_ggzcore_room_set_player_lag(room, player_name, lag);
1238
1509
} else if (strcasecmp(action, "stats") == 0) {
1239
1510
/* FIXME: Should be a player "class-based" event */
1240
1511
_ggzcore_room_set_player_stats(room, player);
1292
1564
if (strcasecmp(action, "add") == 0) {
1293
1565
_ggzcore_room_add_table(room, table_data);
1294
1566
/* Set table_data to NULL so it doesn't get freed at
1295
the end of this function. You would think this wouldn't
1567
the end of this function. You would think this wouldn't
1296
1568
be necessary (since the table is inserted into a list,
1297
1569
which should copy it), but it appears as though it is. */
1298
1570
table_data = NULL;
1299
1571
} else if (strcasecmp(action, "delete") == 0)
1300
_ggzcore_room_remove_table(room, table_data->id);
1572
_ggzcore_room_remove_table(room, table_id);
1301
1573
else if (strcasecmp(action, "join") == 0) {
1302
1574
/* Loop over both seats and spectators. */
1303
for (i = 0; i < table_data->num_seats; i++)
1304
if (table_data->seats[i].type != GGZ_SEAT_NONE)
1305
_ggzcore_table_set_seat(table,
1306
&table_data->seats[i]);
1307
for (i = 0; i < table_data->num_spectator_seats; i++) {
1308
if (table_data->spectator_seats[i].name) {
1575
for (i = 0; i < ggzcore_table_get_num_seats(table_data);
1578
_ggzcore_table_get_nth_seat(table_data, i);
1580
if (seat.type != GGZ_SEAT_NONE) {
1581
_ggzcore_table_set_seat(table, &seat);
1585
i < ggzcore_table_get_num_spectator_seats(table_data);
1587
GGZTableSeat spectator
1588
= _ggzcore_table_get_nth_spectator_seat
1591
if (spectator.name) {
1309
1592
_ggzcore_table_set_spectator_seat(table,
1310
&table_data->spectator_seats[i]);
1313
1596
} else if (strcasecmp(action, "leave") == 0) {
1314
1597
/* The server sends us the player that is leaving - that
1315
1598
is, a GGZ_SEAT_PLAYER not a GGZ_SEAT_OPEN. It may
1316
1599
be either a regular or a spectator seat. */
1317
for (i = 0; i < table_data->num_seats; i++) {
1318
if (table_data->seats[i].type != GGZ_SEAT_NONE) {
1600
for (i = 0; i < ggzcore_table_get_num_seats(table_data);
1602
GGZTableSeat leave_seat =
1603
_ggzcore_table_get_nth_seat(table_data, i);
1605
if (leave_seat.type != GGZ_SEAT_NONE) {
1319
1606
/* Player is vacating seat */
1320
struct _GGZSeat seat;
1321
1608
seat.index = i;
1322
1609
seat.type = GGZ_SEAT_OPEN;
1323
1610
seat.name = NULL;
1324
1611
_ggzcore_table_set_seat(table, &seat);
1327
for (i = 0; i < table_data->num_spectator_seats; i++) {
1328
if (table_data->spectator_seats[i].name) {
1615
i < ggzcore_table_get_num_spectator_seats(table_data);
1617
GGZTableSeat leave_spectator
1618
= _ggzcore_table_get_nth_spectator_seat
1621
if (leave_spectator.name) {
1329
1622
/* Player is vacating seat */
1330
struct _GGZSeat seat;
1331
1624
seat.index = i;
1332
1625
seat.name = NULL;
1333
1626
_ggzcore_table_set_spectator_seat(table,
1595
2004
/* Functions for <ROOM> tag */
1596
static void _ggzcore_net_handle_room(GGZNet *net, GGZXMLElement *element)
2005
static void _ggzcore_net_handle_room(GGZNet * net, GGZXMLElement * element)
1598
2007
GGZRoom *ggz_room;
1601
GGZXMLElement *parent;
2008
int id, game, players;
2009
const char *name, *desc;
2010
GGZXMLElement *parent = ggz_stack_top(net->stack);
1602
2011
const char *parent_tag, *parent_type;
1607
2020
/* Grab data from tag */
1608
2021
id = str_to_int(ATTR(element, "ID"), -1);
1609
2022
name = ATTR(element, "NAME");
1610
2023
game = str_to_int(ATTR(element, "GAME"), -1);
1611
2024
desc = ggz_xmlelement_get_data(element);
2025
players = str_to_int(ATTR(element, "PLAYERS"), -1);
1613
2027
/* Set up GGZRoom object */
1614
2028
ggz_room = _ggzcore_room_new();
1615
_ggzcore_room_init(ggz_room, net->server, id, name, game, desc);
2029
_ggzcore_room_init(ggz_room, net->server, id,
2030
name, game, desc, players);
1617
2032
/* Free description if present */
1619
2034
ggz_free(desc);
1621
2036
/* Get parent off top of stack */
1622
parent = ggz_stack_top(net->stack);
1623
2037
parent_tag = ggz_xmlelement_get_tag(parent);
1624
2038
parent_type = ATTR(parent, "TYPE");
1627
&& strcasecmp(parent_tag, "LIST") == 0
1628
&& strcasecmp(parent_type, "room") == 0)
2040
if (strcasecmp(parent_tag, "LIST") == 0
2041
&& strcasecmp(parent_type, "room") == 0) {
1629
2042
_ggzcore_net_list_insert(parent, ggz_room);
2043
} else if (strcasecmp(parent_tag, "UPDATE") == 0
2044
&& strcasecmp(parent_type, "room") == 0
2045
&& ggz_xmlelement_get_data(parent) == NULL) {
2046
ggz_xmlelement_set_data(parent, ggz_room);
1631
2048
_ggzcore_room_free(ggz_room);
1635
2053
/* Functions for <PLAYER> tag */
1636
static void _ggzcore_net_handle_player(GGZNet *net, GGZXMLElement *element)
2054
static void _ggzcore_net_handle_player(GGZNet * net,
2055
GGZXMLElement * element)
1638
2057
GGZPlayer *ggz_player;
1639
2058
GGZPlayerType type;
1641
char *name, *str_type;
2060
const char *name, *str_type;
1642
2061
int table, lag;
1643
2062
GGZXMLElement *parent;
1644
2063
const char *parent_tag, *parent_type;
2064
int wins, losses, ties, forfeits, rating, ranking, highscore;
1655
2075
lag = str_to_int(ATTR(element, "LAG"), 0);
1657
2077
/* Set player's type */
1658
if (!str_type || strcasecmp(str_type, "guest") == 0)
1659
type = GGZ_PLAYER_GUEST;
1660
else if (strcasecmp(str_type, "normal") == 0)
1661
type = GGZ_PLAYER_NORMAL;
1662
else if (strcasecmp(str_type, "admin") == 0)
1663
type = GGZ_PLAYER_ADMIN;
1665
type = GGZ_PLAYER_GUEST;
2078
type = ggz_string_to_playertype(str_type);
1667
2080
/* Set up GGZPlayer object */
1668
2081
ggz_player = _ggzcore_player_new();
1669
_ggzcore_player_init(ggz_player, name, room, table, type, lag);
2082
_ggzcore_player_init(ggz_player, name, room, table, type, lag);
1671
2084
/* FIXME: should these be initialized through an accessor function? */
1672
ggz_player->wins = str_to_int(ATTR(element, "WINS"), NO_RECORD);
1673
ggz_player->ties = str_to_int(ATTR(element, "TIES"), NO_RECORD);
1674
ggz_player->losses = str_to_int(ATTR(element, "LOSSES"), NO_RECORD);
1675
ggz_player->forfeits = str_to_int(ATTR(element, "FORFEITS"),
1677
ggz_player->rating = str_to_int(ATTR(element, "RATING"), NO_RATING);
1678
ggz_player->ranking = str_to_int(ATTR(element, "RANKING"), NO_RANKING);
1680
/* FIXME: highscore is a long... */
1681
ggz_player->highscore = str_to_int(ATTR(element, "HIGHSCORE"),
2085
wins = str_to_int(ATTR(element, "WINS"), NO_RECORD);
2086
ties = str_to_int(ATTR(element, "TIES"), NO_RECORD);
2087
losses = str_to_int(ATTR(element, "LOSSES"), NO_RECORD);
2088
forfeits = str_to_int(ATTR(element, "FORFEITS"), NO_RECORD);
2089
rating = str_to_int(ATTR(element, "RATING"), NO_RATING);
2090
ranking = str_to_int(ATTR(element, "RANKING"), NO_RANKING);
2091
highscore = str_to_int(ATTR(element, "HIGHSCORE"), NO_HIGHSCORE);
2092
_ggzcore_player_init_stats(ggz_player, wins, losses, ties,
2093
forfeits, rating, ranking, highscore);
1684
2095
/* Get parent off top of stack */
1685
2096
parent = ggz_stack_top(net->stack);
1984
&& type != GGZ_CHAT_BEEP
1985
&& type != GGZ_CHAT_UNKNOWN) {
2404
if (!msg && type != GGZ_CHAT_BEEP && type != GGZ_CHAT_UNKNOWN) {
1986
2405
/* Ignore an empty message, except for the
1987
2406
appropriate chat types. */
2410
msg_unquoted = _ggz_xml_cdata_unescape(msg);
1991
2412
room = ggzcore_server_get_cur_room(net->server);
1992
_ggzcore_room_add_chat(room, type, from, msg);
2413
_ggzcore_room_add_chat(room, type, from, msg_unquoted);
2416
ggz_free(msg_unquoted);
2420
/* Functions for <INFO> tag */
2421
static void _ggzcore_net_handle_info(GGZNet * net, GGZXMLElement * element)
2423
GGZPlayerInfoData *data = _ggzcore_net_playerinfo_get_data(element);
2425
GGZGame *game = ggzcore_server_get_cur_game(net->server);
2426
_ggzcore_game_set_info(game, ggz_list_count(data->infos), data->infos);
2430
/* Functions for <PLAYERINFO> tag */
2431
static void _ggzcore_net_handle_playerinfo(GGZNet * net, GGZXMLElement * element)
2433
GGZXMLElement *parent;
2434
const char *parent_tag;
2439
/* Get parent off top of stack */
2440
parent = ggz_stack_top(net->stack);
2444
parent_tag = ggz_xmlelement_get_tag(parent);
2445
if (strcasecmp(parent_tag, "INFO") != 0)
2448
_ggzcore_net_playerinfo_add_seat(parent,
2449
str_to_int(ATTR(element, "SEAT"), -1),
2450
ATTR(element, "REALNAME"),
2451
ATTR(element, "PHOTO"),
2452
ATTR(element, "HOST"));
1996
2456
/* Function for <PING> tag */
1997
static void _ggzcore_net_handle_ping(GGZNet *net, GGZXMLElement *element)
2457
static void _ggzcore_net_handle_ping(GGZNet * net, GGZXMLElement * element)
1999
2459
/* No need to bother the client or anything, just send pong */
2000
2460
const char *id = ATTR(element, "ID");