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