1 /* TODO: HTTP proxy support */
2
3 #include "gskurltransferhttp.h"
4 #include "../gsknameresolver.h"
5 #include "../gskstreamclient.h"
6 #include "../ssl/gskstreamssl.h"
7 #include "../http/gskhttpclient.h"
8 #include <string.h>
9
10 G_DEFINE_TYPE(GskUrlTransferHttp, gsk_url_transfer_http, GSK_TYPE_URL_TRANSFER);
11
12 /* used to restart a transfer on a new URL (a redirect) */
13 static void start_name_resolution (GskUrlTransferHttp *http);
14
15 struct _GskUrlTransferHttpModifierNode
16 {
17 GskUrlTransferHttpRequestModifierFunc modifier;
18 gpointer data;
19 GDestroyNotify destroy;
20 GskUrlTransferHttpModifierNode *next;
21 };
22
23 static void
handle_http_response(GskHttpRequest * request,GskHttpResponse * response,GskStream * input,gpointer hook_data)24 handle_http_response (GskHttpRequest *request,
25 GskHttpResponse *response,
26 GskStream *input,
27 gpointer hook_data)
28 {
29 GskUrlTransfer *transfer = GSK_URL_TRANSFER (hook_data);
30 GskUrlTransferHttp *http = GSK_URL_TRANSFER_HTTP (hook_data);
31
32 ++(http->response_count);
33 if (gsk_url_transfer_is_done (transfer))
34 return;
35
36 /* For exposition we retain copy
37 the entire list of GskHttpStatusCodes.
38
39 However, the error case is the most common,
40 which we handle in the 'default' case.
41
42 Therefore, most of the error status-codes
43 are commented out: they will use the default case. */
44 switch (response->status_code)
45 {
46 /* errors i don't expect should ever happen, given the request
47 * we've issued (handled with the default case) */
48 /*case GSK_HTTP_STATUS_CONTINUE:*/
49 /*case GSK_HTTP_STATUS_SWITCHING_PROTOCOLS:*/
50 /*case GSK_HTTP_STATUS_NOT_MODIFIED:*/
51 /*case GSK_HTTP_STATUS_BAD_REQUEST:*/
52 /*case GSK_HTTP_STATUS_CREATED:*/
53 /*case GSK_HTTP_STATUS_ACCEPTED:*/
54 /*case GSK_HTTP_STATUS_NONAUTHORITATIVE_INFO:*/
55 /*case GSK_HTTP_STATUS_RESET_CONTENT:*/
56 /*case GSK_HTTP_STATUS_PARTIAL_CONTENT:*/
57 /*case GSK_HTTP_STATUS_CONFLICT:*/
58 /*case GSK_HTTP_STATUS_BAD_RANGE:*/
59 /*case GSK_HTTP_STATUS_BAD_GATEWAY:*/
60 /*case GSK_HTTP_STATUS_GATEWAY_TIMEOUT:*/
61
62 /* XXX: errors that i'm not sure about
63 (currently handled with the default case) */
64 /*case GSK_HTTP_STATUS_MULTIPLE_CHOICES:*/
65 /*case GSK_HTTP_STATUS_USE_PROXY:*/
66 /*case GSK_HTTP_STATUS_PROXY_AUTH_REQUIRED:*/
67
68 case GSK_HTTP_STATUS_NO_CONTENT:
69 case GSK_HTTP_STATUS_OK:
70 gsk_url_transfer_set_response (transfer, G_OBJECT (response));
71 if (input)
72 gsk_url_transfer_set_download (transfer, input);
73 gsk_url_transfer_notify_done (transfer, GSK_URL_TRANSFER_SUCCESS);
74 break;
75
76 /* errors that really mean the download failed,
77 * in a protologically valid way.
78 * (some of these indicate a seriously broken server)
79 * (handled with the default case)
80 */
81 /*case GSK_HTTP_STATUS_UNAUTHORIZED:*/
82 /*case GSK_HTTP_STATUS_PAYMENT_REQUIRED:*/
83 /*case GSK_HTTP_STATUS_FORBIDDEN:*/
84 /*case GSK_HTTP_STATUS_NOT_FOUND:*/
85 /*case GSK_HTTP_STATUS_METHOD_NOT_ALLOWED:*/
86 /*case GSK_HTTP_STATUS_NOT_ACCEPTABLE:*/
87 /*case GSK_HTTP_STATUS_REQUEST_TIMEOUT:*/
88 /*case GSK_HTTP_STATUS_GONE:*/
89 /*case GSK_HTTP_STATUS_LENGTH_REQUIRED:*/
90 /*case GSK_HTTP_STATUS_PRECONDITION_FAILED:*/
91 /*case GSK_HTTP_STATUS_ENTITY_TOO_LARGE:*/
92 /*case GSK_HTTP_STATUS_URI_TOO_LARGE:*/
93 /*case GSK_HTTP_STATUS_EXPECTATION_FAILED:*/
94 /*case GSK_HTTP_STATUS_UNSUPPORTED_MEDIA:*/
95 /*case GSK_HTTP_STATUS_INTERNAL_SERVER_ERROR:*/
96 /*case GSK_HTTP_STATUS_NOT_IMPLEMENTED:*/
97 /*case GSK_HTTP_STATUS_SERVICE_UNAVAILABLE:*/
98 /* XXX: if we get this, we should try again at HTTP/1.0... whatever */
99 /*case GSK_HTTP_STATUS_UNSUPPORTED_VERSION:*/
100
101 /* redirections */
102 case GSK_HTTP_STATUS_MOVED_PERMANENTLY:
103 case GSK_HTTP_STATUS_FOUND:
104 case GSK_HTTP_STATUS_SEE_OTHER:
105 case GSK_HTTP_STATUS_TEMPORARY_REDIRECT:
106 {
107 GskUrl *new_url = NULL;
108 gboolean is_permanent = (response->status_code == GSK_HTTP_STATUS_MOVED_PERMANENTLY);
109 if (response->location != NULL)
110 {
111 GskUrl *old_url = transfer->url;
112 GError *error = NULL;
113 new_url = gsk_url_new_relative (old_url, response->location, &error);
114 if (new_url == NULL)
115 {
116 GskUrlTransferResult result = GSK_URL_TRANSFER_ERROR_UNSUPPORTED; /* XXX: not especially correct */
117 g_warning ("redirect to invalid Location: %s: %s",
118 response->location,
119 error ? error->message : "unknown error");
120 gsk_url_transfer_take_error (transfer, error);
121 gsk_url_transfer_notify_done (transfer, result);
122 if (input)
123 gsk_io_read_shutdown (input, NULL);
124 return;
125 }
126 if (new_url != NULL)
127 {
128 if (!gsk_url_transfer_add_redirect (transfer,
129 NULL,
130 G_OBJECT (response),
131 is_permanent,
132 new_url))
133 {
134 if (input)
135 gsk_io_read_shutdown (input, NULL);
136 g_object_unref (new_url);
137 return;
138 }
139
140 g_object_unref (new_url);
141
142 if (transfer->follow_redirects)
143 /* restart at name-lookup */
144 start_name_resolution (http);
145 else
146 gsk_url_transfer_notify_done (transfer, GSK_URL_TRANSFER_REDIRECT);
147 if (input)
148 gsk_io_read_shutdown (input, NULL);
149
150 return;
151 }
152 }
153 }
154
155 /* default case indicates an error occurred */
156 default:
157 {
158 GEnumClass *eclass = g_type_class_ref (GSK_TYPE_HTTP_STATUS);
159 GEnumValue *evalue = g_enum_get_value (eclass, response->status_code);
160 const char *error_code_name = evalue ? evalue->value_nick : "**unknown status**";
161 GskUrlTransferResult result;
162 gsk_url_transfer_take_error (transfer,
163 g_error_new (GSK_G_ERROR_DOMAIN,
164 GSK_ERROR_HTTP_NOT_FOUND,
165 "error downloading via http: error %d [%s]",
166 response->status_code, error_code_name));
167 g_type_class_unref (eclass);
168
169 if (response->status_code / 100 == 4) /* 400s are not found type errors */
170 result = GSK_URL_TRANSFER_ERROR_NOT_FOUND;
171 else if (response->status_code / 100 == 5) /* 400s are server errors */
172 result = GSK_URL_TRANSFER_ERROR_SERVER_ERROR;
173 else
174 result = GSK_URL_TRANSFER_ERROR_UNSUPPORTED; /* who knows? */
175 gsk_url_transfer_notify_done (transfer, result);
176 if (input)
177 gsk_io_read_shutdown (input, NULL);
178 break;
179 }
180 }
181 }
182
183 static void
http_client_request_destroyed(gpointer data)184 http_client_request_destroyed (gpointer data)
185 {
186 GskUrlTransfer *transfer = GSK_URL_TRANSFER (data);
187 GskUrlTransferHttp *http = GSK_URL_TRANSFER_HTTP (data);
188
189 /* http invariant */
190 g_assert (http->response_count <= http->request_count);
191
192 g_assert (http->undestroyed_requests > 0);
193 --(http->undestroyed_requests);
194
195 if (!transfer->timed_out
196 && !gsk_url_transfer_is_done (transfer)
197 && http->undestroyed_requests == 0
198 && http->response_count < http->request_count)
199 {
200 /* transfer has been aborted for mysterious reasons */
201 gsk_url_transfer_take_error (transfer,
202 g_error_new (GSK_G_ERROR_DOMAIN,
203 GSK_ERROR_BAD_FORMAT,
204 "unable to get HTTP response from server"));
205 gsk_url_transfer_notify_done (transfer, GSK_URL_TRANSFER_ERROR_SERVER_ERROR);
206 }
207 g_object_unref (transfer);
208 }
209
210 static void
handle_name_resolution_succeeded(GskSocketAddress * address,gpointer data)211 handle_name_resolution_succeeded (GskSocketAddress *address,
212 gpointer data)
213 {
214 GskUrlTransfer *transfer = GSK_URL_TRANSFER (data);
215 GskUrlTransferHttp *http = GSK_URL_TRANSFER_HTTP (data);
216 GError *error = NULL;
217 GskStream *transport;
218 GskHttpClient *http_client;
219 GskHttpRequest *http_request;
220 GskStream *upload_stream;
221 GskUrlTransferHttpModifierNode *modifier;
222 GskUrl *url = transfer->redirect_url ? transfer->redirect_url : transfer->url;
223
224 if (gsk_url_transfer_is_done (transfer))
225 return;
226
227 /* Create actual address (with correct port) */
228 {
229 GskSocketAddressIpv4 *found = GSK_SOCKET_ADDRESS_IPV4 (address);
230 GskSocketAddress *addr;
231 guint url_port = gsk_url_get_port (url);
232 if (http->is_proxy || found->ip_port == url_port)
233 addr = g_object_ref (address);
234 else
235 addr = gsk_socket_address_ipv4_new (found->ip_address, url_port);
236 gsk_url_transfer_set_address (transfer, addr);
237
238 /* Create a TCP connection to that address. */
239 if (http->raw_transport != NULL)
240 {
241 /* from a redirect */
242 g_object_unref (http->raw_transport);
243 }
244 http->raw_transport = gsk_stream_new_connecting (addr, &error);
245 if (http->raw_transport == NULL)
246 {
247 gsk_url_transfer_take_error (transfer, error);
248 gsk_url_transfer_notify_done (transfer, GSK_URL_TRANSFER_ERROR_NO_SERVER);
249 return;
250 }
251 g_object_unref (addr);
252 addr = NULL;
253 }
254
255 /* For SSL streams, create the ssl-transport */
256 if (url->scheme == GSK_URL_SCHEME_HTTPS)
257 {
258 transport = gsk_stream_ssl_new_client (http->ssl_cert,
259 http->ssl_key,
260 http->ssl_password,
261 http->raw_transport,
262 &error);
263 if (transport == NULL)
264 {
265 gsk_url_transfer_take_error (transfer, error);
266 gsk_url_transfer_notify_done (transfer, GSK_URL_TRANSFER_ERROR_BAD_REQUEST);
267 return;
268 }
269 }
270 else
271 {
272 /* otherwise, use the raw_transport directly. */
273 transport = g_object_ref (http->raw_transport);
274 }
275
276 /* setup path */
277 {
278 char *to_free = NULL;
279 const char *path;
280 GskHttpVerb verb;
281 if (http->is_proxy)
282 path = to_free = gsk_url_to_string (url);
283 else if (url->query)
284 path = to_free = g_strdup_printf ("%s?%s", url->path, url->query);
285 else
286 path = url->path;
287 verb = gsk_url_transfer_has_upload (transfer) ? GSK_HTTP_VERB_POST : GSK_HTTP_VERB_GET;
288
289 /* make basic request object */
290 http_request = gsk_http_request_new (verb, path);
291 g_free (to_free);
292 if (http->is_proxy)
293 {
294 /* TODO:
295 gsk_http_request_set_proxy_host?
296 */
297 }
298 else if (url->port == 0
299 || url->port == 80)
300 gsk_http_request_set_host (http_request, url->host);
301 else
302 {
303 guint hostlen = strlen (url->host);
304 guint hostport_len = hostlen + 20;
305 char *hostport = g_alloca (hostport_len);
306 g_snprintf (hostport, hostport_len,
307 "%s:%u",
308 url->host, url->port);
309 gsk_http_request_set_host (http_request, hostport);
310 }
311 }
312
313 /* run modifiers */
314 for (modifier = http->first_modifier; modifier != NULL; modifier = modifier->next)
315 modifier->modifier (http_request, modifier->data);
316
317 gsk_url_transfer_set_request (transfer, G_OBJECT (http_request));
318
319 if (gsk_url_transfer_has_upload (transfer))
320 {
321 gssize size;
322 upload_stream = gsk_url_transfer_create_upload (transfer, &size, &error);
323 if (upload_stream == NULL)
324 {
325 g_object_unref (transport);
326 g_object_unref (http_request);
327 gsk_url_transfer_take_error (transfer, error);
328 gsk_url_transfer_notify_done (transfer, GSK_URL_TRANSFER_ERROR_BAD_REQUEST);
329 return;
330 }
331 if (size >= 0)
332 gsk_http_header_set_content_length (GSK_HTTP_HEADER (http_request), size);
333 }
334 else
335 {
336 upload_stream = NULL;
337 }
338
339 http_client = gsk_http_client_new ();
340 ++(http->request_count);
341 ++(http->undestroyed_requests);
342 gsk_http_client_propagate_content_read_shutdown (http_client);
343 gsk_http_client_request (http_client, http_request, upload_stream,
344 handle_http_response,
345 g_object_ref (transfer),
346 http_client_request_destroyed);
347 gsk_http_client_shutdown_when_done (http_client);
348 if (!gsk_stream_attach_pair (transport, GSK_STREAM (http_client), &error))
349 {
350 g_warning ("gsk_stream_attach_pair: transport/http-client: %s", error->message);
351 g_clear_error (&error);
352 }
353 if (upload_stream)
354 g_object_unref (upload_stream);
355 g_object_unref (transport);
356 g_object_unref (http_request);
357 g_object_unref (http_client);
358 }
359
360 static void
handle_name_resolution_failed(GError * error,gpointer data)361 handle_name_resolution_failed (GError *error,
362 gpointer data)
363 {
364 GskUrlTransfer *transfer = GSK_URL_TRANSFER (data);
365 gsk_url_transfer_set_error (transfer, error);
366 if (!gsk_url_transfer_is_done (transfer))
367 gsk_url_transfer_notify_done (transfer, GSK_URL_TRANSFER_ERROR_BAD_NAME);
368 }
369
370 static void
set_name_lookup_NULL_and_unref(gpointer data)371 set_name_lookup_NULL_and_unref (gpointer data)
372 {
373 GskUrlTransferHttp *http = GSK_URL_TRANSFER_HTTP (data);
374 http->name_lookup = NULL;
375 g_object_unref (http);
376 }
377
378 static void
start_name_resolution(GskUrlTransferHttp * http)379 start_name_resolution (GskUrlTransferHttp *http)
380 {
381 GskUrlTransfer *transfer = GSK_URL_TRANSFER (http);
382 GskUrl *url = transfer->redirect_url ? transfer->redirect_url : transfer->url;
383 g_return_if_fail (GSK_IS_URL (url));
384 g_return_if_fail (url->host != NULL);
385 gsk_name_resolver_lookup (GSK_NAME_RESOLVER_FAMILY_IPV4,
386 url->host,
387 handle_name_resolution_succeeded,
388 handle_name_resolution_failed,
389 g_object_ref (transfer),
390 set_name_lookup_NULL_and_unref);
391 }
392
393 static gboolean
gsk_url_transfer_http_start(GskUrlTransfer * transfer,GError ** error)394 gsk_url_transfer_http_start (GskUrlTransfer *transfer,
395 GError **error)
396 {
397 GskUrlTransferHttp *http = GSK_URL_TRANSFER_HTTP (transfer);
398 GskUrl *url = transfer->url;
399 if (url->host == NULL)
400 {
401 g_set_error (error,
402 GSK_G_ERROR_DOMAIN,
403 GSK_ERROR_BAD_FORMAT,
404 "HTTP urls must have hosts");
405 return FALSE;
406 }
407
408 if (transfer->address_hint)
409 handle_name_resolution_succeeded (transfer->address_hint, transfer);
410 else
411 start_name_resolution (http);
412 return TRUE;
413 }
414
415 static void
cancel_internal(GskUrlTransferHttp * http)416 cancel_internal (GskUrlTransferHttp *http)
417 {
418 if (http->name_lookup)
419 {
420 /* we were doing name resolution */
421 gsk_name_resolver_task_cancel (http->name_lookup);
422 }
423 else if (http->raw_transport)
424 {
425 /* who knows where we are... just cancel now. */
426 GError *error = NULL;
427 gsk_io_shutdown (GSK_IO (http->raw_transport), &error);
428 if (error)
429 {
430 g_message ("GskUrlTransferHttp: error shutting down for cancellation: %s", error->message);
431 g_error_free (error);
432 }
433 }
434 }
435
436 static void
gsk_url_transfer_http_cancel(GskUrlTransfer * transfer)437 gsk_url_transfer_http_cancel (GskUrlTransfer *transfer)
438 {
439 g_object_ref (transfer);
440 cancel_internal (GSK_URL_TRANSFER_HTTP (transfer));
441 if (!gsk_url_transfer_is_done (transfer))
442 gsk_url_transfer_notify_done (transfer, GSK_URL_TRANSFER_CANCELLED);
443 g_object_unref (transfer);
444 }
445
446 static void
gsk_url_transfer_http_timed_out(GskUrlTransfer * transfer)447 gsk_url_transfer_http_timed_out (GskUrlTransfer *transfer)
448 {
449 cancel_internal (GSK_URL_TRANSFER_HTTP (transfer));
450 GSK_URL_TRANSFER_CLASS (gsk_url_transfer_http_parent_class)->timed_out (transfer);
451 }
452
453 static char *
gsk_url_transfer_http_get_running_state(GskUrlTransfer * transfer)454 gsk_url_transfer_http_get_running_state (GskUrlTransfer *transfer)
455 {
456 GString *str = g_string_new ("RUNNING: ");
457 GskUrlTransferHttp *http = GSK_URL_TRANSFER_HTTP (transfer);
458 if (transfer->url)
459 {
460 char *url_str = gsk_url_to_string (transfer->url);
461 g_string_append (str, url_str);
462 g_free (url_str);
463 }
464 else
465 g_string_append (str, "(no url!?!)");
466
467 if (http->name_lookup)
468 g_string_append (str, ": doing name lookup");
469 else if (http->raw_transport == NULL)
470 g_string_append (str, ": no raw transport");
471 else if (gsk_io_get_is_connecting (http->raw_transport))
472 g_string_append (str, ": connecting");
473 return g_string_free (str, FALSE);
474 }
475
476 static void
gsk_url_transfer_http_finalize(GObject * object)477 gsk_url_transfer_http_finalize (GObject *object)
478 {
479 GskUrlTransferHttp *http = GSK_URL_TRANSFER_HTTP (object);
480 GskUrlTransferHttpModifierNode *mod;
481
482 g_free (http->ssl_cert);
483 g_free (http->ssl_key);
484 g_free (http->ssl_password);
485
486 g_assert (http->name_lookup == NULL);
487 if (http->raw_transport)
488 g_object_unref (http->raw_transport);
489
490 for (mod = http->first_modifier; mod; )
491 {
492 GskUrlTransferHttpModifierNode *next = mod->next;
493 if (mod->destroy)
494 mod->destroy (mod->data);
495 g_free (mod);
496 mod = next;
497 }
498
499 G_OBJECT_CLASS (gsk_url_transfer_http_parent_class)->finalize (object);
500 }
501
502 static void
gsk_url_transfer_http_init(GskUrlTransferHttp * url_transfer_http)503 gsk_url_transfer_http_init (GskUrlTransferHttp *url_transfer_http)
504 {
505 }
506
507 static void
gsk_url_transfer_http_class_init(GskUrlTransferHttpClass * class)508 gsk_url_transfer_http_class_init (GskUrlTransferHttpClass *class)
509 {
510 GObjectClass *object_class = G_OBJECT_CLASS (class);
511 GskUrlTransferClass *transfer_class = GSK_URL_TRANSFER_CLASS (class);
512 object_class->finalize = gsk_url_transfer_http_finalize;
513 transfer_class->start = gsk_url_transfer_http_start;
514 transfer_class->cancel = gsk_url_transfer_http_cancel;
515 transfer_class->get_running_state = gsk_url_transfer_http_get_running_state;
516 transfer_class->timed_out = gsk_url_transfer_http_timed_out;
517 }
518
519 /**
520 * gsk_url_transfer_http_set_ssl_cert:
521 * @http: the transfer to affect.
522 * @cert_fname: the certificate filename.
523 *
524 * Set the SSL certificate file for this connection.
525 */
526 void
gsk_url_transfer_http_set_ssl_cert(GskUrlTransferHttp * http,const char * cert_fname)527 gsk_url_transfer_http_set_ssl_cert (GskUrlTransferHttp *http,
528 const char *cert_fname)
529 {
530 char *str = g_strdup (cert_fname);
531 g_free (http->ssl_cert);
532 http->ssl_cert = str;
533 }
534
535 void
gsk_url_transfer_http_set_ssl_key(GskUrlTransferHttp * http,const char * key_fname)536 gsk_url_transfer_http_set_ssl_key (GskUrlTransferHttp *http,
537 const char *key_fname)
538 {
539 char *str = g_strdup (key_fname);
540 g_free (http->ssl_key);
541 http->ssl_key = str;
542 }
543
544 void
gsk_url_transfer_http_set_ssl_password(GskUrlTransferHttp * http,const char * password)545 gsk_url_transfer_http_set_ssl_password(GskUrlTransferHttp *http,
546 const char *password)
547 {
548 char *str = g_strdup (password);
549 g_free (http->ssl_password);
550 http->ssl_password = str;
551 }
552
553 static void
transfer_modifier_set_user_agent(GskHttpRequest * request,gpointer mod_data)554 transfer_modifier_set_user_agent (GskHttpRequest *request,
555 gpointer mod_data)
556 {
557 gsk_http_request_set_user_agent (request, (const char*)mod_data);
558 }
559
560 /**
561 * gsk_url_transfer_http_set_user_agent:
562 * @http: the transfer to affect.
563 * @user_agent: the User-Agent: header's value for this transfer.
564 *
565 * Set the User-Agent to use for this HTTP transaction.
566 */
567 void
gsk_url_transfer_http_set_user_agent(GskUrlTransferHttp * http,const char * user_agent)568 gsk_url_transfer_http_set_user_agent (GskUrlTransferHttp *http,
569 const char *user_agent)
570 {
571 gsk_url_transfer_http_add_modifier (http,
572 transfer_modifier_set_user_agent,
573 g_strdup (user_agent),
574 g_free);
575 }
576
577 /**
578 * gsk_url_transfer_http_set_proxy_address:
579 * @http: the transfer to affect.
580 * @proxy_address: the socket-address to
581 * really connect to.
582 *
583 * Set an HTTP proxy for this transfer.
584 */
585 void
gsk_url_transfer_http_set_proxy_address(GskUrlTransferHttp * http,GskSocketAddress * proxy_address)586 gsk_url_transfer_http_set_proxy_address (GskUrlTransferHttp *http,
587 GskSocketAddress *proxy_address)
588 {
589 gsk_url_transfer_set_address_hint (GSK_URL_TRANSFER (http), proxy_address);
590 http->is_proxy = TRUE;
591 }
592
593 static void
transfer_modifier_set_misc_header(GskHttpRequest * request,gpointer mod_data)594 transfer_modifier_set_misc_header (GskHttpRequest *request,
595 gpointer mod_data)
596 {
597 char *key = mod_data;
598 char *value = strchr (key, 0) + 1;
599 gsk_http_header_add_misc (GSK_HTTP_HEADER (request), key, value);
600 }
601
602 /**
603 * gsk_url_transfer_http_add_extra_header:
604 * @http: the transfer to affect.
605 * @key: a HTTP header name
606 * @value: the value of that HTTP header.
607 *
608 * Add an arbitrary header to the HTTP request.
609 */
610 void
gsk_url_transfer_http_add_extra_header(GskUrlTransferHttp * http,const char * key,const char * value)611 gsk_url_transfer_http_add_extra_header(GskUrlTransferHttp *http,
612 const char *key,
613 const char *value)
614 {
615 guint key_len, value_len;
616 char *kv;
617 g_return_if_fail (key != NULL && value != NULL);
618 key_len = strlen (key);
619 value_len = strlen (value);
620 kv = g_malloc (key_len + 1 + value_len + 1);
621 strcpy (kv, key);
622 strcpy (kv + key_len + 1, value);
623 gsk_url_transfer_http_add_modifier (http,
624 transfer_modifier_set_misc_header,
625 kv,
626 g_free);
627 }
628
629 /**
630 * gsk_url_transfer_http_add_modifier:
631 * @http: the transfer to affect.
632 * @modifier: function to call to modify the HTTP request header.
633 * @data: data to pass to modifier.
634 * @destroy: called with data when the modifier is destroyed.
635 *
636 * Add a generic transformation to do to the HTTP request header.
637 */
638 void
gsk_url_transfer_http_add_modifier(GskUrlTransferHttp * http,GskUrlTransferHttpRequestModifierFunc modifier,gpointer data,GDestroyNotify destroy)639 gsk_url_transfer_http_add_modifier (GskUrlTransferHttp *http,
640 GskUrlTransferHttpRequestModifierFunc modifier,
641 gpointer data,
642 GDestroyNotify destroy)
643 {
644 GskUrlTransferHttpModifierNode *node = g_new (GskUrlTransferHttpModifierNode, 1);
645 node->modifier = modifier;
646 node->data = data;
647 node->destroy = destroy;
648 node->next = NULL;
649 if (http->first_modifier == NULL)
650 http->first_modifier = node;
651 else
652 http->last_modifier->next = node;
653 http->last_modifier = node;
654 }
655