1 /* purple
2  *
3  * Purple is the legal property of its developers, whose names are too numerous
4  * to list here.  Please refer to the COPYRIGHT file distributed with this
5  * source distribution.
6  *
7  * This program is free software; you can redistribute it and/or modify
8  * it under the terms of the GNU General Public License as published by
9  * the Free Software Foundation; either version 2 of the License, or
10  * (at your option) any later version.
11  *
12  * This program is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15  * GNU General Public License for more details.
16  *
17  * You should have received a copy of the GNU General Public License
18  * along with this program; if not, write to the Free Software
19  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02111-1301  USA
20  */
21 
22 #include "internal.h"
23 
24 #include <string.h>
25 
26 #include "http.h"
27 
28 struct _FbHttpConns
29 {
30 	GHashTable *cons;
31 	gboolean canceled;
32 };
33 
34 GQuark
fb_http_error_quark(void)35 fb_http_error_quark(void)
36 {
37 	static GQuark q = 0;
38 
39 	if (G_UNLIKELY(q == 0)) {
40 		q = g_quark_from_static_string("fb-http-error-quark");
41 	}
42 
43 	return q;
44 }
45 
46 FbHttpConns *
fb_http_conns_new(void)47 fb_http_conns_new(void)
48 {
49 	FbHttpConns *cons;
50 
51 	cons = g_new0(FbHttpConns, 1);
52 	cons->cons = g_hash_table_new(g_direct_hash, g_direct_equal);
53 	return cons;
54 }
55 
56 void
fb_http_conns_free(FbHttpConns * cons)57 fb_http_conns_free(FbHttpConns *cons)
58 {
59 	g_return_if_fail(cons != NULL);
60 
61 	g_hash_table_destroy(cons->cons);
62 	g_free(cons);
63 }
64 
65 void
fb_http_conns_cancel_all(FbHttpConns * cons)66 fb_http_conns_cancel_all(FbHttpConns *cons)
67 {
68 	GHashTableIter iter;
69 	gpointer con;
70 
71 	g_return_if_fail(cons != NULL);
72 	g_return_if_fail(!cons->canceled);
73 
74 	cons->canceled = TRUE;
75 	g_hash_table_iter_init(&iter, cons->cons);
76 
77 	while (g_hash_table_iter_next(&iter, &con, NULL)) {
78 		g_hash_table_iter_remove(&iter);
79 		purple_http_conn_cancel(con);
80 	}
81 }
82 
83 gboolean
fb_http_conns_is_canceled(FbHttpConns * cons)84 fb_http_conns_is_canceled(FbHttpConns *cons)
85 {
86 	g_return_val_if_fail(cons != NULL, TRUE);
87 	return cons->canceled;
88 }
89 
90 void
fb_http_conns_add(FbHttpConns * cons,PurpleHttpConnection * con)91 fb_http_conns_add(FbHttpConns *cons, PurpleHttpConnection *con)
92 {
93 	g_return_if_fail(cons != NULL);
94 	g_return_if_fail(!cons->canceled);
95 	g_hash_table_replace(cons->cons, con, con);
96 }
97 
98 void
fb_http_conns_remove(FbHttpConns * cons,PurpleHttpConnection * con)99 fb_http_conns_remove(FbHttpConns *cons, PurpleHttpConnection *con)
100 {
101 	g_return_if_fail(cons != NULL);
102 	g_return_if_fail(!cons->canceled);
103 	g_hash_table_remove(cons->cons, con);
104 }
105 
106 void
fb_http_conns_reset(FbHttpConns * cons)107 fb_http_conns_reset(FbHttpConns *cons)
108 {
109 	g_return_if_fail(cons != NULL);
110 	cons->canceled = FALSE;
111 	g_hash_table_remove_all(cons->cons);
112 }
113 
114 gboolean
fb_http_error_chk(PurpleHttpResponse * res,GError ** error)115 fb_http_error_chk(PurpleHttpResponse *res, GError **error)
116 {
117 	const gchar *msg;
118 	gint code;
119 
120 	if (purple_http_response_is_successful(res)) {
121 		return TRUE;
122 	}
123 
124 	msg = purple_http_response_get_error(res);
125 	code = purple_http_response_get_code(res);
126 	g_set_error(error, FB_HTTP_ERROR, code, "%s", msg);
127 	return FALSE;
128 }
129 
130 FbHttpParams *
fb_http_params_new(void)131 fb_http_params_new(void)
132 {
133         return g_hash_table_new_full(g_str_hash, g_str_equal, g_free, g_free);
134 }
135 
136 FbHttpParams *
fb_http_params_new_parse(const gchar * data,gboolean isurl)137 fb_http_params_new_parse(const gchar *data, gboolean isurl)
138 {
139 	const gchar *tail;
140 	gchar *key;
141 	gchar **ps;
142 	gchar *val;
143 	guint i;
144 	FbHttpParams *params;
145 
146 	params = fb_http_params_new();
147 
148 	if (data == NULL) {
149 		return params;
150 	}
151 
152 	if (isurl) {
153 		data = strchr(data, '?');
154 
155 		if (data == NULL) {
156 			return params;
157 		}
158 
159 		tail = strchr(++data, '#');
160 
161 		if (tail != NULL) {
162 			data = g_strndup(data, tail - data);
163 		} else {
164 			data = g_strdup(data);
165 		}
166 	}
167 
168 	ps = g_strsplit(data, "&", 0);
169 
170 	for (i = 0; ps[i] != NULL; i++) {
171 		key = ps[i];
172 		val = strchr(ps[i], '=');
173 
174 		if (val == NULL) {
175 			continue;
176 		}
177 
178 		*(val++) = 0;
179 		key = g_uri_unescape_string(key, NULL);
180 		val = g_uri_unescape_string(val, NULL);
181 		g_hash_table_replace(params, key, val);
182 	}
183 
184 	if (isurl) {
185 		g_free((gchar *) data);
186 	}
187 
188 	g_strfreev(ps);
189 	return params;
190 }
191 
192 void
fb_http_params_free(FbHttpParams * params)193 fb_http_params_free(FbHttpParams *params)
194 {
195 	g_hash_table_destroy(params);
196 }
197 
198 gchar *
fb_http_params_close(FbHttpParams * params,const gchar * url)199 fb_http_params_close(FbHttpParams *params, const gchar *url)
200 {
201 	GHashTableIter iter;
202 	gpointer key;
203 	gpointer val;
204 	GString *ret;
205 
206 	g_hash_table_iter_init(&iter, params);
207 	ret = g_string_new(NULL);
208 
209 	while (g_hash_table_iter_next(&iter, &key, &val)) {
210 		if (val == NULL) {
211 			g_hash_table_iter_remove(&iter);
212 			continue;
213 		}
214 
215 		if (ret->len > 0) {
216 			g_string_append_c(ret, '&');
217 		}
218 
219 		g_string_append_uri_escaped(ret, key, NULL, TRUE);
220 		g_string_append_c(ret, '=');
221 		g_string_append_uri_escaped(ret, val, NULL, TRUE);
222 	}
223 
224 	if (url != NULL) {
225 		g_string_prepend_c(ret, '?');
226 		g_string_prepend(ret, url);
227 	}
228 
229 	fb_http_params_free(params);
230 	return g_string_free(ret, FALSE);
231 }
232 
233 static const gchar *
fb_http_params_get(FbHttpParams * params,const gchar * name,GError ** error)234 fb_http_params_get(FbHttpParams *params, const gchar *name, GError **error)
235 {
236 	const gchar *ret;
237 
238 	ret = g_hash_table_lookup(params, name);
239 
240 	if (ret == NULL) {
241 		g_set_error(error, FB_HTTP_ERROR, FB_HTTP_ERROR_NOMATCH,
242 		            _("No matches for %s"), name);
243 		return NULL;
244 	}
245 
246 	return ret;
247 }
248 
249 gboolean
fb_http_params_get_bool(FbHttpParams * params,const gchar * name,GError ** error)250 fb_http_params_get_bool(FbHttpParams *params, const gchar *name,
251                         GError **error)
252 {
253 	const gchar *val;
254 
255 	val = fb_http_params_get(params, name, error);
256 
257 	if (val == NULL) {
258 		return FALSE;
259 	}
260 
261 	return g_ascii_strcasecmp(val, "TRUE") == 0;
262 }
263 
264 gdouble
fb_http_params_get_dbl(FbHttpParams * params,const gchar * name,GError ** error)265 fb_http_params_get_dbl(FbHttpParams *params, const gchar *name,
266                        GError **error)
267 {
268 	const gchar *val;
269 
270 	val = fb_http_params_get(params, name, error);
271 
272 	if (val == NULL) {
273 		return 0.0;
274 	}
275 
276 	return g_ascii_strtod(val, NULL);
277 }
278 
279 gint64
fb_http_params_get_int(FbHttpParams * params,const gchar * name,GError ** error)280 fb_http_params_get_int(FbHttpParams *params, const gchar *name,
281                        GError **error)
282 {
283 	const gchar *val;
284 
285 	val = fb_http_params_get(params, name, error);
286 
287 	if (val == NULL) {
288 		return 0;
289 	}
290 
291 	return g_ascii_strtoll(val, NULL, 10);
292 }
293 
294 const gchar *
fb_http_params_get_str(FbHttpParams * params,const gchar * name,GError ** error)295 fb_http_params_get_str(FbHttpParams *params, const gchar *name,
296                        GError **error)
297 {
298 	return fb_http_params_get(params, name, error);
299 }
300 
301 gchar *
fb_http_params_dup_str(FbHttpParams * params,const gchar * name,GError ** error)302 fb_http_params_dup_str(FbHttpParams *params, const gchar *name,
303                        GError **error)
304 {
305 	const gchar *str;
306 
307 	str = fb_http_params_get(params, name, error);
308 	return g_strdup(str);
309 }
310 
311 static void
fb_http_params_set(FbHttpParams * params,const gchar * name,gchar * value)312 fb_http_params_set(FbHttpParams *params, const gchar *name, gchar *value)
313 {
314 	gchar *key;
315 
316 	key = g_strdup(name);
317 	g_hash_table_replace(params, key, value);
318 }
319 
320 void
fb_http_params_set_bool(FbHttpParams * params,const gchar * name,gboolean value)321 fb_http_params_set_bool(FbHttpParams *params, const gchar *name,
322                         gboolean value)
323 {
324 	gchar *val;
325 
326 	val = g_strdup(value ? "true" : "false");
327 	fb_http_params_set(params, name, val);
328 }
329 
330 void
fb_http_params_set_dbl(FbHttpParams * params,const gchar * name,gdouble value)331 fb_http_params_set_dbl(FbHttpParams *params, const gchar *name, gdouble value)
332 {
333 	gchar *val;
334 
335 	val = g_strdup_printf("%f", value);
336 	fb_http_params_set(params, name, val);
337 }
338 
339 void
fb_http_params_set_int(FbHttpParams * params,const gchar * name,gint64 value)340 fb_http_params_set_int(FbHttpParams *params, const gchar *name, gint64 value)
341 {
342 	gchar *val;
343 
344 	val = g_strdup_printf("%" G_GINT64_FORMAT, value);
345 	fb_http_params_set(params, name, val);
346 }
347 
348 void
fb_http_params_set_str(FbHttpParams * params,const gchar * name,const gchar * value)349 fb_http_params_set_str(FbHttpParams *params, const gchar *name,
350                        const gchar *value)
351 {
352 	gchar *val;
353 
354 	val = g_strdup(value);
355 	fb_http_params_set(params, name, val);
356 }
357 
358 void
fb_http_params_set_strf(FbHttpParams * params,const gchar * name,const gchar * format,...)359 fb_http_params_set_strf(FbHttpParams *params, const gchar *name,
360                         const gchar *format, ...)
361 {
362 	gchar *val;
363 	va_list ap;
364 
365 	va_start(ap, format);
366 	val = g_strdup_vprintf(format, ap);
367 	va_end(ap);
368 
369 	fb_http_params_set(params, name, val);
370 }
371 
372 gboolean
fb_http_urlcmp(const gchar * url1,const gchar * url2,gboolean protocol)373 fb_http_urlcmp(const gchar *url1, const gchar *url2, gboolean protocol)
374 {
375 	const gchar *str1;
376 	const gchar *str2;
377 	gboolean ret = TRUE;
378 	gint int1;
379 	gint int2;
380 	guint i;
381 	PurpleHttpURL *purl1;
382 	PurpleHttpURL *purl2;
383 
384 	static const gchar * (*funcs[]) (const PurpleHttpURL *url) = {
385 		/* Always first so it can be skipped */
386 		purple_http_url_get_protocol,
387 
388 		purple_http_url_get_fragment,
389 		purple_http_url_get_host,
390 		purple_http_url_get_password,
391 		purple_http_url_get_path,
392 		purple_http_url_get_username
393 	};
394 
395 	if ((url1 == NULL) || (url2 == NULL)) {
396 		return url1 == url2;
397 	}
398 
399 	if (strstr(url1, url2) != NULL || strstr(url2, url1) != NULL) {
400 		return TRUE;
401 	}
402 
403 	purl1 = purple_http_url_parse(url1);
404 
405 	if (purl1 == NULL) {
406 		return g_ascii_strcasecmp(url1, url2) == 0;
407 	}
408 
409 	purl2 = purple_http_url_parse(url2);
410 
411 	if (purl2 == NULL) {
412 		purple_http_url_free(purl1);
413 		return g_ascii_strcasecmp(url1, url2) == 0;
414 	}
415 
416 	for (i = protocol ? 0 : 1; i < G_N_ELEMENTS(funcs); i++) {
417 		str1 = funcs[i](purl1);
418 		str2 = funcs[i](purl2);
419 
420 		if (!purple_strequal(str1, str2)) {
421 			ret = FALSE;
422 			break;
423 		}
424 	}
425 
426 	if (ret && protocol) {
427 		int1 = purple_http_url_get_port(purl1);
428 		int2 = purple_http_url_get_port(purl2);
429 
430 		if (int1 != int2) {
431 			ret = FALSE;
432 		}
433 	}
434 
435 	purple_http_url_free(purl1);
436 	purple_http_url_free(purl2);
437 	return ret;
438 }
439