1 /* -*- Mode: C++; c-basic-offset: 2; tab-width: 2; indent-tabs-mode: nil -*-
2 *
3 * Quadra, an action puzzle game
4 * Copyright (C) 1998-2000 Ludus Design
5 *
6 * This program is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Lesser General Public
8 * License as published by the Free Software Foundation; either
9 * version 2.1 of the License, or (at your option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * Lesser General Public License for more details.
15 *
16 * You should have received a copy of the GNU Lesser General Public
17 * License along with this library; if not, write to the Free Software
18 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
19 */
20
21 #include "qserv.h"
22
23 #include <string.h>
24 #include <stdarg.h>
25 #include <stdio.h>
26 #include <errno.h>
27 #include "config.h"
28 #include "cfgfile.h"
29 #include "url.h"
30 #include "http_post.h"
31 #include "dict.h"
32 #include "stringtable.h"
33 #include "video.h"
34 #include "config.h"
35 #include "version.h"
36 #include "SDL_video.h"
37 #include "quadra.h"
38
39 #if defined(HAVE_SYS_UTSNAME_H) || defined(UGS_XCODE)
40 #include <sys/utsname.h>
41 #endif
42
43 Dword Qserv::http_addr=0;
44 int Qserv::http_port=0;
45
Qserv()46 Qserv::Qserv() {
47 req=NULL;
48 status[0]=0;
49 reply=NULL;
50
51 create_req();
52
53 req->add_data_raw("data=");
54 }
55
~Qserv()56 Qserv::~Qserv() {
57 if(req)
58 delete req;
59 if(reply)
60 delete reply;
61 }
62
done()63 bool Qserv::done() {
64 if(!req)
65 return true;
66 if(!req->done())
67 return false;
68
69 // Clear the IP/port info, until we know it's okay.
70 Qserv::http_addr = 0;
71 Qserv::http_port = 0;
72
73 //Parse reply
74 reply=new Dict();
75 if(!req->getsize()) {
76 strcpy(status, "");
77 }
78 else {
79 Stringtable st(req->getbuf(), req->getsize());
80 int i;
81 for(i = 0; i < st.size(); ++i)
82 if(strlen(st.get(i)) == 0) { //end of HTTP header
83 ++i; // Skip blank line
84 if(i >= st.size())
85 strcpy(status, "");
86 else {
87 strncpy(status, st.get(i), sizeof(status) - 1);
88 status[sizeof(status) - 1] = 0;
89 ++i; // Skip status line
90 }
91 break;
92 }
93 while(i<st.size()) {
94 skelton_msgbox("Qserv::done: adding [%-60.60s]\n", st.get(i));
95 reply->add(st.get(i));
96 i++;
97 }
98
99 // At this point, should be good enough to cache the IP/port info.
100 Qserv::http_addr = req->gethostaddr();
101 Qserv::http_port = req->gethostport();
102 }
103 if(!strcmp(status, "Redirect permanent") && reply->find("location")) {
104 //we are being redirected to another qserv server
105 //update the config
106 strncpy(config.info.game_server_address, reply->find("location"), sizeof(config.info.game_server_address)-1);
107 config.info.game_server_address[sizeof(config.info.game_server_address)-1]=0;
108 //clear cache of ip address unless we use a proxy
109 Url proxy(config.info2.proxy_address);
110 if(!strlen(proxy.getHost())) {
111 Qserv::http_addr = 0;
112 Qserv::http_port = 0;
113 }
114 //and retry the query with the same data
115 Buf data = req->get_data();
116 delete req;
117 delete reply;
118 status[0] = 0;
119 create_req();
120 req->add_data_raw(data);
121 req->send();
122 return false;
123 }
124 else {
125 delete req;
126 req=NULL;
127 }
128 msgbox("Qserv::done: done\n");
129 return true;
130 }
131
add_data(const char * s,...)132 void Qserv::add_data(const char *s, ...) {
133 char st[32768];
134 Textbuf buf;
135 va_list marker;
136 va_start(marker, s);
137 vsprintf(st, s, marker);
138 va_end(marker);
139 Http_request::url_encode(st, buf);
140 req->add_data_raw(buf.get());
141 }
142
add_data_large(const Textbuf & buf)143 void Qserv::add_data_large(const Textbuf &buf) {
144 req->add_data_raw(buf.get());
145 }
146
send()147 void Qserv::send() {
148 // FIXME: This is obsolete, but I am not sure what depends on it, so
149 // we'll fake it for the moment.
150 req->add_data_encode("info/language 0\n");
151 req->add_data_encode("info/quadra_version %s\n", VERSION_STRING);
152 #if (defined(HAVE_SYS_UTSNAME_H) && defined(HAVE_UNAME)) || defined(UGS_XCODE)
153 struct utsname unameinfo;
154 if (uname(&unameinfo) == 0)
155 req->add_data_encode("info/platform/os %s/%s\n", unameinfo.sysname,
156 unameinfo.machine);
157 else {
158 req->add_data_encode("info/platform/os unknown\n");
159 skelton_msgbox("uname: %s\n", strerror(errno));
160 }
161 #elif defined(WIN32)
162 req->add_data_encode("info/platform/os Windows\n");
163 #else
164 #error "What platform???"
165 #endif
166 if(video_is_dumb)
167 req->add_data_encode("info/platform/display None\n");
168 else {
169 char buf[512];
170 req->add_data_encode("info/platform/display %s\n",
171 SDL_VideoDriverName(buf, sizeof(buf)));
172 }
173 req->send();
174 }
175
get_status()176 const char *Qserv::get_status() {
177 if(status[0])
178 return status;
179 else
180 return NULL;
181 }
182
get_reply()183 Dict *Qserv::get_reply() {
184 return reply;
185 }
186
isconnected() const187 bool Qserv::isconnected() const {
188 if(req && req->isconnected())
189 return true;
190 return false;
191 }
192
getnbrecv() const193 Dword Qserv::getnbrecv() const {
194 int val = 0;
195 if(req) {
196 val = req->getsize();
197 if(val < 0)
198 val = 0;
199 }
200 return val;
201 }
202
create_req()203 void Qserv::create_req()
204 {
205 const char* host;
206 int port;
207 char path[256];
208
209 Url defaulturl(config.info3.default_game_server_address);
210 if(!defaulturl.getPort())
211 defaulturl.setPort(80);
212 if(!strcmp(defaulturl.getHost(), ""))
213 defaulturl.setHost("quadra.sourceforge.net:80");
214 if(!strcmp(defaulturl.getPath(), "/"))
215 defaulturl.setPath("/cgi-bin/qserv.pl");
216
217 Url url(config.info.game_server_address);
218 if(!url.getScheme() || strlen(url.getScheme()) == 0)
219 url.setScheme("http");
220 if(!url.getPort())
221 url.setPort(defaulturl.getPort());
222 if(!strcmp(url.getHost(), ""))
223 url.setHost(defaulturl.getHost());
224 if(!strcmp(url.getPath(), "/"))
225 url.setPath(defaulturl.getPath());
226
227 Url proxy(config.info2.proxy_address);
228 if(!proxy.getPort())
229 proxy.setPort(80);
230
231 if(strlen(proxy.getHost())) {
232 //Use proxy info for host and port, and full game server address for path
233 host = proxy.getHost();
234 port = proxy.getPort();
235 url.getFull(path);
236 } else {
237 //No proxy configuration, use game server address for everything
238 host = url.getHost();
239 port = url.getPort();
240 strcpy(path, url.getPath());
241 }
242
243 //Use IP cache if set
244 if(http_addr)
245 req=new Http_post(host, http_addr, http_port, path);
246 else
247 req=new Http_post(host, port, path);
248 }
249