1 #include <string.h>
2 #include "gskurltransfer.h"
3 #include "../gskmemory.h"
4
5 /**
6 * gsk_url_transfer_result_name:
7 *
8 * @result: the enumeration value.
9 *
10 * Convert a GskUrlTransferResult value
11 * into a human-readable string.
12 *
13 * returns: the constant string.
14 */
15 const char *
gsk_url_transfer_result_name(GskUrlTransferResult result)16 gsk_url_transfer_result_name (GskUrlTransferResult result)
17 {
18 switch (result)
19 {
20 case GSK_URL_TRANSFER_ERROR_BAD_REQUEST:
21 return "Error: Bad Request";
22 case GSK_URL_TRANSFER_ERROR_BAD_NAME:
23 return "Error: Bad Name";
24 case GSK_URL_TRANSFER_ERROR_NO_SERVER:
25 return "Error: No Server";
26 case GSK_URL_TRANSFER_ERROR_NOT_FOUND:
27 return "Error: Not Found";
28 case GSK_URL_TRANSFER_ERROR_SERVER_ERROR:
29 return "Error: Server Error";
30 case GSK_URL_TRANSFER_ERROR_UNSUPPORTED:
31 return "Error: Unsupported";
32 case GSK_URL_TRANSFER_ERROR_TIMED_OUT:
33 return "Error: Timed Out";
34 case GSK_URL_TRANSFER_ERROR_REDIRECT_LOOP:
35 return "Error: Redirect Loop";
36 case GSK_URL_TRANSFER_REDIRECT:
37 return "Redirect";
38 case GSK_URL_TRANSFER_CANCELLED:
39 return "Cancelled";
40 case GSK_URL_TRANSFER_SUCCESS:
41 return "Success";
42 default:
43 g_warning ("requested name of invalid transfer result %u", result);
44 g_return_val_if_reached (NULL);
45 }
46 }
47
48 G_DEFINE_TYPE(GskUrlTransfer, gsk_url_transfer, G_TYPE_OBJECT);
49
50 typedef enum
51 {
52 GSK_URL_TRANSFER_STATE_CONSTRUCTING,
53 GSK_URL_TRANSFER_STATE_STARTED,
54 GSK_URL_TRANSFER_STATE_DONE,
55 GSK_URL_TRANSFER_STATE_ERROR
56 } GskUrlTransferState;
57
58 static inline void
gsk_url_transfer_redirect_free_1(GskUrlTransferRedirect * redirect)59 gsk_url_transfer_redirect_free_1 (GskUrlTransferRedirect *redirect)
60 {
61 g_object_unref (redirect->url);
62 if (redirect->request)
63 g_object_unref (redirect->request);
64 if (redirect->response)
65 g_object_unref (redirect->response);
66 g_free (redirect);
67 }
68
69 static void
gsk_url_transfer_finalize(GObject * object)70 gsk_url_transfer_finalize (GObject *object)
71 {
72 GskUrlTransfer *transfer = GSK_URL_TRANSFER (object);
73 GskUrlTransferRedirect *redir_at;
74 g_assert (transfer->transfer_state != GSK_URL_TRANSFER_STATE_STARTED);
75
76 if (transfer->url)
77 g_object_unref (transfer->url);
78 redir_at = transfer->first_redirect;
79 while (redir_at)
80 {
81 GskUrlTransferRedirect *next = redir_at->next;
82 gsk_url_transfer_redirect_free_1 (redir_at);
83 redir_at = next;
84 }
85 if (transfer->address)
86 g_object_unref (transfer->address);
87 if (transfer->address_hint)
88 g_object_unref (transfer->address_hint);
89
90 /* may be available: protocol-specific headers */
91 if (transfer->request)
92 g_object_unref (transfer->request);
93 if (transfer->response)
94 g_object_unref (transfer->response);
95
96 if (transfer->content)
97 g_object_unref (transfer->content);
98 if (transfer->upload_destroy != NULL)
99 (*transfer->upload_destroy) (transfer->upload_data);
100
101 g_clear_error (&transfer->error);
102
103 G_OBJECT_CLASS (gsk_url_transfer_parent_class)->finalize (object);
104 }
105
106 static void
gsk_url_transfer_real_timed_out(GskUrlTransfer * transfer)107 gsk_url_transfer_real_timed_out (GskUrlTransfer *transfer)
108 {
109 gsk_url_transfer_take_error (transfer,
110 g_error_new (GSK_G_ERROR_DOMAIN,
111 GSK_ERROR_OPERATION_TIMED_OUT,
112 "Transfer with URL timed out"));
113 if (transfer->transfer_state != GSK_URL_TRANSFER_STATE_DONE)
114 gsk_url_transfer_notify_done (transfer, GSK_URL_TRANSFER_ERROR_TIMED_OUT);
115 }
116
117 static char *
gsk_url_transfer_real_get_constructing_state(GskUrlTransfer * transfer)118 gsk_url_transfer_real_get_constructing_state (GskUrlTransfer *transfer)
119 {
120 if (transfer->url)
121 {
122 char *url_str = gsk_url_to_string (transfer->url);
123 char *rv = g_strdup_printf ("NOT STARTED: %s", url_str);
124 g_free (url_str);
125 return rv;
126 }
127 return g_strdup ("NOT STARTED: (no url)");
128 }
129
130 static char *
gsk_url_transfer_real_get_running_state(GskUrlTransfer * transfer)131 gsk_url_transfer_real_get_running_state (GskUrlTransfer *transfer)
132 {
133 if (transfer->url)
134 {
135 char *url_str = gsk_url_to_string (transfer->url);
136 char *rv = g_strdup_printf ("RUNNING: %s", url_str);
137 g_free (url_str);
138 return rv;
139 }
140 return g_strdup ("RUNNING: (no url!?!)");
141 }
142
143 static char *
gsk_url_transfer_real_get_done_state(GskUrlTransfer * transfer)144 gsk_url_transfer_real_get_done_state (GskUrlTransfer *transfer)
145 {
146 if (transfer->url)
147 {
148 char *url_str = gsk_url_to_string (transfer->url);
149 char *rv = g_strdup_printf ("DONE: %s: %s", url_str,
150 gsk_url_transfer_result_name (transfer->result));
151 g_free (url_str);
152 return rv;
153 }
154 return g_strdup_printf ("DONE: [no url]: %s",
155 gsk_url_transfer_result_name (transfer->result));
156 }
157
158 static void
gsk_url_transfer_init(GskUrlTransfer * transfer)159 gsk_url_transfer_init (GskUrlTransfer *transfer)
160 {
161 transfer->transfer_state = GSK_URL_TRANSFER_STATE_CONSTRUCTING;
162 transfer->follow_redirects = 1;
163 }
164
165 static void
gsk_url_transfer_class_init(GskUrlTransferClass * transfer_class)166 gsk_url_transfer_class_init (GskUrlTransferClass *transfer_class)
167 {
168 GObjectClass *object_class = G_OBJECT_CLASS (transfer_class);
169 object_class->finalize = gsk_url_transfer_finalize;
170 transfer_class->timed_out = gsk_url_transfer_real_timed_out;
171 transfer_class->get_constructing_state = gsk_url_transfer_real_get_constructing_state;
172 transfer_class->get_running_state = gsk_url_transfer_real_get_running_state;
173 transfer_class->get_done_state = gsk_url_transfer_real_get_done_state;
174 }
175
176
177 /**
178 * gsk_url_transfer:
179 * @url: the URL to which to upload or from which to download data.
180 * @upload_func: optional function that can create the upload's content
181 * as a #GskStream.
182 * @upload_data: data which can be used by the upload function.
183 * @upload_destroy: optional function that will be notified when upload()
184 * will no longer be called (note that the streams it created may
185 * still be extant though).
186 * @handler: function to be called with the transfer request is
187 * done. (The transfer content itself is just provided as a stream
188 * though-- only after reading the stream is the transfer truly done)
189 * This function may also be called in a number of error cases.
190 * @data: data to pass to the handler function.
191 * @destroy: function to call when you are done with data.
192 * @error: place to put the error if anything goes wrong.
193 *
194 * Begin a upload and/or download with a URL.
195 * There is no way to cancel this transfer.
196 *
197 * If you wish to perform an upload,
198 * provide a function that can create the stream of content to
199 * upload on demand. Note that the upload_destroy() method
200 * is called only once the transfer is done and all the upload streams
201 * are finalized. Therefore, you can assume that the upload_data
202 * will be available for all your upload-streams.
203 *
204 * The handler/data/destroy triple is used for result notification.
205 * handler() is always invoked exactly once. To find out how things
206 * went, the handler() should almost always start by
207 * examining transfer->result.
208 *
209 * returns: whether the transfer began.
210 * Unsupported URL schemes and malformed URLs are the
211 * most common ways for this function to fail.
212 */
213 gboolean
gsk_url_transfer(GskUrl * url,GskUrlUploadFunc upload_func,gpointer upload_data,GDestroyNotify upload_destroy,GskUrlTransferFunc handler,gpointer data,GDestroyNotify destroy,GError ** error)214 gsk_url_transfer (GskUrl *url,
215 GskUrlUploadFunc upload_func,
216 gpointer upload_data,
217 GDestroyNotify upload_destroy,
218 GskUrlTransferFunc handler,
219 gpointer data,
220 GDestroyNotify destroy,
221 GError **error)
222 {
223 GskUrlTransfer *transfer = gsk_url_transfer_new (url);
224 if (transfer == NULL)
225 {
226 g_set_error (error,
227 GSK_G_ERROR_DOMAIN,
228 GSK_ERROR_INVALID_ARGUMENT,
229 "could not create Transfer object for url of scheme %s", url->scheme_name);
230 return FALSE;
231 }
232 gsk_url_transfer_set_handler (transfer, handler, data, destroy);
233 if (upload_func != NULL)
234 gsk_url_transfer_set_upload (transfer, upload_func, upload_data, upload_destroy);
235 if (!gsk_url_transfer_start (transfer, error))
236 return FALSE;
237 g_object_unref (transfer);
238 return TRUE;
239 }
240
241 static gboolean
handle_timeout(gpointer data)242 handle_timeout (gpointer data)
243 {
244 GskUrlTransfer *transfer = GSK_URL_TRANSFER (data);
245 GskUrlTransferClass *class = GSK_URL_TRANSFER_GET_CLASS (transfer);
246 g_return_val_if_fail (transfer->transfer_state == GSK_URL_TRANSFER_STATE_STARTED, FALSE);
247 transfer->timeout_source = NULL;
248 transfer->timed_out = TRUE;
249
250 /* hold a reference to transfer temporarily to ensure
251 that the transfer doesn't die in the middle */
252 g_object_ref (transfer);
253 class->timed_out (transfer);
254 g_object_unref (transfer); /* transfer may now be dead */
255 return FALSE;
256 }
257
258 /**
259 * gsk_url_transfer_start:
260 * @transfer: the Transfer to affect.
261 * @error: place to put the error if anything goes wrong.
262 *
263 * Begin the upload and/or download. (Maybe start with name-lookup).
264 *
265 * returns: whether the transfer started successfully.
266 * If it returns TRUE, you are guaranteed to receive your
267 * done-notification. If is returns FALSE, you will definitely not
268 * receive done-notification.
269 */
270 gboolean
gsk_url_transfer_start(GskUrlTransfer * transfer,GError ** error)271 gsk_url_transfer_start (GskUrlTransfer *transfer,
272 GError **error)
273 {
274 GskUrlTransferClass *class = GSK_URL_TRANSFER_GET_CLASS (transfer);
275 g_assert (class->start != NULL);
276 g_return_val_if_fail (transfer->transfer_state == GSK_URL_TRANSFER_STATE_CONSTRUCTING, FALSE);
277 g_object_ref (transfer);
278 transfer->transfer_state = GSK_URL_TRANSFER_STATE_STARTED;
279 if (!class->start (transfer, error))
280 {
281 transfer->transfer_state = GSK_URL_TRANSFER_STATE_ERROR;
282 g_object_unref (transfer);
283 return FALSE;
284 }
285 if (transfer->has_timeout
286 && transfer->transfer_state == GSK_URL_TRANSFER_STATE_STARTED)
287 {
288 transfer->timeout_source = gsk_main_loop_add_timer (gsk_main_loop_default (),
289 handle_timeout,
290 transfer,
291 NULL,
292 transfer->timeout_ms,
293 -1);
294 }
295 return TRUE;
296 }
297
298 /**
299 * gsk_url_transfer_set_handler:
300 * @transfer: the Transfer to affect.
301 * @handler: function to be called with the transfer request is
302 * done. (The transfer content itself is just provided as a stream
303 * though-- only after reading the stream is the transfer truly done)
304 * This function may also be called in a number of error cases.
305 * @data: data to pass to the handler function.
306 * @destroy: function to call when you are done with data.
307 *
308 * The handler/data/destroy triple is used for result notification.
309 * handler() is always invoked exactly once. To find out how things
310 * went, the handler() should almost always start by
311 * examining transfer->result.
312 */
313 void
gsk_url_transfer_set_handler(GskUrlTransfer * transfer,GskUrlTransferFunc handler,gpointer data,GDestroyNotify destroy)314 gsk_url_transfer_set_handler(GskUrlTransfer *transfer,
315 GskUrlTransferFunc handler,
316 gpointer data,
317 GDestroyNotify destroy)
318 {
319 g_return_if_fail (transfer->transfer_state == GSK_URL_TRANSFER_STATE_CONSTRUCTING);
320 g_return_if_fail (transfer->handler == NULL);
321 transfer->handler = handler;
322 transfer->handler_data = data;
323 transfer->handler_data_destroy = destroy;
324 }
325
326 /**
327 * gsk_url_transfer_set_url:
328 * @transfer: the Transfer to affect.
329 * @url: the URL to which to upload or from which to download data.
330 *
331 * Set the URL that is the target of this transfer.
332 * This can only be done once, before the transfer
333 * is started.
334 *
335 * You seldom need to use this function, as it
336 * is called by gsk_url_transfer_new().
337 */
338 void
gsk_url_transfer_set_url(GskUrlTransfer * transfer,GskUrl * url)339 gsk_url_transfer_set_url (GskUrlTransfer *transfer,
340 GskUrl *url)
341 {
342 g_return_if_fail (transfer->transfer_state == GSK_URL_TRANSFER_STATE_CONSTRUCTING);
343 g_return_if_fail (transfer->url == NULL);
344 g_return_if_fail (GSK_IS_URL (url));
345 transfer->url = g_object_ref (url);
346 }
347
348 /**
349 * gsk_url_transfer_set_timeout:
350 * @transfer: the Transfer to affect.
351 * @millis: milliseconds to wait before aborting the transfer.
352 *
353 * Set the timeout on the download.
354 *
355 * This can be used to avoid hanging on slow servers.
356 *
357 * This must be called before the transfer is started
358 * (with gsk_url_transfer_start).
359 */
360 void
gsk_url_transfer_set_timeout(GskUrlTransfer * transfer,guint millis)361 gsk_url_transfer_set_timeout(GskUrlTransfer *transfer,
362 guint millis)
363 {
364 g_return_if_fail (transfer->transfer_state == GSK_URL_TRANSFER_STATE_CONSTRUCTING);
365 transfer->has_timeout = 1;
366 transfer->timeout_ms = millis;
367 }
368
369 /**
370 * gsk_url_transfer_clear_timeout:
371 * @transfer: the Transfer to affect.
372 *
373 * Clear the timeout on the download.
374 *
375 * This must be called before the transfer is started
376 * (with gsk_url_transfer_start).
377 */
378 void
gsk_url_transfer_clear_timeout(GskUrlTransfer * transfer)379 gsk_url_transfer_clear_timeout(GskUrlTransfer *transfer)
380 {
381 g_return_if_fail (transfer->transfer_state == GSK_URL_TRANSFER_STATE_CONSTRUCTING);
382 transfer->has_timeout = 0;
383 }
384
385 /**
386 * gsk_url_transfer_set_follow_redirects:
387 * @transfer: the Transfer to affect.
388 * @follow_redirs: whether to follow redirect responses.
389 *
390 * Configure how the transfer will behave when it encounters
391 * redirection responses.
392 *
393 * The default behavior is to follow redirects,
394 * adding them to the list of redirects, but not notifying the
395 * user until we reach a real page (or error).
396 *
397 * If follow_redirects is FALSE, then we are done
398 * even if the download led to a redirect.
399 */
400 void
gsk_url_transfer_set_follow_redirects(GskUrlTransfer * transfer,gboolean follow_redirs)401 gsk_url_transfer_set_follow_redirects(GskUrlTransfer *transfer,
402 gboolean follow_redirs)
403 {
404 g_return_if_fail (transfer->transfer_state == GSK_URL_TRANSFER_STATE_CONSTRUCTING);
405 transfer->follow_redirects = follow_redirs ? 1 : 0;
406 }
407
408
409 /**
410 * gsk_url_transfer_set_address_hint:
411 * @transfer: the Transfer to affect.
412 * @address: the socket-address to use for connecting,
413 * possibly with the wrong port (the port will be overridden by
414 * the URL's port).
415 *
416 * To avoid DNS lookups in very bulky transfer situations,
417 * DNS may be bypassed and replaced with this address.
418 *
419 * Chances are, you want to suppress redirects to:
420 * otherwise, DNS may be used on the redirected URLs.
421 */
422 void
gsk_url_transfer_set_address_hint(GskUrlTransfer * transfer,GskSocketAddress * address)423 gsk_url_transfer_set_address_hint (GskUrlTransfer *transfer,
424 GskSocketAddress *address)
425 {
426 g_return_if_fail (transfer->transfer_state == GSK_URL_TRANSFER_STATE_CONSTRUCTING);
427 g_return_if_fail (transfer->address_hint == NULL);
428 transfer->address_hint = g_object_ref (address);
429 }
430
431
432 /**
433 * gsk_url_transfer_set_upload:
434 * @transfer: the Transfer to affect.
435 * @func: function that can create the upload's content
436 * as a #GskStream.
437 * @data: data which can be used by the upload function.
438 * @destroy: optional function that will be notified when upload()
439 * will no longer be called (note that the streams it created may
440 * still be extant though).
441 *
442 * Set the upload stream as generally as possible.
443 * Actually you must provide a function
444 * that can make an upload stream on demand--
445 * this is necessary to get redirects right.
446 *
447 * The destroy() function will be called after no more upload-streams
448 * need to be created-- it is quite possible that not all upload-streams
449 * have been finalized by the time the destroy() is invoked.
450 *
451 * If you don't care about redirects, you can
452 * use gsk_url_transfer_set_oneshot_upload().
453 *
454 * If you have a slab of memory that you want to use as the upload stream,
455 * consider using gsk_url_transfer_set_upload_packet().
456 */
457 void
gsk_url_transfer_set_upload(GskUrlTransfer * transfer,GskUrlUploadFunc func,gpointer data,GDestroyNotify destroy)458 gsk_url_transfer_set_upload (GskUrlTransfer *transfer,
459 GskUrlUploadFunc func,
460 gpointer data,
461 GDestroyNotify destroy)
462 {
463 g_return_if_fail (transfer->transfer_state == GSK_URL_TRANSFER_STATE_CONSTRUCTING);
464 g_return_if_fail (transfer->upload_func == NULL);
465 g_return_if_fail (func != NULL);
466 transfer->upload_func = func;
467 transfer->upload_data = data;
468 transfer->upload_destroy = destroy;
469 }
470
471 /**
472 * gsk_url_transfer_set_upload_packet:
473 * @transfer: the Transfer to affect.
474 * @packet: the GskPacket containing the upload content as data.
475 *
476 * Set the upload stream in an easy, reliable way using a GskPacket.
477 */
478 static GskStream *
make_packet_into_stream(gpointer data,gssize * size_out,GError ** error)479 make_packet_into_stream (gpointer data,
480 gssize *size_out,
481 GError **error)
482 {
483 GskPacket *packet = data;
484 GskStream *rv = gsk_memory_slab_source_new (packet->data, packet->len,
485 (GDestroyNotify) gsk_packet_unref,
486 gsk_packet_ref (packet));
487 *size_out = packet->len;
488 return rv;
489 }
490
491 void
gsk_url_transfer_set_upload_packet(GskUrlTransfer * transfer,GskPacket * packet)492 gsk_url_transfer_set_upload_packet (GskUrlTransfer *transfer,
493 GskPacket *packet)
494 {
495 gsk_url_transfer_set_upload (transfer,
496 make_packet_into_stream,
497 gsk_packet_ref (packet),
498 (GDestroyNotify) gsk_packet_unref);
499 }
500
501 /**
502 * gsk_url_transfer_set_oneshot_upload:
503 * @transfer: the Transfer to affect.
504 * @size: the length of the stream in bytes, or -1 if you don't know.
505 * @stream: the upload content stream.
506 *
507 * Set the content to upload to the remote URL,
508 * as a #GskStream.
509 *
510 * Since streams can only be read once,
511 * this method only works on URLs that do not require
512 * redirection.
513 */
514 typedef struct
515 {
516 GskStream *stream;
517 gssize size;
518 } ReturnStreamOnce;
519
520 static GskStream *
return_stream_once(gpointer data,gssize * size_out,GError ** error)521 return_stream_once (gpointer data,
522 gssize *size_out,
523 GError **error)
524 {
525 ReturnStreamOnce *once = data;
526 GskStream *rv;
527 if (once->stream == NULL)
528 {
529 g_set_error (error,
530 GSK_G_ERROR_DOMAIN,
531 GSK_ERROR_TOO_MANY_LINKS,
532 "one-shot upload transfer was redirected: cannot re-upload data");
533 return NULL;
534 }
535 rv = once->stream;
536 once->stream = NULL;
537 *size_out = once->size;
538 return rv;
539 }
540 static void
destroy_return_stream_once(gpointer data)541 destroy_return_stream_once (gpointer data)
542 {
543 ReturnStreamOnce *once = data;
544 if (once->stream)
545 g_object_unref (once->stream);
546 g_free (once);
547 }
548
549 void
gsk_url_transfer_set_oneshot_upload(GskUrlTransfer * transfer,GskStream * stream,gssize size)550 gsk_url_transfer_set_oneshot_upload (GskUrlTransfer *transfer,
551 GskStream *stream,
552 gssize size)
553 {
554 ReturnStreamOnce *once;
555 g_return_if_fail (GSK_IS_STREAM (stream));
556 once = g_new (ReturnStreamOnce, 1);
557 once->stream = g_object_ref (stream);
558 once->size = size;
559 gsk_url_transfer_set_upload (transfer,
560 return_stream_once,
561 once,
562 destroy_return_stream_once);
563 }
564
565 /**
566 * gsk_url_transfer_cancel:
567 * @transfer: the Transfer to affect.
568 *
569 * Abort a running transfer.
570 *
571 * If you registered a handler, it will be called with
572 * result GSK_URL_TRANSFER_CANCELLED.
573 */
574 void
gsk_url_transfer_cancel(GskUrlTransfer * transfer)575 gsk_url_transfer_cancel (GskUrlTransfer *transfer)
576 {
577 GskUrlTransferClass *class = GSK_URL_TRANSFER_GET_CLASS (transfer);
578 g_return_if_fail (transfer->transfer_state == GSK_URL_TRANSFER_STATE_STARTED);
579 if (class->cancel == NULL)
580 {
581 g_warning ("%s does not implement cancel()!", G_OBJECT_CLASS_NAME (class));
582 return;
583 }
584 class->cancel (transfer);
585 }
586
587 /* --- Protected API --- */
588 /**
589 * gsk_url_transfer_has_upload:
590 * @transfer: the Transfer to query.
591 *
592 * Figure out whether this transfer has upload data.
593 *
594 * This function should only be needed by implementors
595 * of types of GskUrlTransfer.
596 *
597 * returns: whether the transfer has upload data.
598 */
599 gboolean
gsk_url_transfer_has_upload(GskUrlTransfer * transfer)600 gsk_url_transfer_has_upload (GskUrlTransfer *transfer)
601 {
602 return transfer->upload_func != NULL;
603 }
604
605 /**
606 * gsk_url_transfer_create_upload:
607 * @transfer: the Transfer to use.
608 * @size_out: the size of the stream in bytes, or -1 if the size is unknown.
609 * @error: optional location to store the #GError if there is a problem.
610 *
611 * Create a upload stream for this transfer based on the user's creator
612 * function.
613 *
614 * This function should only be needed by implementors
615 * of types of GskUrlTransfer.
616 *
617 * returns: a newly allocated #GskStream, or NULL if an error occurs.
618 */
619 GskStream *
gsk_url_transfer_create_upload(GskUrlTransfer * transfer,gssize * size_out,GError ** error)620 gsk_url_transfer_create_upload (GskUrlTransfer *transfer,
621 gssize *size_out,
622 GError **error)
623 {
624 g_return_val_if_fail (transfer->upload_func != NULL, NULL);
625 *size_out = -1;
626 return transfer->upload_func (transfer->upload_data, size_out, error);
627 }
628
629 /**
630 * gsk_url_transfer_peek_expects_download_stream:
631 * @transfer: the Transfer to use.
632 * returns: whether this transfer has a download handler.
633 *
634 * This function can be used to see if download-content is expected.
635 *
636 * This function should only be needed by implementors
637 * of types of GskUrlTransfer.
638 */
639 gboolean
gsk_url_transfer_peek_expects_download_stream(GskUrlTransfer * transfer)640 gsk_url_transfer_peek_expects_download_stream (GskUrlTransfer *transfer)
641 {
642 g_return_val_if_fail (transfer->transfer_state == GSK_URL_TRANSFER_STATE_STARTED, FALSE);
643 return transfer->handler != NULL;
644 }
645
646 /**
647 * gsk_url_transfer_set_address:
648 * @transfer: the Transfer to affect.
649 * @addr: the address of the host whose lookup was completed.
650 *
651 * Set the socket-address for informational purposes.
652 * This is occasionally interesting to the user of the Transfer.
653 *
654 * This function should only be needed by implementors
655 * of types of GskUrlTransfer.
656 */
657 void
gsk_url_transfer_set_address(GskUrlTransfer * transfer,GskSocketAddress * addr)658 gsk_url_transfer_set_address (GskUrlTransfer *transfer,
659 GskSocketAddress *addr)
660 {
661 g_object_ref (addr);
662 if (transfer->address)
663 g_object_unref (transfer->address);
664 transfer->address = addr;
665 }
666
667 static inline gboolean
strings_equal(const char * a,const char * b)668 strings_equal (const char *a, const char *b)
669 {
670 if (a == NULL)
671 return b == NULL;
672 else if (b == NULL)
673 return FALSE;
674 else
675 return strcmp (a, b) == 0;
676 }
677
678 static gboolean
urls_equal_up_to_fragment(const GskUrl * a,const GskUrl * b)679 urls_equal_up_to_fragment (const GskUrl *a,
680 const GskUrl *b)
681 {
682 return a->scheme == b->scheme
683 && strings_equal (a->host, b->host)
684 && strings_equal (a->password, b->password)
685 && gsk_url_get_port (a) == gsk_url_get_port (b)
686 && strings_equal (a->user_name, b->user_name)
687 && strings_equal (a->path, b->path)
688 && strings_equal (a->query, b->query);
689 }
690
691 /**
692 * gsk_url_transfer_add_redirect:
693 * @transfer: the Transfer to affect.
694 * @request: request object for this segment of the transfer.
695 * @response: response object for this segment of the transfer.
696 * @is_permanent: whether the content is permanently relocated to this address.
697 * @dest_url: the URL to which we have been redirected.
698 *
699 * Add an entry to the list of redirects
700 * that we have encountered while trying to
701 * service this request.
702 *
703 * Most users of GskUrlTransfer won't care about these redirects--
704 * they are provided to the rare client that cares about the redirect-path.
705 * More commonly, users merely wish to suppress redirect handling: that can be done
706 * more easily by gsk_url_transfer_set_follow_redirects().
707 *
708 * This function should only be needed by implementors
709 * of types of GskUrlTransfer.
710 *
711 * returns: whether the redirect was allowed (it is disallowed if
712 * it is a circular redirect. In that case, we will set 'transfer->error',
713 * and call gsk_url_transfer_notify_done().
714 */
715 gboolean
gsk_url_transfer_add_redirect(GskUrlTransfer * transfer,GObject * request,GObject * response,gboolean is_permanent,GskUrl * dest_url)716 gsk_url_transfer_add_redirect (GskUrlTransfer *transfer,
717 GObject *request,
718 GObject *response,
719 gboolean is_permanent,
720 GskUrl *dest_url)
721 {
722 GskUrlTransferRedirect *redirect;
723 g_return_val_if_fail (transfer->transfer_state == GSK_URL_TRANSFER_STATE_STARTED, TRUE);
724 g_return_val_if_fail (GSK_IS_URL (dest_url), TRUE);
725
726 /* Detect circular references. */
727 if (urls_equal_up_to_fragment (dest_url, transfer->url))
728 goto circular_redirect;
729 for (redirect = transfer->first_redirect; redirect != NULL; redirect = redirect->next)
730 if (urls_equal_up_to_fragment (redirect->url, dest_url))
731 goto circular_redirect;
732
733 redirect = g_new (GskUrlTransferRedirect, 1);
734 redirect->is_permanent = is_permanent;
735 redirect->url = g_object_ref (dest_url);
736 redirect->request = request ? g_object_ref (request) : transfer->request ? g_object_ref (transfer->request) : NULL;
737 redirect->response = response ? g_object_ref (response) : NULL;
738 redirect->next = NULL;
739
740 if (transfer->first_redirect == NULL)
741 transfer->first_redirect = redirect;
742 else
743 transfer->last_redirect->next = redirect;
744 transfer->last_redirect = redirect;
745
746 transfer->redirect_is_permanent = is_permanent;
747 transfer->redirect_url = dest_url;
748 return TRUE;
749
750
751 circular_redirect:
752 gsk_url_transfer_take_error (transfer,
753 g_error_new (GSK_G_ERROR_DOMAIN,
754 GSK_ERROR_CIRCULAR,
755 "circular redirects encountered"));
756 gsk_url_transfer_notify_done (transfer, GSK_URL_TRANSFER_ERROR_REDIRECT_LOOP);
757 return FALSE;
758 }
759
760 /**
761 * gsk_url_transfer_set_download:
762 * @transfer: the Transfer to affect.
763 * @content: the content-stream for the downloaded data.
764 *
765 * Set the incoming-content that is associated with this transfer.
766 * This will used by the user of the Transfer.
767 *
768 * This function should only be needed by implementors
769 * of types of GskUrlTransfer.
770 */
771 void
gsk_url_transfer_set_download(GskUrlTransfer * transfer,GskStream * content)772 gsk_url_transfer_set_download (GskUrlTransfer *transfer,
773 GskStream *content)
774 {
775 g_return_if_fail (transfer->transfer_state == GSK_URL_TRANSFER_STATE_STARTED);
776 g_return_if_fail (transfer->content == NULL);
777 g_return_if_fail (GSK_IS_STREAM (content));
778 transfer->content = g_object_ref (content);
779 }
780
781 /**
782 * gsk_url_transfer_set_request:
783 * @transfer: the Transfer to affect.
784 * @request: the request object to store in the transfer information.
785 *
786 * Set the outgoing-request header data for this transaction.
787 *
788 * This function should only be needed by implementors
789 * of types of GskUrlTransfer.
790 */
791 void
gsk_url_transfer_set_request(GskUrlTransfer * transfer,GObject * request)792 gsk_url_transfer_set_request (GskUrlTransfer *transfer,
793 GObject *request)
794 {
795 GObject *old_request = transfer->request;
796 g_return_if_fail (transfer->transfer_state == GSK_URL_TRANSFER_STATE_STARTED);
797 g_return_if_fail (G_IS_OBJECT (request));
798 transfer->request = g_object_ref (request);
799 if (old_request)
800 g_object_unref (old_request);
801 }
802
803 /**
804 * gsk_url_transfer_set_response:
805 * @transfer: the Transfer to affect.
806 * @response: the response object to store in the transfer information.
807 *
808 * Set the incoming-response header data for this transaction.
809 *
810 * This function should only be needed by implementors
811 * of types of GskUrlTransfer.
812 */
813 void
gsk_url_transfer_set_response(GskUrlTransfer * transfer,GObject * response)814 gsk_url_transfer_set_response (GskUrlTransfer *transfer,
815 GObject *response)
816 {
817 GObject *old_response = transfer->response;
818 g_return_if_fail (transfer->transfer_state == GSK_URL_TRANSFER_STATE_STARTED);
819 g_return_if_fail (transfer->response == NULL);
820 transfer->response = g_object_ref (response);
821 if (old_response)
822 g_object_unref (old_response);
823 }
824
825 /**
826 * gsk_url_transfer_set_error:
827 * @transfer: the Transfer to affect.
828 * @error: the error to associate with the transfer.
829 *
830 * Set the error field for this transaction.
831 * A copy of the error parameter is made.
832 *
833 * This function should only be needed by implementors
834 * of types of GskUrlTransfer.
835 */
836 void
gsk_url_transfer_set_error(GskUrlTransfer * transfer,const GError * error)837 gsk_url_transfer_set_error (GskUrlTransfer *transfer,
838 const GError *error)
839 {
840 GError *copy = g_error_copy (error);
841 g_return_if_fail (error != NULL);
842 if (transfer->error)
843 g_error_free (transfer->error);
844 transfer->error = copy;
845 }
846
847 /**
848 * gsk_url_transfer_take_error:
849 * @transfer: the Transfer to affect.
850 * @error: the error to associate with the transfer.
851 *
852 * Set the error field for this transaction.
853 * The error parameter will be freed eventually by the
854 * #GskUrlTransfer.
855 *
856 * This function should only be needed by implementors
857 * of types of GskUrlTransfer.
858 */
859 void
gsk_url_transfer_take_error(GskUrlTransfer * transfer,GError * error)860 gsk_url_transfer_take_error (GskUrlTransfer *transfer,
861 GError *error)
862 {
863 g_return_if_fail (error != NULL);
864 if (error == transfer->error)
865 return;
866 if (transfer->error)
867 g_error_free (transfer->error);
868 transfer->error = error;
869 }
870
871 /**
872 * gsk_url_transfer_is_done:
873 * @transfer: the Transfer to query.
874 *
875 * Find out whether the transfer is done.
876 * The transfer is done iff the callback has been invoked.
877 *
878 * returns: whether the function is done.
879 */
880 gboolean
gsk_url_transfer_is_done(GskUrlTransfer * transfer)881 gsk_url_transfer_is_done (GskUrlTransfer *transfer)
882 {
883 return (transfer->transfer_state == GSK_URL_TRANSFER_STATE_DONE);
884 }
885
886 /**
887 * gsk_url_transfer_notify_done:
888 * @transfer: the Transfer to affect.
889 * @result: the transfer's result status code.
890 *
891 * Transition the transfer to the DONE state,
892 * and invoke the user's callback (if any).
893 * This function may only be invoked once per transfer.
894 *
895 * This function should only be needed by implementors
896 * of types of GskUrlTransfer.
897 */
898 void
gsk_url_transfer_notify_done(GskUrlTransfer * transfer,GskUrlTransferResult result)899 gsk_url_transfer_notify_done (GskUrlTransfer *transfer,
900 GskUrlTransferResult result)
901 {
902 g_assert (transfer->transfer_state == GSK_URL_TRANSFER_STATE_STARTED);
903 transfer->transfer_state = GSK_URL_TRANSFER_STATE_DONE;
904 transfer->result = result;
905
906 if (transfer->timeout_source)
907 {
908 GskSource *timeout = transfer->timeout_source;
909 transfer->timeout_source = NULL;
910 gsk_source_remove (timeout);
911 }
912
913 if (transfer->handler != NULL)
914 transfer->handler (transfer, transfer->handler_data);
915
916 /* we must relinquish these, or else there will circular ref-count leaks
917 if the user tries to do tricks like the above commented code. */
918 if (transfer->content != NULL)
919 {
920 GskStream *tmp = transfer->content;
921 transfer->content = NULL;
922 g_object_unref (tmp);
923 }
924 if (transfer->upload_func != NULL)
925 {
926 gpointer data = transfer->upload_data;
927 GDestroyNotify destroy = transfer->upload_destroy;
928 transfer->upload_func = NULL;
929 transfer->upload_data = NULL;
930 transfer->upload_destroy = NULL;
931 if (destroy)
932 destroy (data);
933 }
934
935 if (transfer->handler_data_destroy)
936 transfer->handler_data_destroy (transfer->handler_data);
937
938 transfer->handler = NULL;
939 transfer->handler_data_destroy = NULL;
940
941 g_object_unref (transfer);
942 }
943
944 /* Registering a transfer type */
945 static GHashTable *scheme_to_slist_of_classes = NULL;
946 /**
947 * gsk_url_transfer_class_register:
948 * @scheme: the URL scheme that this class of transfer can handle.
949 * @transfer_class: the class that can handle the URL type.
950 *
951 * Register a class of URL transfer that can
952 * handle a given scheme.
953 * It will only be instantiated if the class' test method
954 * returns TRUE, to indicate that it can handle the specific URL.
955 */
956 void
gsk_url_transfer_class_register(GskUrlScheme scheme,GskUrlTransferClass * transfer_class)957 gsk_url_transfer_class_register (GskUrlScheme scheme,
958 GskUrlTransferClass *transfer_class)
959 {
960 GSList *list;
961 if (scheme_to_slist_of_classes == NULL)
962 scheme_to_slist_of_classes = g_hash_table_new (NULL, NULL);
963 list = g_hash_table_lookup (scheme_to_slist_of_classes, GUINT_TO_POINTER (scheme));
964 if (list == NULL)
965 {
966 list = g_slist_prepend (NULL, transfer_class);
967 g_hash_table_insert (scheme_to_slist_of_classes, GUINT_TO_POINTER (scheme), list);
968 }
969 else
970 list = g_slist_append (list, transfer_class);
971 }
972
973 /**
974 * gsk_url_transfer_new:
975 * @url: the URL to create a transfer object for.
976 *
977 * Create a URL transfer of the appropriate type for the given URL.
978 * We try the registered classes, in order.
979 *
980 * returns: a newly allocated Transfer object, or NULL if no transfer-class
981 * could handle the URL.
982 */
983 GskUrlTransfer *
gsk_url_transfer_new(GskUrl * url)984 gsk_url_transfer_new (GskUrl *url)
985 {
986 GSList *list = g_hash_table_lookup (scheme_to_slist_of_classes, GUINT_TO_POINTER (url->scheme));
987 while (list != NULL)
988 {
989 GskUrlTransferClass *class = GSK_URL_TRANSFER_CLASS (list->data);
990 if (class->test == NULL || class->test (class, url))
991 {
992 GskUrlTransfer *transfer = g_object_new (G_OBJECT_CLASS_TYPE (class), NULL);
993 gsk_url_transfer_set_url (transfer, url);
994 return transfer;
995 }
996 list = list->next;
997 }
998 return NULL;
999 }
1000
1001 /**
1002 * gsk_url_transfer_get_state_string:
1003 * @transfer: the transfer to describe.
1004 *
1005 * Get a newly allocated, human-readable description
1006 * of the state of the transfer.
1007 *
1008 * returns: the newly-allocated string.
1009 */
1010 char *
gsk_url_transfer_get_state_string(GskUrlTransfer * transfer)1011 gsk_url_transfer_get_state_string (GskUrlTransfer *transfer)
1012 {
1013 GskUrlTransferClass *class = GSK_URL_TRANSFER_GET_CLASS (transfer);
1014 switch (transfer->transfer_state)
1015 {
1016 case GSK_URL_TRANSFER_STATE_CONSTRUCTING:
1017 return class->get_constructing_state (transfer);
1018 case GSK_URL_TRANSFER_STATE_STARTED:
1019 return class->get_running_state (transfer);
1020 case GSK_URL_TRANSFER_STATE_DONE:
1021 return class->get_done_state (transfer);
1022 default:
1023 return g_strdup ("gsk_url_transfer_get_state_string: INVALID state");
1024 }
1025 }
1026
1027
1028 /* --- convert a transfer to a stream --- */
1029 GType gsk_url_transfer_stream_get_type(void) G_GNUC_CONST;
1030 #define GSK_TYPE_URL_TRANSFER_STREAM (gsk_url_transfer_stream_get_type ())
1031 #define GSK_URL_TRANSFER_STREAM(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GSK_TYPE_URL_TRANSFER_STREAM, GskUrlTransferStream))
1032 #define GSK_URL_TRANSFER_STREAM_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GSK_TYPE_URL_TRANSFER_STREAM, GskUrlTransferStreamClass))
1033 #define GSK_URL_TRANSFER_STREAM_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GSK_TYPE_URL_TRANSFER_STREAM, GskUrlTransferStreamClass))
1034 #define GSK_IS_URL_TRANSFER_STREAM(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GSK_TYPE_URL_TRANSFER_STREAM))
1035 #define GSK_IS_URL_TRANSFER_STREAM_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GSK_TYPE_URL_TRANSFER_STREAM))
1036 typedef struct _UfUrlTransferStreamClass GskUrlTransferStreamClass;
1037 typedef struct _UfUrlTransferStream GskUrlTransferStream;
1038 struct _UfUrlTransferStreamClass
1039 {
1040 GskStreamClass base_class;
1041 };
1042 struct _UfUrlTransferStream
1043 {
1044 GskStream base_instance;
1045 GskUrlTransfer *transfer;
1046 GskStream *substream;
1047 };
1048
1049 G_DEFINE_TYPE(GskUrlTransferStream, gsk_url_transfer_stream, GSK_TYPE_STREAM);
1050
1051 static void
gsk_url_transfer_stream_finalize(GObject * object)1052 gsk_url_transfer_stream_finalize (GObject *object)
1053 {
1054 GskUrlTransferStream *transfer_stream = GSK_URL_TRANSFER_STREAM (object);
1055 g_assert (transfer_stream->transfer == NULL);
1056 if (transfer_stream->substream)
1057 {
1058 gsk_io_untrap_readable (transfer_stream->substream);
1059 g_object_unref (transfer_stream->substream);
1060 }
1061 G_OBJECT_CLASS (gsk_url_transfer_stream_parent_class)->finalize (object);
1062 }
1063 static gboolean
handle_substream_is_readable(GskIO * io,gpointer data)1064 handle_substream_is_readable (GskIO *io, gpointer data)
1065 {
1066 gsk_io_notify_ready_to_read (data);
1067 return TRUE;
1068 }
1069 static gboolean
handle_substream_read_shutdown(GskIO * io,gpointer data)1070 handle_substream_read_shutdown (GskIO *io, gpointer data)
1071 {
1072 GskUrlTransferStream *transfer_stream = GSK_URL_TRANSFER_STREAM (data);
1073 gsk_io_notify_read_shutdown (GSK_IO (transfer_stream));
1074 if (transfer_stream->substream)
1075 {
1076 gsk_io_untrap_readable (transfer_stream->substream);
1077 g_object_unref (transfer_stream->substream);
1078 transfer_stream->substream = NULL;
1079 }
1080 return FALSE;
1081 }
1082
1083 static void
gsk_url_transfer_stream_set_poll_read(GskIO * io,gboolean do_poll)1084 gsk_url_transfer_stream_set_poll_read (GskIO *io,
1085 gboolean do_poll)
1086 {
1087 GskUrlTransferStream *transfer_stream = GSK_URL_TRANSFER_STREAM (io);
1088 if (transfer_stream->substream == NULL)
1089 return;
1090 if (do_poll)
1091 gsk_io_trap_readable (transfer_stream->substream,
1092 handle_substream_is_readable,
1093 handle_substream_read_shutdown,
1094 transfer_stream,
1095 NULL);
1096 else
1097 gsk_io_untrap_readable (transfer_stream->substream);
1098 }
1099
1100 static gboolean
gsk_url_transfer_stream_shutdown_read(GskIO * io,GError ** error)1101 gsk_url_transfer_stream_shutdown_read (GskIO *io,
1102 GError **error)
1103 {
1104 GskUrlTransferStream *transfer_stream = GSK_URL_TRANSFER_STREAM (io);
1105 if (transfer_stream->transfer != NULL)
1106 gsk_url_transfer_cancel (transfer_stream->transfer);
1107 if (transfer_stream->substream != NULL)
1108 gsk_io_read_shutdown (GSK_IO (transfer_stream->substream), NULL);
1109 return TRUE;
1110 }
1111
1112 static guint
gsk_url_transfer_stream_raw_read(GskStream * stream,gpointer data,guint length,GError ** error)1113 gsk_url_transfer_stream_raw_read (GskStream *stream,
1114 gpointer data,
1115 guint length,
1116 GError **error)
1117 {
1118 GskUrlTransferStream *transfer_stream = GSK_URL_TRANSFER_STREAM (stream);
1119 if (transfer_stream->substream == NULL)
1120 return 0;
1121 return gsk_stream_read (transfer_stream->substream, data, length, error);
1122 }
1123
1124 static guint
gsk_url_transfer_stream_raw_read_buffer(GskStream * stream,GskBuffer * buffer,GError ** error)1125 gsk_url_transfer_stream_raw_read_buffer (GskStream *stream,
1126 GskBuffer *buffer,
1127 GError **error)
1128 {
1129 GskUrlTransferStream *transfer_stream = GSK_URL_TRANSFER_STREAM (stream);
1130 if (transfer_stream->substream == NULL)
1131 return 0;
1132 return gsk_stream_read_buffer (transfer_stream->substream, buffer, error);
1133 }
1134
1135 static void
gsk_url_transfer_stream_class_init(GskUrlTransferStreamClass * class)1136 gsk_url_transfer_stream_class_init (GskUrlTransferStreamClass *class)
1137 {
1138 GskIOClass *io_class = GSK_IO_CLASS (class);
1139 GskStreamClass *stream_class = GSK_STREAM_CLASS (class);
1140 GObjectClass *object_class = G_OBJECT_CLASS (class);
1141 stream_class->raw_read = gsk_url_transfer_stream_raw_read;
1142 stream_class->raw_read_buffer = gsk_url_transfer_stream_raw_read_buffer;
1143 io_class->set_poll_read = gsk_url_transfer_stream_set_poll_read;
1144 io_class->shutdown_read = gsk_url_transfer_stream_shutdown_read;
1145 object_class->finalize = gsk_url_transfer_stream_finalize;
1146 }
1147
1148 static void
gsk_url_transfer_stream_init(GskUrlTransferStream * transfer_stream)1149 gsk_url_transfer_stream_init (GskUrlTransferStream *transfer_stream)
1150 {
1151 gsk_stream_mark_is_readable (transfer_stream);
1152 }
1153
1154 static void
handle_transfer_done(GskUrlTransfer * transfer,gpointer data)1155 handle_transfer_done (GskUrlTransfer *transfer,
1156 gpointer data)
1157 {
1158 GskUrlTransferStream *transfer_stream = GSK_URL_TRANSFER_STREAM (data);
1159 g_assert (transfer_stream->transfer == transfer);
1160 transfer_stream->transfer = NULL;
1161
1162 if (transfer->error != NULL)
1163 gsk_io_set_gerror (GSK_IO (transfer_stream), GSK_IO_ERROR_CONNECT,
1164 g_error_copy (transfer->error));
1165 if (transfer->content != NULL)
1166 {
1167 transfer_stream->substream = g_object_ref (transfer->content);
1168 if (gsk_io_is_polling_for_read (transfer_stream))
1169 gsk_io_trap_readable (transfer_stream->substream,
1170 handle_substream_is_readable,
1171 handle_substream_read_shutdown,
1172 g_object_ref (transfer_stream),
1173 g_object_unref);
1174 }
1175 else
1176 {
1177 gsk_io_notify_read_shutdown (GSK_IO (transfer_stream));
1178 }
1179 }
1180
1181 /**
1182 * gsk_url_transfer_stream_new:
1183 * @transfer: the transfer. must not be started.
1184 * @error: optional location to store the #GError if there is a problem.
1185 *
1186 * This code will start the transfer,
1187 * and return a stream that you can trap immediately.
1188 *
1189 * returns: the new stream, or NULL if an error occurred.
1190 */
1191 GskStream *
gsk_url_transfer_stream_new(GskUrlTransfer * transfer,GError ** error)1192 gsk_url_transfer_stream_new (GskUrlTransfer *transfer,
1193 GError **error)
1194 {
1195 GskUrlTransferStream *transfer_stream = g_object_new (GSK_TYPE_URL_TRANSFER_STREAM, NULL);
1196 transfer_stream->transfer = transfer;
1197 gsk_url_transfer_set_handler (transfer,
1198 handle_transfer_done,
1199 g_object_ref (transfer_stream),
1200 g_object_unref);
1201 if (!gsk_url_transfer_start (transfer, error))
1202 {
1203 transfer_stream->transfer = NULL;
1204 g_object_unref (transfer_stream);
1205 return NULL;
1206 }
1207 return GSK_STREAM (transfer_stream);
1208 }
1209