1 /* XQF - Quake server browser and launcher
2  * Copyright (C) 1998-2000 Roman 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 #include "gnuconfig.h"
19 
20 #ifdef USE_GEOIP
21 
22 #include "country-filter.h"
23 #include "i18n.h"
24 #include "debug.h"
25 #include "pixmaps.h"
26 #include "loadpixmap.h"
27 #include "xpm/noflag.xpm"
28 
29 #include <gdk-pixbuf/gdk-pixbuf.h>
30 #include <ctype.h>
31 #include <unistd.h> // access()
32 
33 #include <GeoIP.h>
34 #include <dlfcn.h>
35 
36 static const char*  xqf_geoip_country_code;
37 static const char** xqf_geoip_country_name;
38 
39 unsigned MaxCountries;
40 static unsigned LAN_GeoIPid;
41 
42 static GeoIP* gi;
43 
44 /* array of (struct pixmap*), indexed by country id.
45 flags[id] has three states:
46 	-1     flag unavailable
47 	0     flag not yet loaded
48 	<else> pointer to struct pixmap
49 */
50 
51 static struct pixmap* flags = NULL;
52 
geoip_init(void)53 void geoip_init(void) {
54 	const char* geoipdat = getenv("xqf_GEOIPDAT");
55 
56 	if (gi) { // already initialized
57 		return;
58 	}
59 
60 	if (geoipdat) {
61 		gi = GeoIP_open(geoipdat, GEOIP_STANDARD);
62 	}
63 
64 	if (!gi) {
65 		gi = GeoIP_new(GEOIP_STANDARD);
66 	}
67 
68 	if (gi && geoip_num_countries()) {
69 		flags = g_malloc0((MaxCountries+1) *sizeof(struct pixmap)); /*+1-> flag for LAN server*/
70 	}
71 	else {
72 		geoip_done();
73 		xqf_error("GeoIP initialization failed");
74 	}
75 }
76 
geoip_done(void)77 void geoip_done(void) {
78 	if (gi) {
79 		GeoIP_delete(gi);
80 	}
81 
82 	g_free(flags);
83 }
84 
geoip_is_working(void)85 gboolean geoip_is_working (void) {
86 
87 	if (gi) {
88 		return TRUE;
89 	}
90 	else {
91 		return FALSE;
92 	}
93 }
94 
geoip_code_by_id(int id)95 const char* geoip_code_by_id(int id) {
96 	if (id < 0 || id > MaxCountries) {
97 		return NULL;
98 	}
99 
100 	/* LAN server have code ="00" */
101 	if (id == LAN_GeoIPid) {
102 		return "00";
103 	}
104 	else {
105 		return &xqf_geoip_country_code[id*3];
106 	}
107 }
108 
109 
geoip_name_by_id(int id)110 const char* geoip_name_by_id(int id) {
111 	if (!gi || id < 0 || id > MaxCountries) {
112 		return NULL;
113 	}
114 
115 	if (id == LAN_GeoIPid) {
116 		return "LAN";
117 	}
118 	else {
119 		return xqf_geoip_country_name[id];
120 	}
121 }
122 
geoip_id_by_code(const char * country)123 int geoip_id_by_code(const char *country) {
124 	int i;
125 
126 	if (!gi) return -1; {
127 	}
128 
129 	if (strcmp(country,"00")==0) {
130 		return LAN_GeoIPid;
131 	}
132 
133 	for (i=1;i<MaxCountries;++i) {
134 		if (strcmp(country,xqf_geoip_country_name[i])==0) {
135 			return i;
136 		}
137 	}
138 
139 	return 0;
140 }
141 
142 /*Checks for RFC1918 private addresses; returns TRUE if is a private address. */
143 /*from the napshare source*/
is_private_ip(guint32 ip)144 static gboolean is_private_ip(guint32 ip) {
145 	/* 127.0.0.0 -- (localhost) */
146 	if ((ip & 0xff000000) == 0x7f000000) {
147 		return TRUE;
148 	}
149 
150 	/* 10.0.0.0 -- (10/8 prefix) */
151 	if ((ip & 0xff000000) == 0xa000000) {
152 		return TRUE;
153 	}
154 
155 	/* 172.16.0.0 -- (172.16/12 prefix) */
156 	if ((ip & 0xfff00000) == 0xac100000) {
157 		return TRUE;
158 	}
159 
160 	/* 192.168.0.0 -- (192.168/16 prefix) */
161 	if ((ip & 0xffff0000) == 0xc0a80000) {
162 		return TRUE;
163 	}
164 
165 	return FALSE;
166 }
167 
168 
geoip_id_by_ip(struct in_addr in)169 int geoip_id_by_ip(struct in_addr in) {
170 
171 	if (!gi) {
172 		return -1;
173 	}
174 
175 	/*check if the server is inside a LAN*/
176 	if (is_private_ip(htonl(in.s_addr))) {
177 		return LAN_GeoIPid;
178 	}
179 	else {
180 		return GeoIP_country_id_by_addr(gi, inet_ntoa (in));
181 	}
182 }
183 
find_flag_file(int id)184 static char* find_flag_file(int id) {
185 	gchar file[] = "flags/lan.png";
186 	gchar* filename;
187 
188 	if (id != LAN_GeoIPid) {
189 		const gchar* code = geoip_code_by_id(id);
190 		if (!code) return NULL;
191 		strncpy(file+strlen("flags/"), code, 2);
192 		strcpy(file+strlen("flags/")+2, ".png");
193 	}
194 
195 	filename = find_pixmap_directory(g_ascii_strdown(file, -1));
196 
197 	return filename;
198 }
199 
get_pixmap_for_country(int id)200 struct pixmap* get_pixmap_for_country(int id) {
201 	GdkPixbuf* pixbuf = NULL;
202 	struct pixmap* pix = NULL;
203 
204 	char* filename = NULL;
205 
206 	if (!flags) {
207 		return NULL;
208 	}
209 	if (id < 1) {   // no flag for N/A
210 		return NULL;
211 	}
212 
213 	pix = &flags[id];
214 
215 	if (pix->pix == GINT_TO_POINTER(-1)) {
216 		return NULL;
217 	}
218 	if (pix->pix) {
219 		return pix;
220 	}
221 
222 	if (!GDK_PIXBUF_INSTALLED) {
223 		return NULL;
224 	}
225 
226 	filename = find_flag_file(id);
227 
228 	if (!filename || access(filename,R_OK)) {
229 		g_free(filename);
230 		return NULL;
231 	}
232 
233 	debug(4,"loading %s",filename);
234 
235 	pixbuf = gdk_pixbuf_new_from_file(filename, NULL);
236 
237 	if (pixbuf == NULL) {
238 		pix->pix=GINT_TO_POINTER(-1);
239 		g_warning (_("Error loading pixmap file: %s"), filename);
240 		g_free (filename);
241 		return NULL;
242 	}
243 
244 	gdk_pixbuf_render_pixmap_and_mask(pixbuf,&pix->pix,&pix->mask,255);
245 
246 	g_object_unref(pixbuf);
247 	g_free (filename);
248 
249 	return pix;
250 }
251 
get_pixmap_for_country_with_fallback(int id)252 struct pixmap* get_pixmap_for_country_with_fallback(int id) {
253 	struct pixmap* pix = get_pixmap_for_country(id);
254 	if (pix) {
255 		return pix;
256 	}
257 
258 	if (!flags || flags[0].pix == GINT_TO_POINTER(-1)) {
259 		return NULL;
260 	}
261 
262 	if (!flags[0].pix) {
263 		flags[0].pix = gdk_pixmap_colormap_create_from_xpm_d(NULL,
264 				gdk_colormap_get_system(),
265 				&flags[0].mask,
266 				NULL,
267 				noflag_xpm);
268 		if (!flags[0].pix)
269 			flags[0].pix = GINT_TO_POINTER(-1);
270 	}
271 	return &flags[0];
272 }
273 
lookup_symbol(const char * name)274 static void* lookup_symbol(const char* name) {
275 	void* p;
276 	char* error;
277 	dlerror();
278 	p = dlsym(NULL, name);
279 	if ((error = dlerror()) != NULL) {
280 		xqf_error("failed to lookup %s: %s", name, error);
281 		return NULL;
282 	}
283 	return p;
284 }
285 
geoip_num_countries()286 unsigned geoip_num_countries() {
287 	if ((void*)xqf_geoip_country_code == (void*)-1) {
288 		return 0;
289 	}
290 
291 	if (!xqf_geoip_country_code) {
292 		unsigned i;
293 
294 		xqf_geoip_country_name = lookup_symbol("GeoIP_country_name");
295 		xqf_geoip_country_code = lookup_symbol("GeoIP_country_code");
296 
297 		if (!xqf_geoip_country_name || !xqf_geoip_country_code) {
298 			/* nasty hack to determine the number of countries at run time */
299 			MaxCountries = LAN_GeoIPid = 0;
300 			xqf_geoip_country_name = (void*)-1;
301 			xqf_geoip_country_code = (void*)-1;
302 			return 0;
303 		}
304 
305 		for (i = 0; xqf_geoip_country_code[i*3] && i < 333 /* arbitrary limit */; ++i) {
306 			/* nothing */
307 		}
308 
309 		if (i >= 333) {
310 			xqf_geoip_country_name = (void*)-1;
311 			xqf_geoip_country_code = (void*)-1;
312 			xqf_error("failed to determine number of supported countries");
313 			return 0;
314 		}
315 
316 		MaxCountries = LAN_GeoIPid = i;
317 	}
318 
319 	debug(1, "MaxCountries %u", MaxCountries);
320 
321 	return MaxCountries;
322 }
323 
324 #endif
325