dashdemux: add support for HTTP HEAD method of time sync
[platform/upstream/gstreamer.git] / gst-libs / gst / uridownloader / gsturidownloader.c
1 /* GStreamer
2  * Copyright (C) 2011 Andoni Morales Alastruey <ylatuya@gmail.com>
3  *
4  * gstfragment.c:
5  *
6  * This library is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU Library General Public
8  * License as published by the Free Software Foundation; either
9  * version 2 of the License, or (at your option) any later version.
10  *
11  * This library 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  * Library General Public License for more details.
15  *
16  * You should have received a copy of the GNU Library General Public
17  * License along with this library; if not, write to the
18  * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
19  * Boston, MA 02110-1301, USA.
20  */
21
22 #include <glib.h>
23 #include "gstfragment.h"
24 #include "gsturidownloader.h"
25 #include "gsturidownloader_debug.h"
26
27 #define GST_CAT_DEFAULT uridownloader_debug
28 GST_DEBUG_CATEGORY (uridownloader_debug);
29
30 #define GST_URI_DOWNLOADER_GET_PRIVATE(obj)  \
31    (G_TYPE_INSTANCE_GET_PRIVATE ((obj), \
32     GST_TYPE_URI_DOWNLOADER, GstUriDownloaderPrivate))
33
34 struct _GstUriDownloaderPrivate
35 {
36   /* Fragments fetcher */
37   GstElement *urisrc;
38   GstBus *bus;
39   GstPad *pad;
40   GTimeVal *timeout;
41   GstFragment *download;
42   gboolean got_buffer;
43   GMutex download_lock;         /* used to restrict to one download only */
44
45   GError *err;
46
47   GCond cond;
48   gboolean cancelled;
49 };
50
51 static void gst_uri_downloader_finalize (GObject * object);
52 static void gst_uri_downloader_dispose (GObject * object);
53
54 static GstFlowReturn gst_uri_downloader_chain (GstPad * pad, GstObject * parent,
55     GstBuffer * buf);
56 static gboolean gst_uri_downloader_sink_event (GstPad * pad, GstObject * parent,
57     GstEvent * event);
58 static GstBusSyncReply gst_uri_downloader_bus_handler (GstBus * bus,
59     GstMessage * message, gpointer data);
60
61 static GstStaticPadTemplate sinkpadtemplate = GST_STATIC_PAD_TEMPLATE ("sink",
62     GST_PAD_SINK,
63     GST_PAD_ALWAYS,
64     GST_STATIC_CAPS_ANY);
65
66 #define _do_init \
67 { \
68   GST_DEBUG_CATEGORY_INIT (uridownloader_debug, "uridownloader", 0, "URI downloader"); \
69 }
70
71 G_DEFINE_TYPE_WITH_CODE (GstUriDownloader, gst_uri_downloader, GST_TYPE_OBJECT,
72     _do_init);
73
74 static void
75 gst_uri_downloader_class_init (GstUriDownloaderClass * klass)
76 {
77   GObjectClass *gobject_class;
78
79   gobject_class = (GObjectClass *) klass;
80
81   g_type_class_add_private (klass, sizeof (GstUriDownloaderPrivate));
82
83   gobject_class->dispose = gst_uri_downloader_dispose;
84   gobject_class->finalize = gst_uri_downloader_finalize;
85 }
86
87 static void
88 gst_uri_downloader_init (GstUriDownloader * downloader)
89 {
90   downloader->priv = GST_URI_DOWNLOADER_GET_PRIVATE (downloader);
91
92   /* Initialize the sink pad. This pad will be connected to the src pad of the
93    * element created with gst_element_make_from_uri and will handle the download */
94   downloader->priv->pad =
95       gst_pad_new_from_static_template (&sinkpadtemplate, "sink");
96   gst_pad_set_chain_function (downloader->priv->pad,
97       GST_DEBUG_FUNCPTR (gst_uri_downloader_chain));
98   gst_pad_set_event_function (downloader->priv->pad,
99       GST_DEBUG_FUNCPTR (gst_uri_downloader_sink_event));
100   gst_pad_set_element_private (downloader->priv->pad, downloader);
101   gst_pad_set_active (downloader->priv->pad, TRUE);
102
103   /* Create a bus to handle error and warning message from the source element */
104   downloader->priv->bus = gst_bus_new ();
105
106   g_mutex_init (&downloader->priv->download_lock);
107   g_cond_init (&downloader->priv->cond);
108 }
109
110 static void
111 gst_uri_downloader_dispose (GObject * object)
112 {
113   GstUriDownloader *downloader = GST_URI_DOWNLOADER (object);
114
115   if (downloader->priv->urisrc != NULL) {
116     gst_element_set_state (downloader->priv->urisrc, GST_STATE_NULL);
117     gst_object_unref (downloader->priv->urisrc);
118     downloader->priv->urisrc = NULL;
119   }
120
121   if (downloader->priv->bus != NULL) {
122     gst_object_unref (downloader->priv->bus);
123     downloader->priv->bus = NULL;
124   }
125
126   if (downloader->priv->pad) {
127     gst_object_unref (downloader->priv->pad);
128     downloader->priv->pad = NULL;
129   }
130
131   if (downloader->priv->download) {
132     g_object_unref (downloader->priv->download);
133     downloader->priv->download = NULL;
134   }
135
136   G_OBJECT_CLASS (gst_uri_downloader_parent_class)->dispose (object);
137 }
138
139 static void
140 gst_uri_downloader_finalize (GObject * object)
141 {
142   GstUriDownloader *downloader = GST_URI_DOWNLOADER (object);
143
144   g_mutex_clear (&downloader->priv->download_lock);
145   g_cond_clear (&downloader->priv->cond);
146
147   G_OBJECT_CLASS (gst_uri_downloader_parent_class)->finalize (object);
148 }
149
150 GstUriDownloader *
151 gst_uri_downloader_new (void)
152 {
153   return g_object_new (GST_TYPE_URI_DOWNLOADER, NULL);
154 }
155
156 static gboolean
157 gst_uri_downloader_sink_event (GstPad * pad, GstObject * parent,
158     GstEvent * event)
159 {
160   gboolean ret = FALSE;
161   GstUriDownloader *downloader;
162
163   downloader = GST_URI_DOWNLOADER (gst_pad_get_element_private (pad));
164
165   switch (event->type) {
166     case GST_EVENT_EOS:{
167       GST_OBJECT_LOCK (downloader);
168       GST_DEBUG_OBJECT (downloader, "Got EOS on the fetcher pad");
169       if (downloader->priv->download != NULL) {
170         /* signal we have fetched the URI */
171         downloader->priv->download->completed = TRUE;
172         downloader->priv->download->download_stop_time =
173             gst_util_get_timestamp ();
174         GST_DEBUG_OBJECT (downloader, "Signaling chain funtion");
175         g_cond_signal (&downloader->priv->cond);
176       }
177       GST_OBJECT_UNLOCK (downloader);
178       gst_event_unref (event);
179       break;
180     }
181     case GST_EVENT_CUSTOM_DOWNSTREAM_STICKY:{
182       const GstStructure *str;
183       str = gst_event_get_structure (event);
184       if (gst_structure_has_name (str, "http-headers")) {
185         GST_OBJECT_LOCK (downloader);
186         if (downloader->priv->download != NULL) {
187           if (downloader->priv->download->headers)
188             gst_structure_free (downloader->priv->download->headers);
189           downloader->priv->download->headers = gst_structure_copy (str);
190         }
191         GST_OBJECT_UNLOCK (downloader);
192       }
193     }
194       /* falls through */
195     default:
196       ret = gst_pad_event_default (pad, parent, event);
197       break;
198   }
199
200   return ret;
201 }
202
203 static GstBusSyncReply
204 gst_uri_downloader_bus_handler (GstBus * bus,
205     GstMessage * message, gpointer data)
206 {
207   GstUriDownloader *downloader = (GstUriDownloader *) (data);
208
209   if (GST_MESSAGE_TYPE (message) == GST_MESSAGE_ERROR) {
210     GError *err = NULL;
211     gchar *dbg_info = NULL;
212     gchar *new_error = NULL;
213
214     gst_message_parse_error (message, &err, &dbg_info);
215     GST_WARNING_OBJECT (downloader,
216         "Received error: %s from %s, the download will be cancelled",
217         err->message, GST_OBJECT_NAME (message->src));
218     GST_DEBUG ("Debugging info: %s\n", (dbg_info) ? dbg_info : "none");
219
220     if (dbg_info)
221       new_error = g_strdup_printf ("%s: %s\n", err->message, dbg_info);
222     if (new_error) {
223       g_free (err->message);
224       err->message = new_error;
225     }
226
227     if (!downloader->priv->err)
228       downloader->priv->err = err;
229     else
230       g_error_free (err);
231
232     g_free (dbg_info);
233
234     /* remove the sync handler to avoid duplicated messages */
235     gst_bus_set_sync_handler (downloader->priv->bus, NULL, NULL, NULL);
236
237     /* stop the download */
238     GST_OBJECT_LOCK (downloader);
239     if (downloader->priv->download != NULL) {
240       GST_DEBUG_OBJECT (downloader, "Stopping download");
241       g_object_unref (downloader->priv->download);
242       downloader->priv->download = NULL;
243       downloader->priv->cancelled = TRUE;
244       g_cond_signal (&downloader->priv->cond);
245     }
246     GST_OBJECT_UNLOCK (downloader);
247   } else if (GST_MESSAGE_TYPE (message) == GST_MESSAGE_WARNING) {
248     GError *err = NULL;
249     gchar *dbg_info = NULL;
250
251     gst_message_parse_warning (message, &err, &dbg_info);
252     GST_WARNING_OBJECT (downloader,
253         "Received warning: %s from %s",
254         GST_OBJECT_NAME (message->src), err->message);
255     GST_DEBUG ("Debugging info: %s\n", (dbg_info) ? dbg_info : "none");
256     g_error_free (err);
257     g_free (dbg_info);
258   }
259
260   gst_message_unref (message);
261   return GST_BUS_DROP;
262 }
263
264 static GstFlowReturn
265 gst_uri_downloader_chain (GstPad * pad, GstObject * parent, GstBuffer * buf)
266 {
267   GstUriDownloader *downloader;
268
269   downloader = GST_URI_DOWNLOADER (gst_pad_get_element_private (pad));
270
271   /* HTML errors (404, 500, etc...) are also pushed through this pad as
272    * response but the source element will also post a warning or error message
273    * in the bus, which is handled synchronously cancelling the download.
274    */
275   GST_OBJECT_LOCK (downloader);
276   if (downloader->priv->download == NULL) {
277     /* Download cancelled, quit */
278     gst_buffer_unref (buf);
279     GST_OBJECT_UNLOCK (downloader);
280     goto done;
281   }
282
283   GST_LOG_OBJECT (downloader, "The uri fetcher received a new buffer "
284       "of size %" G_GSIZE_FORMAT, gst_buffer_get_size (buf));
285   downloader->priv->got_buffer = TRUE;
286   if (!gst_fragment_add_buffer (downloader->priv->download, buf)) {
287     GST_WARNING_OBJECT (downloader, "Could not add buffer to fragment");
288     gst_buffer_unref (buf);
289   }
290   GST_OBJECT_UNLOCK (downloader);
291
292 done:
293   {
294     return GST_FLOW_OK;
295   }
296 }
297
298 void
299 gst_uri_downloader_reset (GstUriDownloader * downloader)
300 {
301   g_return_if_fail (downloader != NULL);
302
303   GST_OBJECT_LOCK (downloader);
304   downloader->priv->cancelled = FALSE;
305   GST_OBJECT_UNLOCK (downloader);
306 }
307
308 void
309 gst_uri_downloader_cancel (GstUriDownloader * downloader)
310 {
311   GST_OBJECT_LOCK (downloader);
312   if (downloader->priv->download != NULL) {
313     GST_DEBUG_OBJECT (downloader, "Cancelling download");
314     g_object_unref (downloader->priv->download);
315     downloader->priv->download = NULL;
316     downloader->priv->cancelled = TRUE;
317     GST_DEBUG_OBJECT (downloader, "Signaling chain funtion");
318     g_cond_signal (&downloader->priv->cond);
319   } else {
320     gboolean cancelled;
321
322     cancelled = downloader->priv->cancelled;
323     downloader->priv->cancelled = TRUE;
324     if (cancelled)
325       GST_DEBUG_OBJECT (downloader,
326           "Trying to cancel a download that was alredy cancelled");
327   }
328   GST_OBJECT_UNLOCK (downloader);
329 }
330
331 static gboolean
332 gst_uri_downloader_set_range (GstUriDownloader * downloader,
333     gint64 range_start, gint64 range_end)
334 {
335   g_return_val_if_fail (range_start >= 0, FALSE);
336   g_return_val_if_fail (range_end >= -1, FALSE);
337
338   if (range_start || (range_end >= 0)) {
339     GstEvent *seek;
340
341     seek = gst_event_new_seek (1.0, GST_FORMAT_BYTES, GST_SEEK_FLAG_FLUSH,
342         GST_SEEK_TYPE_SET, range_start, GST_SEEK_TYPE_SET, range_end);
343
344     return gst_element_send_event (downloader->priv->urisrc, seek);
345   }
346   return TRUE;
347 }
348
349 static gboolean
350 gst_uri_downloader_set_uri (GstUriDownloader * downloader, const gchar * uri,
351     const gchar * referer, gboolean compress, gboolean refresh,
352     gboolean allow_cache)
353 {
354   GstPad *pad;
355   GObjectClass *gobject_class;
356
357   if (!gst_uri_is_valid (uri))
358     return FALSE;
359
360   if (downloader->priv->urisrc) {
361     gchar *old_protocol, *new_protocol;
362     gchar *old_uri;
363
364     old_uri =
365         gst_uri_handler_get_uri (GST_URI_HANDLER (downloader->priv->urisrc));
366     old_protocol = gst_uri_get_protocol (old_uri);
367     new_protocol = gst_uri_get_protocol (uri);
368
369     if (!g_str_equal (old_protocol, new_protocol)) {
370       gst_element_set_state (downloader->priv->urisrc, GST_STATE_NULL);
371       gst_object_unref (downloader->priv->urisrc);
372       downloader->priv->urisrc = NULL;
373       GST_DEBUG_OBJECT (downloader, "Can't re-use old source element");
374     } else {
375       GError *err = NULL;
376
377       GST_DEBUG_OBJECT (downloader, "Re-using old source element");
378       if (!gst_uri_handler_set_uri (GST_URI_HANDLER (downloader->priv->urisrc),
379               uri, &err)) {
380         GST_DEBUG_OBJECT (downloader, "Failed to re-use old source element: %s",
381             err->message);
382         g_clear_error (&err);
383         gst_element_set_state (downloader->priv->urisrc, GST_STATE_NULL);
384         gst_object_unref (downloader->priv->urisrc);
385         downloader->priv->urisrc = NULL;
386       }
387     }
388     g_free (old_uri);
389     g_free (old_protocol);
390     g_free (new_protocol);
391   }
392
393   if (!downloader->priv->urisrc) {
394     GST_DEBUG_OBJECT (downloader, "Creating source element for the URI:%s",
395         uri);
396     downloader->priv->urisrc =
397         gst_element_make_from_uri (GST_URI_SRC, uri, NULL, NULL);
398     if (!downloader->priv->urisrc)
399       return FALSE;
400   }
401
402   gobject_class = G_OBJECT_GET_CLASS (downloader->priv->urisrc);
403   if (g_object_class_find_property (gobject_class, "compress"))
404     g_object_set (downloader->priv->urisrc, "compress", compress, NULL);
405   if (g_object_class_find_property (gobject_class, "keep-alive"))
406     g_object_set (downloader->priv->urisrc, "keep-alive", TRUE, NULL);
407   if (g_object_class_find_property (gobject_class, "extra-headers")) {
408     if (referer || refresh || !allow_cache) {
409       GstStructure *extra_headers = gst_structure_new_empty ("headers");
410
411       if (referer)
412         gst_structure_set (extra_headers, "Referer", G_TYPE_STRING, referer,
413             NULL);
414
415       if (!allow_cache)
416         gst_structure_set (extra_headers, "Cache-Control", G_TYPE_STRING,
417             "no-cache", NULL);
418       else if (refresh)
419         gst_structure_set (extra_headers, "Cache-Control", G_TYPE_STRING,
420             "max-age=0", NULL);
421
422       g_object_set (downloader->priv->urisrc, "extra-headers", extra_headers,
423           NULL);
424
425       gst_structure_free (extra_headers);
426     } else {
427       g_object_set (downloader->priv->urisrc, "extra-headers", NULL, NULL);
428     }
429   }
430
431   /* add a sync handler for the bus messages to detect errors in the download */
432   gst_element_set_bus (GST_ELEMENT (downloader->priv->urisrc),
433       downloader->priv->bus);
434   gst_bus_set_sync_handler (downloader->priv->bus,
435       gst_uri_downloader_bus_handler, downloader, NULL);
436
437   pad = gst_element_get_static_pad (downloader->priv->urisrc, "src");
438   if (!pad)
439     return FALSE;
440   gst_pad_link (pad, downloader->priv->pad);
441   gst_object_unref (pad);
442   return TRUE;
443 }
444
445 static gboolean
446 gst_uri_downloader_set_method (GstUriDownloader * downloader,
447     const gchar * method)
448 {
449   GObjectClass *gobject_class;
450
451   if (!downloader->priv->urisrc)
452     return FALSE;
453
454   gobject_class = G_OBJECT_GET_CLASS (downloader->priv->urisrc);
455   if (g_object_class_find_property (gobject_class, "method")) {
456     g_object_set (downloader->priv->urisrc, "method", method, NULL);
457     return TRUE;
458   }
459   return FALSE;
460 }
461
462 GstFragment *
463 gst_uri_downloader_fetch_uri (GstUriDownloader * downloader,
464     const gchar * uri, const gchar * referer, gboolean compress,
465     gboolean refresh, gboolean allow_cache, GError ** err)
466 {
467   return gst_uri_downloader_fetch_uri_with_range (downloader, uri,
468       referer, compress, refresh, allow_cache, 0, -1, err);
469 }
470
471 /**
472  * gst_uri_downloader_fetch_uri_with_range:
473  * @downloader: the #GstUriDownloader
474  * @uri: the uri
475  * @range_start: the starting byte index
476  * @range_end: the final byte index, use -1 for unspecified
477  *
478  * Returns the downloaded #GstFragment
479  */
480 GstFragment *
481 gst_uri_downloader_fetch_uri_with_range (GstUriDownloader *
482     downloader, const gchar * uri, const gchar * referer, gboolean compress,
483     gboolean refresh, gboolean allow_cache,
484     gint64 range_start, gint64 range_end, GError ** err)
485 {
486   GstStateChangeReturn ret;
487   GstFragment *download = NULL;
488
489   GST_DEBUG_OBJECT (downloader, "Fetching URI %s", uri);
490
491   g_mutex_lock (&downloader->priv->download_lock);
492   downloader->priv->err = NULL;
493   downloader->priv->got_buffer = FALSE;
494
495   GST_OBJECT_LOCK (downloader);
496   if (downloader->priv->cancelled) {
497     GST_DEBUG_OBJECT (downloader, "Cancelled, aborting fetch");
498     goto quit;
499   }
500
501   if (!gst_uri_downloader_set_uri (downloader, uri, referer, compress, refresh,
502           allow_cache)) {
503     GST_WARNING_OBJECT (downloader, "Failed to set URI");
504     goto quit;
505   }
506
507   gst_bus_set_flushing (downloader->priv->bus, FALSE);
508   if (downloader->priv->download)
509     g_object_unref (downloader->priv->download);
510   downloader->priv->download = gst_fragment_new ();
511   downloader->priv->download->range_start = range_start;
512   downloader->priv->download->range_end = range_end;
513   GST_OBJECT_UNLOCK (downloader);
514   ret = gst_element_set_state (downloader->priv->urisrc, GST_STATE_READY);
515   GST_OBJECT_LOCK (downloader);
516   if (ret == GST_STATE_CHANGE_FAILURE || downloader->priv->download == NULL) {
517     GST_WARNING_OBJECT (downloader, "Failed to set src to READY");
518     goto quit;
519   }
520
521   /* might have been cancelled because of failures in state change */
522   if (downloader->priv->cancelled) {
523     goto quit;
524   }
525
526   if (range_start < 0 && range_end < 0) {
527     if (!gst_uri_downloader_set_method (downloader, "HEAD")) {
528       GST_WARNING_OBJECT (downloader, "Failed to set HTTP method");
529       goto quit;
530     }
531   } else {
532     if (!gst_uri_downloader_set_range (downloader, range_start, range_end)) {
533       GST_WARNING_OBJECT (downloader, "Failed to set range");
534       goto quit;
535     }
536   }
537
538   GST_OBJECT_UNLOCK (downloader);
539   ret = gst_element_set_state (downloader->priv->urisrc, GST_STATE_PLAYING);
540   GST_OBJECT_LOCK (downloader);
541   if (ret == GST_STATE_CHANGE_FAILURE) {
542     if (downloader->priv->download) {
543       g_object_unref (downloader->priv->download);
544       downloader->priv->download = NULL;
545     }
546     goto quit;
547   }
548
549   /* might have been cancelled because of failures in state change */
550   if (downloader->priv->cancelled) {
551     goto quit;
552   }
553
554   /* wait until:
555    *   - the download succeed (EOS in the src pad)
556    *   - the download failed (Error message on the fetcher bus)
557    *   - the download was canceled
558    */
559   GST_DEBUG_OBJECT (downloader, "Waiting to fetch the URI %s", uri);
560   while (!downloader->priv->cancelled && !downloader->priv->download->completed)
561     g_cond_wait (&downloader->priv->cond, GST_OBJECT_GET_LOCK (downloader));
562
563   if (downloader->priv->cancelled) {
564     if (downloader->priv->download) {
565       g_object_unref (downloader->priv->download);
566       downloader->priv->download = NULL;
567     }
568     goto quit;
569   }
570
571   download = downloader->priv->download;
572   downloader->priv->download = NULL;
573   if (!downloader->priv->got_buffer) {
574     if (download->range_start < 0 && download->range_end < 0) {
575       /* HEAD request, so we don't expect a response */
576     } else {
577       g_object_unref (download);
578       download = NULL;
579       GST_ERROR_OBJECT (downloader, "Didn't retrieve a buffer before EOS");
580     }
581   }
582
583   if (download != NULL)
584     GST_INFO_OBJECT (downloader, "URI fetched successfully");
585   else
586     GST_INFO_OBJECT (downloader, "Error fetching URI");
587
588 quit:
589   {
590     if (downloader->priv->urisrc) {
591       GstPad *pad;
592       GstElement *urisrc;
593
594       urisrc = downloader->priv->urisrc;
595
596       GST_DEBUG_OBJECT (downloader, "Stopping source element %s",
597           GST_ELEMENT_NAME (urisrc));
598
599       /* remove the bus' sync handler */
600       gst_bus_set_sync_handler (downloader->priv->bus, NULL, NULL, NULL);
601       gst_bus_set_flushing (downloader->priv->bus, TRUE);
602
603       /* set the element state to NULL */
604       GST_OBJECT_UNLOCK (downloader);
605       if (download == NULL) {
606         gst_element_set_state (urisrc, GST_STATE_NULL);
607       } else {
608         GstQuery *query;
609
610         /* Download successfull, let's query the URI */
611         query = gst_query_new_uri ();
612         if (gst_element_query (urisrc, query)) {
613           gst_query_parse_uri (query, &download->uri);
614           gst_query_parse_uri_redirection (query, &download->redirect_uri);
615           gst_query_parse_uri_redirection_permanent (query,
616               &download->redirect_permanent);
617         }
618         gst_query_unref (query);
619         gst_element_set_state (urisrc, GST_STATE_READY);
620       }
621       GST_OBJECT_LOCK (downloader);
622       gst_element_set_bus (urisrc, NULL);
623
624       /* unlink the source element from the internal pad */
625       pad = gst_pad_get_peer (downloader->priv->pad);
626       if (pad) {
627         gst_pad_unlink (pad, downloader->priv->pad);
628         gst_object_unref (pad);
629       }
630     }
631     GST_OBJECT_UNLOCK (downloader);
632
633     if (download == NULL) {
634       if (!downloader->priv->err) {
635         g_set_error (err, GST_RESOURCE_ERROR, GST_RESOURCE_ERROR_OPEN_READ,
636             "Failed to download '%s'", uri);
637       } else {
638         g_propagate_error (err, downloader->priv->err);
639         downloader->priv->err = NULL;
640       }
641     }
642
643     downloader->priv->cancelled = FALSE;
644
645     g_mutex_unlock (&downloader->priv->download_lock);
646     return download;
647   }
648 }