1 /* Gnome Music Player Client (GMPC)
2  * Copyright (C) 2004-2011 Qball Cow <qball@gmpclient.org>
3  * Project homepage: http://gmpclient.org/
4 
5  * This program is free software; you can redistribute it and/or modify
6  * it under the terms of the GNU General Public License as published by
7  * the Free Software Foundation; either version 2 of the License, or
8  * (at your option) any later version.
9 
10  * This program is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13  * GNU General Public License for more details.
14 
15  * You should have received a copy of the GNU General Public License along
16  * with this program; if not, write to the Free Software Foundation, Inc.,
17  * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
18  */
19 
20 #include <stdio.h>
21 #include <stdlib.h>
22 #include <string.h>
23 #include <glib.h>
24 #include <libsoup/soup.h>
25 #include <zlib.h>
26 #include "gmpc_easy_download.h"
27 #include "main.h"
28 
29 #define LOG_DOMAIN "EasyDownload"
30 /****
31  * ZIP MISC
32  * **********/
33 #define HEAD_CRC     0x02	/* bit 1 set: header CRC present */
34 #define EXTRA_FIELD  0x04	/* bit 2 set: extra field present */
35 #define ORIG_NAME    0x08	/* bit 3 set: original file name present */
36 #define COMMENT      0x10	/* bit 4 set: file comment present */
37 static char gz_magic[2] = { 0x1f, 0x8b };
38 
skip_gzip_header(const char * src,gsize size)39 static int skip_gzip_header(const char *src, gsize size)
40 {
41 	int idx;
42 	if (size < 10 || memcmp(src, gz_magic, 2))
43 		return -1;
44 	if (src[2] != Z_DEFLATED)
45 	{
46 		g_log(LOG_DOMAIN, G_LOG_LEVEL_WARNING,
47 					"unsupported compression method (%d).\n", (int)src[3]);
48 		return -1;
49 	}
50 	idx = 10;
51 	/* skip past header, mtime and xos */
52 
53 	if (src[3] & EXTRA_FIELD)
54 		idx += src[idx] + (src[idx + 1] << 8) + 2;
55 	if (src[3] & ORIG_NAME)
56 	{
57 		while (src[idx])
58 			idx++;
59 		idx++;
60 	}
61 	if (src[3] & COMMENT)
62 	{
63 		while (src[idx])
64 			idx++;
65 		idx++;
66 	}
67 	if (src[3] & HEAD_CRC)
68 		idx += 2;
69 	return idx;
70 }
71 
read_cb(void * z,char * buffer,int size)72 static int read_cb(void *z, char *buffer, int size)
73 {
74 	int r = 0;
75 	z_stream *zs = z;
76 	if (zs)
77 	{
78 		zs->next_out = (void *)buffer;
79 		zs->avail_out = size;
80 		r = inflate(zs, Z_SYNC_FLUSH);
81 		if (r == Z_OK || r == Z_STREAM_END || r == Z_NEED_DICT || r == Z_BUF_ERROR)
82 		{
83 			return size - zs->avail_out;
84 		}
85 	}
86 	g_log(LOG_DOMAIN, G_LOG_LEVEL_WARNING, "failed unzipping stream: %i\n", r);
87 	return -1;
88 }
89 
close_cb(void * z)90 static int close_cb(void *z)
91 {
92 	z_stream *zs = z;
93 	inflateEnd(zs);
94 	g_free(zs);
95 	return 0;
96 }
97 
98 static SoupSession *soup_session = NULL;
99 
gmpc_easy_download_set_proxy(SoupSession * session)100 static void gmpc_easy_download_set_proxy(SoupSession * session)
101 {
102 	if (session == NULL)
103 		return;
104 
105 	if (cfg_get_single_value_as_int_with_default(config, "Network Settings", "Use Proxy", FALSE))
106 	{
107 		char *value = cfg_get_single_value_as_string(config, "Network Settings", "Proxy Address");
108 		char *username = NULL;
109 		char *password = NULL;
110 		gint port = cfg_get_single_value_as_int_with_default(config, "Network Settings", "Proxy Port", 8080);
111 		if (cfg_get_single_value_as_int_with_default(config, "Network Settings", "Use authentication", FALSE))
112 		{
113 			password = cfg_get_single_value_as_string(config, "Network Settings", "Proxy authentication password");
114 			username = cfg_get_single_value_as_string(config, "Network Settings", "Proxy authentication username");
115 		}
116 		if (value)
117 		{
118 			SoupURI *uri = NULL;
119 			gchar *ppath = NULL;
120 			if (username && username[0] != '\0' && password && password[0] != '\0')
121 			{
122 				gchar *usere = gmpc_easy_download_uri_escape(username);
123 				gchar *passe = gmpc_easy_download_uri_escape(password);
124 				ppath = g_strdup_printf("http://%s:%s@%s:%i", usere, passe, value, port);
125 				g_free(usere);
126 				g_free(passe);
127 			} else if (username && username[0] != '\0')
128 			{
129 				gchar *usere = gmpc_easy_download_uri_escape(username);
130 				ppath = g_strdup_printf("http://%s@%s:%i", usere, value, port);
131 				g_free(usere);
132 			} else
133 			{
134 				ppath = g_strdup_printf("http://%s:%i", value, port);
135 			}
136 			uri = soup_uri_new(ppath);
137 			g_object_set(G_OBJECT(session), SOUP_SESSION_PROXY_URI, uri, NULL);
138 			soup_uri_free(uri);
139 			g_free(ppath);
140 		}
141 		g_free(username);
142 		g_free(password);
143 		g_free(value);
144 	} else
145 	{
146 		g_object_set(G_OBJECT(session), SOUP_SESSION_PROXY_URI, NULL, NULL);
147 	}
148 }
149 
150 /***
151  * preferences window
152  */
153 /* for gtkbuilder */
154 void proxy_pref_use_proxy_toggled(GtkWidget * toggle_button);
155 void proxy_pref_http_address_changed(GtkWidget * entry);
156 void proxy_pref_http_port_changed(GtkWidget * entry);
157 void proxy_pref_use_auth_toggled(GtkWidget * toggle_button);
158 void proxy_pref_auth_username_changed(GtkWidget * entry);
159 void proxy_pref_auth_password_changed(GtkWidget * entry);
160 
161 static GtkBuilder *proxy_pref_xml = NULL;
proxy_pref_destroy(GtkWidget * container)162 static void proxy_pref_destroy(GtkWidget * container)
163 {
164 	GObject *temp = gtk_builder_get_object(proxy_pref_xml, "frame_proxy_settings");
165 	gtk_container_remove(GTK_CONTAINER(container), GTK_WIDGET(temp));
166 	g_object_unref(proxy_pref_xml);
167 	proxy_pref_xml = NULL;
168 }
169 
proxy_pref_use_proxy_toggled(GtkWidget * toggle_button)170 void proxy_pref_use_proxy_toggled(GtkWidget * toggle_button)
171 {
172 	cfg_set_single_value_as_int(config, "Network Settings", "Use Proxy",
173 								gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(toggle_button)));
174 	gmpc_easy_download_set_proxy(soup_session);
175 }
176 
proxy_pref_http_address_changed(GtkWidget * entry)177 void proxy_pref_http_address_changed(GtkWidget * entry)
178 {
179 	cfg_set_single_value_as_string(config, "Network Settings", "Proxy Address",
180 								   (char *)gtk_entry_get_text(GTK_ENTRY(entry)));
181 	gmpc_easy_download_set_proxy(soup_session);
182 }
183 
proxy_pref_http_port_changed(GtkWidget * entry)184 void proxy_pref_http_port_changed(GtkWidget * entry)
185 {
186 	cfg_set_single_value_as_int(config, "Network Settings", "Proxy Port",
187 								gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(entry)));
188 	gmpc_easy_download_set_proxy(soup_session);
189 }
190 
proxy_pref_use_auth_toggled(GtkWidget * toggle_button)191 void proxy_pref_use_auth_toggled(GtkWidget * toggle_button)
192 {
193 	cfg_set_single_value_as_int(config, "Network Settings", "Use authentication",
194 								gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(toggle_button)));
195 	gmpc_easy_download_set_proxy(soup_session);
196 }
197 
proxy_pref_auth_username_changed(GtkWidget * entry)198 void proxy_pref_auth_username_changed(GtkWidget * entry)
199 {
200 	cfg_set_single_value_as_string(config, "Network Settings", "Proxy authentication username",
201 								   (char *)gtk_entry_get_text(GTK_ENTRY(entry)));
202 	gmpc_easy_download_set_proxy(soup_session);
203 }
204 
proxy_pref_auth_password_changed(GtkWidget * entry)205 void proxy_pref_auth_password_changed(GtkWidget * entry)
206 {
207 	cfg_set_single_value_as_string(config, "Network Settings", "Proxy authentication password",
208 								   gtk_entry_get_text(GTK_ENTRY(entry)));
209 	gmpc_easy_download_set_proxy(soup_session);
210 }
211 
proxy_pref_construct(GtkWidget * container)212 static void proxy_pref_construct(GtkWidget * container)
213 {
214 	GObject *temp = NULL;
215 	gchar *string;
216 	GError *error = NULL;
217 	gchar *path = gmpc_get_full_glade_path("preferences-proxy.ui");
218 	proxy_pref_xml = gtk_builder_new();
219 	gtk_builder_add_from_file(proxy_pref_xml, path, &error);
220 
221 	if (error)
222 	{
223 		g_log(LOG_DOMAIN, G_LOG_LEVEL_WARNING, "Failed to load %s: '%s'", path, error->message);
224 		g_error_free(error);
225 		g_free(path);
226 
227 		return;
228 	}
229 
230 	q_free(path);
231 	/* use proxy */
232 	temp = gtk_builder_get_object(proxy_pref_xml, "checkbutton_use_proxy");
233 	gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(temp),
234 								 cfg_get_single_value_as_int_with_default(config, "Network Settings", "Use Proxy",
235 																		  FALSE));
236 
237 	/* hostname */
238 	temp = gtk_builder_get_object(proxy_pref_xml, "entry_http_hostname");
239 	string = cfg_get_single_value_as_string(config, "Network Settings", "Proxy Address");
240 	if (string)
241 	{
242 		gtk_entry_set_text(GTK_ENTRY(temp), string);
243 		g_free(string);
244 	}
245 
246 	/* port */
247 	temp = gtk_builder_get_object(proxy_pref_xml, "spinbutton_http_port");
248 	gtk_spin_button_set_value(GTK_SPIN_BUTTON(temp),
249 							  cfg_get_single_value_as_int_with_default(config, "Network Settings", "Proxy Port", 8080));
250 
251 	/* use auth */
252 	temp = gtk_builder_get_object(proxy_pref_xml, "checkbutton_use_auth");
253 	gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(temp),
254 								 cfg_get_single_value_as_int_with_default(config, "Network Settings",
255 																		  "Use authentication", FALSE));
256 
257 	/* username */
258 	temp = gtk_builder_get_object(proxy_pref_xml, "entry_auth_username");
259 	string = cfg_get_single_value_as_string(config, "Network Settings", "Proxy authentication username");
260 	if (string)
261 	{
262 		gtk_entry_set_text(GTK_ENTRY(temp), string);
263 		g_free(string);
264 	}
265 
266 	/* username */
267 	temp = gtk_builder_get_object(proxy_pref_xml, "entry_auth_password");
268 	string = cfg_get_single_value_as_string(config, "Network Settings", "Proxy authentication password");
269 	if (string)
270 	{
271 		gtk_entry_set_text(GTK_ENTRY(temp), string);
272 		g_free(string);
273 	}
274 
275 	/* signal autoconnect */
276 	gtk_builder_connect_signals(proxy_pref_xml, NULL);
277 	/* Add to parent */
278 	temp = gtk_builder_get_object(proxy_pref_xml, "frame_proxy_settings");
279 	gtk_container_add(GTK_CONTAINER(container), GTK_WIDGET(temp));
280 	gtk_widget_show_all(container);
281 }
282 
283 gmpcPrefPlugin proxyplug_pref = {
284 	.construct = proxy_pref_construct,
285 	.destroy = proxy_pref_destroy
286 };
287 
288 gmpcPlugin proxyplug = {
289 	.name = N_("Proxy"),
290 	.version = {0, 0, 0}
291 	,
292 	.plugin_type = GMPC_INTERNALL,
293 	.pref = &proxyplug_pref
294 };
295 
296 /**
297  * LIBSOUP BASED ASYNC DOWNLOADER
298  */
299 
300 static void gmpc_easy_async_free_handler_real(GEADAsyncHandler * handle);
301 typedef struct
302 {
303 	SoupMessage *msg;
304 	gchar *uri;
305 	GEADAsyncCallback callback;
306 	gpointer userdata;
307 	gchar *data;
308 	goffset length;
309 	int is_gzip;
310 	int is_deflate;
311 	z_stream *z;
312 	gpointer extra_data;
313 	guint uid;
314 	guint old_status_code;
315 } _GEADAsyncHandler;
316 
317 static guint uid = 0;
gmpc_easy_async_headers_update(SoupMessage * msg,gpointer data)318 static void gmpc_easy_async_headers_update(SoupMessage * msg, gpointer data)
319 {
320 	_GEADAsyncHandler *d = data;
321 	const gchar *encoding = soup_message_headers_get(msg->response_headers, "Content-Encoding");
322 	goffset size = soup_message_headers_get_content_length(msg->response_headers);
323 	g_log("EasyDownload", G_LOG_LEVEL_DEBUG,
324 			  "Expected download length: %u",(guint)size);
325 	if (encoding)
326 	{
327 		if (strcmp(encoding, "gzip") == 0)
328 		{
329 			d->is_gzip = 1;
330 			g_log(LOG_DOMAIN, G_LOG_LEVEL_DEBUG, "Url is gzipped");
331 		} else if (strcmp(encoding, "deflate") == 0)
332 		{
333 			d->is_deflate = 1;
334 			g_log(LOG_DOMAIN, G_LOG_LEVEL_DEBUG, "Url is enflated");
335 		}
336 	}
337 
338 	/* If a second set comes in, close that */
339 	else
340 	{
341 		d->is_gzip = 0;
342 		d->is_deflate = 0;
343 	}
344 
345 	/**
346 	 * Don't record data from redirection, in a while it _will_ be redirected,
347 	 * We care about that
348 	 */
349 	if (d->old_status_code != 0 && d->old_status_code != msg->status_code)
350 	{
351 		g_log("EasyDownload", G_LOG_LEVEL_DEBUG,
352 			  "Cleaning out the previous block of data: status_code:  %i(old) ->%i(new)",
353 			  d->old_status_code, msg->status_code);
354 		/* Clear buffer */
355 		g_free(d->data);
356 		d->data = NULL;
357 		d->length = 0;
358 		if (d->z)
359 			close_cb(d->z);
360 		d->z = NULL;
361 	}
362 	d->old_status_code = msg->status_code;
363 }
364 
gmpc_easy_async_status_update(SoupMessage * msg,SoupBuffer * buffer,gpointer data)365 static void gmpc_easy_async_status_update(SoupMessage * msg, SoupBuffer * buffer, gpointer data)
366 {
367 	_GEADAsyncHandler *d = data;
368 	/* don't store error data, not used anyway */
369 	if (!SOUP_STATUS_IS_SUCCESSFUL(msg->status_code))
370 	{
371 		g_log(LOG_DOMAIN, G_LOG_LEVEL_DEBUG,
372 						"Error mesg status code: %i\n", msg->status_code);
373 		return;
374 	}
375 	if (d->is_gzip || d->is_deflate)
376 	{
377 		if (d->z == NULL)
378 		{
379 			long data_start;
380 			d->z = g_malloc0(sizeof(*d->z));
381 			data_start = (d->is_gzip == 1) ? skip_gzip_header(buffer->data, buffer->length) : 0;
382 			d->z->next_in = (void *)((buffer->data) + data_start);
383 			d->z->avail_in = buffer->length - data_start;
384             d->z->zalloc = NULL;
385             d->z->zfree = NULL;
386             d->z->opaque = NULL;
387 			if (inflateInit2(d->z, -MAX_WBITS) == Z_OK)
388 			{
389 				int res = 0;
390 				do
391 				{
392 					d->data = g_realloc(d->data, d->length + 12 * 1024 + 1);
393 					res = read_cb(d->z, &(d->data[d->length]), 12 * 1024);
394 
395 					if (res > 0)
396 						d->length += res;
397 
398 					d->data[d->length] = '\0';
399 				} while (res > 0);
400 				if (res < 0)
401 				{
402 					g_log(LOG_DOMAIN, G_LOG_LEVEL_WARNING, "Failure during unzipping 1: %s", d->uri);
403 					soup_session_cancel_message(soup_session, d->msg, SOUP_STATUS_MALFORMED);
404 				}
405 			} else
406 			{
407 				/* give error */
408 				g_log(LOG_DOMAIN, G_LOG_LEVEL_WARNING, "Failure during inflateInit2: %s", d->uri);
409 				soup_session_cancel_message(soup_session, d->msg, SOUP_STATUS_MALFORMED);
410 			}
411 		} else
412 		{
413 			int res = 0;
414 			d->z->next_in = (void *)((buffer->data));
415 			d->z->avail_in = buffer->length;
416 			do
417 			{
418 				d->data = g_realloc(d->data, d->length + 12 * 1024 + 1);
419 				res = read_cb(d->z, &(d->data[d->length]), 12 * 1024);
420 
421 				if (res > 0)
422 					d->length += res;
423 
424 				d->data[d->length] = '\0';
425 			} while (res > 0);
426 			if (res < 0)
427 			{
428 				g_log(LOG_DOMAIN, G_LOG_LEVEL_WARNING, "Failure during unzipping 2: %s", d->uri);
429 				soup_session_cancel_message(soup_session, d->msg, SOUP_STATUS_MALFORMED);
430 			}
431 		}
432 
433 	} else
434 	{
435 		d->data = g_realloc(d->data, d->length + buffer->length + 1);
436 		g_memmove(&(d->data[d->length]), buffer->data, (size_t) buffer->length);
437 		d->length += buffer->length;
438 		d->data[d->length] = '\0';
439 	}
440 	d->callback((GEADAsyncHandler *) d, GEAD_PROGRESS, d->userdata);
441 }
442 
gmpc_easy_async_callback(SoupSession * session,SoupMessage * msg,gpointer data)443 static void gmpc_easy_async_callback(SoupSession * session, SoupMessage * msg, gpointer data)
444 {
445 	_GEADAsyncHandler *d = data;
446 	if (SOUP_STATUS_IS_SUCCESSFUL(msg->status_code))
447 	{
448 		d->callback((GEADAsyncHandler *) d, GEAD_DONE, d->userdata);
449 	} else if (msg->status_code == SOUP_STATUS_CANCELLED)
450 	{
451 		d->callback((GEADAsyncHandler *) d, GEAD_CANCELLED, d->userdata);
452 	} else
453 	{
454 		d->callback((GEADAsyncHandler *) d, GEAD_FAILED, d->userdata);
455 	}
456 	gmpc_easy_async_free_handler_real((GEADAsyncHandler *) d);
457 }
458 
gmpc_easy_async_free_handler_real(GEADAsyncHandler * handle)459 static void gmpc_easy_async_free_handler_real(GEADAsyncHandler * handle)
460 {
461 	_GEADAsyncHandler *d = (_GEADAsyncHandler *) handle;
462 	if (d->z)
463 		close_cb(d->z);
464 	g_free(d->uri);
465 	g_free(d->data);
466 	g_free(d);
467 }
468 
469 /**
470  * Get the total size of the download, if available.
471  */
gmpc_easy_handler_get_content_size(const GEADAsyncHandler * handle)472 goffset gmpc_easy_handler_get_content_size(const GEADAsyncHandler * handle)
473 {
474 	_GEADAsyncHandler *d = (_GEADAsyncHandler *) handle;
475 	return soup_message_headers_get_content_length(d->msg->response_headers);
476 }
477 
gmpc_easy_handler_get_uri(const GEADAsyncHandler * handle)478 const char *gmpc_easy_handler_get_uri(const GEADAsyncHandler * handle)
479 {
480 	_GEADAsyncHandler *d = (_GEADAsyncHandler *) handle;
481 	return d->uri;
482 }
483 
gmpc_easy_handler_get_data(const GEADAsyncHandler * handle,goffset * length)484 const char *gmpc_easy_handler_get_data(const GEADAsyncHandler * handle, goffset * length)
485 {
486 	_GEADAsyncHandler *d = (_GEADAsyncHandler *) handle;
487 	if (length)
488 		*length = d->length;
489 	return d->data;
490 }
491 
gmpc_easy_handler_get_data_vala_wrap(const GEADAsyncHandler * handle,gint * length)492 const guchar *gmpc_easy_handler_get_data_vala_wrap(const GEADAsyncHandler * handle, gint * length)
493 {
494 	_GEADAsyncHandler *d = (_GEADAsyncHandler *) handle;
495 	if (length)
496 		*length = (gint) d->length;
497 	return (guchar *) d->data;
498 }
gmpc_easy_handler_get_data_as_string(const GEADAsyncHandler * handle)499 const char *gmpc_easy_handler_get_data_as_string(const GEADAsyncHandler * handle)
500 {
501 	_GEADAsyncHandler *d = (_GEADAsyncHandler *) handle;
502 	g_assert(d->data[d->length] == '\0');
503 	return (gchar *) d->data;
504 }
gmpc_easy_handler_set_user_data(const GEADAsyncHandler * handle,gpointer user_data)505 void gmpc_easy_handler_set_user_data(const GEADAsyncHandler * handle, gpointer user_data)
506 {
507 	_GEADAsyncHandler *d = (_GEADAsyncHandler *) handle;
508 	d->extra_data = user_data;
509 }
510 
gmpc_easy_handler_get_user_data(const GEADAsyncHandler * handle)511 gpointer gmpc_easy_handler_get_user_data(const GEADAsyncHandler * handle)
512 {
513 	_GEADAsyncHandler *d = (_GEADAsyncHandler *) handle;
514 	return d->extra_data;
515 }
516 
gmpc_easy_async_cancel(const GEADAsyncHandler * handle)517 void gmpc_easy_async_cancel(const GEADAsyncHandler * handle)
518 {
519 	_GEADAsyncHandler *d = (_GEADAsyncHandler *) handle;
520 	soup_session_cancel_message(soup_session, d->msg, SOUP_STATUS_CANCELLED);
521 }
522 
gmpc_easy_async_downloader(const gchar * uri,GEADAsyncCallback callback,gpointer user_data)523 GEADAsyncHandler *gmpc_easy_async_downloader(const gchar * uri, GEADAsyncCallback callback, gpointer user_data)
524 {
525 	if (uri == NULL)
526 	{
527 		g_log(LOG_DOMAIN,G_LOG_LEVEL_WARNING, "No download uri specified.");
528 		return NULL;
529 	}
530 	return gmpc_easy_async_downloader_with_headers(uri, callback, user_data, NULL);
531 }
532 
gmpc_easy_async_downloader_with_headers(const gchar * uri,GEADAsyncCallback callback,gpointer user_data,...)533 GEADAsyncHandler *gmpc_easy_async_downloader_with_headers(const gchar * uri, GEADAsyncCallback callback,
534 														  gpointer user_data, ...)
535 {
536 	SoupMessage *msg;
537 	_GEADAsyncHandler *d;
538 	va_list ap;
539 	char *va_entry;
540 	if (soup_session == NULL)
541 	{
542 		soup_session = soup_session_async_new();
543 		gmpc_easy_download_set_proxy(soup_session);
544 		g_object_set(soup_session, "timeout", 5, NULL);
545 		/* Set user agent, to get around wikipedia ban. */
546 		g_object_set(soup_session, "user-agent", "gmpc ",NULL);
547 	}
548 
549 	msg = soup_message_new("GET", uri);
550 	if (!msg)
551 		return NULL;
552 
553 	soup_message_headers_append(msg->request_headers, "Accept-Encoding", "deflate,gzip");
554 	va_start(ap, user_data);
555 	va_entry = va_arg(ap, typeof(va_entry));
556 	while (va_entry)
557 	{
558 		char *value = va_arg(ap, typeof(value));
559 		soup_message_headers_append(msg->request_headers, va_entry, value);
560 		va_entry = va_arg(ap, typeof(va_entry));
561 	}
562 	va_end(ap);
563 
564 	d = g_malloc0(sizeof(*d));
565 	d->uid = ++uid;
566 	d->is_gzip = 0;
567 	d->is_deflate = 0;
568 	d->z = NULL;
569 	d->data = NULL;
570 	d->msg = msg;
571 	d->uri = g_strdup(uri);
572 	d->callback = callback;
573 	d->userdata = user_data;
574 	d->extra_data = NULL;
575 	d->old_status_code = 0;
576 	soup_message_body_set_accumulate(msg->response_body, FALSE);
577 	g_signal_connect_after(msg, "got-chunk", G_CALLBACK(gmpc_easy_async_status_update), d);
578 	g_signal_connect_after(msg, "got-headers", G_CALLBACK(gmpc_easy_async_headers_update), d);
579 	soup_session_queue_message(soup_session, msg, gmpc_easy_async_callback, d);
580 
581 	return (GEADAsyncHandler *) d;
582 }
583 
gmpc_easy_async_quit(void)584 void gmpc_easy_async_quit(void)
585 {
586 	if (soup_session)
587 	{
588 		soup_session_abort(soup_session);
589 		g_object_unref(soup_session);
590 		soup_session = NULL;
591 	}
592 }
593 
gmpc_easy_download_uri_escape(const char * part)594 char *gmpc_easy_download_uri_escape(const char *part)
595 {
596 	return soup_uri_encode(part, "&+");
597 }
598 
599 /**************************************************************/
600 
601 
602 typedef struct {
603 	void *a;
604 	void *b;
605 	GEADAsyncCallbackVala callback;
606 } valaf;
607 
temp_callback(const GEADAsyncHandler * handle,GEADStatus status,gpointer user_data)608 static void temp_callback(const GEADAsyncHandler *handle, GEADStatus status, gpointer user_data)
609 {
610 	valaf *f = (valaf*)user_data;
611 	f->callback(handle, status, f->b, f->a);
612 	if(status != GEAD_PROGRESS) g_free(f);
613 }
gmpc_easy_async_downloader_vala(const char * path,gpointer user_data2,GEADAsyncCallbackVala callback,gpointer user_data)614 GEADAsyncHandler * gmpc_easy_async_downloader_vala(const char *path, gpointer user_data2, GEADAsyncCallbackVala callback,
615 														  gpointer user_data
616 														  )
617 {
618 	valaf *f = g_malloc0(sizeof(*f));
619 	f->a = user_data;
620 	f->b =user_data2;
621 	f->callback = callback;
622 	return gmpc_easy_async_downloader(path, temp_callback, f);
623 }
624 
625 
626 /* vim: set noexpandtab ts=4 sw=4 sts=4 tw=120: */
627