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